/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

#ifndef AACE_ALEXA_ALEXA_CONFIGURATION_H
#define AACE_ALEXA_ALEXA_CONFIGURATION_H

#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <memory>
#include <chrono>

#include "AACE/Core/EngineConfiguration.h"
#include "AlexaEngineInterfaces.h"

/** @file */

namespace aace {
namespace alexa {
namespace config {

/**
 * A factory interface for creating Alexa configuration objects
 */
class AlexaConfiguration {
public:
    /**
     * Factory function used to programmatically generate alexa client device info
     * configuration data. The data generated by this function is equivalent to
     * providing the following JSON values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "alexaClientInfo": {
     *       "clientId": "<CLIENT_ID>",
     *       "productId": "<PRODUCT_ID>",
     *       "amazonId": "<AMAZON_ID>"
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] clientId The client ID of the product in the AVS developer
     *             console
     * @param [in] productId The product ID of the product in the AVS developer
     *             console
     * @param [in] amazonId The Amazon ID of the product in the AVS developer
     *             portal
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createAlexaClientInfoConfig(
        const std::string& clientId,
        const std::string& productId,
        const std::string& amazonId);

    /**
     * Factory method used to programmatically generate notifications configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "avsDeviceSDK": {
     *       "notifications": {
     *         "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>"
     *       }
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent notifications data.
     * The database will be created on initialization if it does not already exist.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createNotificationsConfig(
        const std::string& databaseFilePath);

    /**
     * Factory method used to programmatically generate certified sender configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "avsDeviceSDK": {
     *       "certifiedSender": {
     *         "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>"
     *       }
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent certified sender data.
     * The database will be created on initialization if it does not already exist.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createCertifiedSenderConfig(
        const std::string& databaseFilePath);

    /**
     * Factory method used to programmatically generate capabilities delegate configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "avsDeviceSDK": {
     *       "capabilitiesDelegate": {
     *         "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>"
     *       }
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store device capabilities.
     * The database will be created on initialization if it does not already exist.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createCapabilitiesDelegateConfig(
        const std::string& databaseFilePath);

    /**
     * Factory method used to programmatically generate CURL configuration data.
     *
     * The 'libCurlUtils' sub-component of the global configuration supports the following options:
     * - CURLOPT_CAPATH If present, specifies a value for the libcurl property CURLOPT_CAPATH.
     * - CURLOPT_INTERFACE if present, specifies a value for the libcurl property CURLOPT_INTERFACE.
     *
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "avsDeviceSDK": {
     *       "libcurlUtils": {
     *         "CURLOPT_CAPATH": "<CA_CERTIFICATES_FILE_PATH>",
     *         "CURLOPT_INTERFACE": "<NETWORK_INTERFACE_NAME>",
     *         "CURLOPT_PROXY": "<PROXY_ADDRESS>"
     *       }
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] certsPath The file path to the directory holding CA certificates
     * @param [in] iface The specific network interface to use. This can be a network interface name, an IP address or a
     * host name. Default to the system's primary network interface.
     * @param [in] proxy The proxy used for outgoing requests.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createCurlConfig(
        const std::string& certsPath,
        const std::string& iface = "",
        const std::string& proxy = "");

    /**
     * @deprecated
     * Use @c AlexaConfiguration::createDeviceSettingsConfig().
     *
     * Factory method used to programmatically generate settings configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "avsDeviceSDK": {
     *       "deviceSettings": {
     *         "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>",
     *         "defaultAVSClientSettings": {
     *           "locale": "<LOCALE>"
     *         }
     *       }
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent settings data.
     * The database will be created on initialization if it does not already exist.
     * @param [in] locale The current locale setting on the client. Default to "en-US".
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createSettingsConfig(
        const std::string& databaseFilePath,
        const std::string& locale = "en-US");

    /**
     * Factory method used to programmatically generate device settings configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "avsDeviceSDK": {
     *       "deviceSettings": {
     *         "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>",
     *         "locales": [<LIST_OF_LOCALE_STRINGS>],
     *         "defaultLocale": "<DEFAULT_LOCALE_STRING>",
     *         "localeCombinations": [[<LOCALE_STRING_PAIR>]],
     *         "defaultTimezone": "<TIMEZONE>"
     *       }
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent settings data.
     *             The database will be created on initialization if it does not already exist.
     * @param [in] locales A list of locales supported by the device. The default is ["en-US","en-GB","de-DE",
     *             "en-IN","en-CA","ja-JP","en-AU","fr-FR","it-IT","es-ES","es-MX","fr-CA","es-US","hi-IN","pt-BR","ar-SA"].
     *              For the accepted values, refer to https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/system.html#locales
     * @param [in] defaultLocale The default locale setting on the device. The default is "en-US".
     * @param [in] defaultTimezone The default timezone setting on the device. The default is "America/Vancouver".
     *             For accepted values, refer to the accepted timezones here:
     *             https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/system.html#timezonechanged
     * @param [in] localeCombinations A list of locale combinations supported by the device for dual-locale mode.
     *             The permitted combinations are [["en-CA","fr-CA"],["fr-CA","en-CA"],["en-US","es-US"],
     *             ["es-US","en-US"],["en-IN","hi-IN"],["hi-IN","en-IN"],["en-US","fr-FR"],["fr-FR","en-US"],
     *             ["en-US","de-DE"],["de-DE","en-US"],["en-US","ja-JP"],["ja-JP","en-US"],["en-US","it-IT"],
     *             ["it-IT","en-US"],["en-US","es-ES"],["es-ES","en-US"]]. Any locale specified in this list must also
     *             be specified in the @a locales list.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createDeviceSettingsConfig(
        const std::string& databaseFilePath,
        const std::vector<std::string>& locales =
            {"en-US",
             "en-GB",
             "de-DE",
             "en-IN",
             "en-CA",
             "ja-JP",
             "en-AU",
             "fr-FR",
             "it-IT",
             "es-ES",
             "es-MX",
             "fr-CA",
             "es-US",
             "hi-IN",
             "pt-BR",
             "ar-SA"},
        const std::string& defaultLocale = "en-US",
        const std::string& defaultTimezone = "America/Vancouver",
        const std::vector<std::vector<std::string>>& localeCombinations = {});

    /**
     * Factory method used to programmatically generate misc storage configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "avsDeviceSDK": {
     *       "miscDatabase": {
     *         "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>"
     *       }
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent misc storage data.
     * The database will be created on initialization if it does not already exist.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createMiscStorageConfig(
        const std::string& databaseFilePath);

    /**
     * Factory method used to programmatically generate speaker manager configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "speakerManager": {
     *       "enabled": [true|false]
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] enabled Enable or disable the speaker manager (default is enabled)
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createSpeakerManagerConfig(bool enabled);

    /**
     * Factory method used to programmatically generate system configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "system": {
     *       "firmwareVersion": "<FIRMWARE_VERSION>"
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] firmwareVersion The firmware version of the client device
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createSystemConfig(uint32_t firmwareVersion);

    /**
     * Factory method used to programmatically generate encoder configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "speechRecognizer": {
     *       "encoder": {
     *         "name": "<ENCODER_NAME>"
     *       }
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] encoderName The encoder codec name to be used
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createSpeechRecognizerConfig(
        const std::string& encoderName);

    /**
     * enum specifying the configurable TemplateRuntime timeout.
     */
    enum class TemplateRuntimeTimeoutType {
        /**
         *  Display Card timeout in ms when Alexa completes TTS
         */
        DISPLAY_CARD_TTS_FINISHED_TIMEOUT,

        /**
         *  Display Card timeout in ms when AudioPlayback Completes.
         */
        DISPLAY_CARD_AUDIO_PLAYBACK_FINISHED_TIMEOUT,

        /**
         *  Display Card timeout in ms when AudioPlayback Stopped or Paused.
         */
        DISPLAY_CARD_AUDIO_PLAYBACK_STOPPED_PAUSED_TIMEOUT,

    };

    /**
     * Identifies a Template Runtime configuration with a type and value pair
     */
    using TemplateRuntimeTimeout = std::pair<TemplateRuntimeTimeoutType, std::chrono::milliseconds>;

    /**
     * Factory method used to programmatically generate template runtime configuration data.
     * This is an optional configuration. Following are the accepted keys and their description.
     * - displayCardTTSFinishedTimeout If present, specifies the values in milli seconds to control the timeout of
     * display card at the end of Alexa TTS.
     * - displayCardAudioPlaybackFinishedTimeout If present, specifies the values in milli seconds to control the
     * timeout of display card at the FINISHED state of AudioPlayback.
     * - displayCardAudioPlaybackStoppedPausedTimeout If present, specifies the values in milli seconds to control the
     * timeout of display card at STOP or PAUSE state of AudioPlayback. The data generated by this method is equivalent
     * to providing the following JSON values in a configuration:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "templateRuntimeCapabilityAgent": {
     *       "displayCardTTSFinishedTimeout": <TIMEOUT_IN_MS>,
     *       "displayCardAudioPlaybackFinishedTimeout": <TIMEOUT_IN_MS>,
     *       "displayCardAudioPlaybackStoppedPausedTimeout": <TIMEOUT_IN_MS>
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] timeoutList A list of @c TemplateRuntimeTimeout type and value pairs
     *
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createTemplateRuntimeTimeoutConfig(
        const std::vector<TemplateRuntimeTimeout>& timeoutList);

    /**
     * Factory method used to programmatically generate external media player configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "externalMediaPlayer": {
     *       "agent": "<agent>"
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] agent The external media player agent
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createExternalMediaPlayerConfig(
        const std::string& agent);

    /**
     * Describes the equalizer bands supported by Alexa. The platform implementation may support a subset of these.
     *
     * @sa aace::alexa::EqualizerControllerEngineInterface::EqualizerBand
     */
    using EqualizerBand = aace::alexa::EqualizerControllerEngineInterface::EqualizerBand;

    /**
     * Describes the level of gain of a particular equalizer band as an integer dB value. This is an
     * @c aace::alexa::EqualizerController::EqualizerBand and @c int pair.
     *
     * @sa aace::alexa::EqualizerControllerEngineInterface::EqualizerBandLevel
     */
    using EqualizerBandLevel = aace::alexa::EqualizerControllerEngineInterface::EqualizerBandLevel;

    /**
     * Factory method used to programmatically generate equalizer controller configuration data. This is an optional
     * configuration, and default settings will be used if configuration is not provided. This method produces
     * configuration data according to the JSON structure in the sample below.
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "equalizer": {
     *       "bands": {
     *         "BASS": true,
     *         "MIDRANGE": false,
     *         "TREBLE": true
     *       },
     *     "defaultState": {
     *       "bands": {
     *         "BASS": 4,
     *         "TREBLE": -1
     *       }
     *     },
     *     "minLevel": -6,
     *     "maxLevel": 6
     *   }
     * }
     * @endcode
     *
     * The configuration branches are used as follows:
     *
     * @li equalizer.bands: Specifies which bands are supported by the device and will be enabled for control with
     *  Alexa. Each child key is the name of an Alexa-supported band ("BASS", "MIDRANGE", or "TREBLE") and value is
     *  whether the device supports the band. Only bands explicitly declared supported will be enabled in the SDK and
     *  Alexa. Omitting this branch enables all bands by default.
     *
     * @li equalizer.defaultState: Describes the default or reset state of the equalizer. These settings are used to
     *  reset the equalizer with Alexa such as by saying "Alexa, reset bass." If this branch or its child is omitted,
     *  default values will be used.
     * @li equalizer.defaultState.bands: Defines the default gain level setting in dB for each supported equalizer
     *  band. Each element key is the name of a supported band and value is a level (int) specifying the default gain
     *  in dB. All of the supported bands must be provided once this branch is defined. All dB levels must obey the
     *  limits declared in "equalizer.minLevel" and "equalizer.maxLevel". Omitting this branch uses the default 0dB for
     *  each band.
     *
     * @li equalizer.minLevel and equalizer.maxLevel: Integer values specifying the decibel level range on which Alexa
     *  may operate for the supported bands. The device may support a different range internally, but Alexa will know
     *  only about the limits declared here. Values should be specified as absolute amplitude gain in integer dB and
     *  scaled to the platform's internal range as necessary. If these values are omitted, the default range min -6dB
     *  and max +6dB will be used.
     *
     * @param supportedBands A list of supported equalizer bands. Corresponds to the "equalizer.bands" config branch.
     *        Only bands provided in the list will be enabled. Unspecified or empty @a supportedBands omits the config
     *        branch. Nonempty @a supportedBands includes the branch and declares each band in the list with a value
     *        "true".
     * @param minLevel The minimum gain level for the equalizer bands in integer dB. Corresponds to
     *        "equalizer.minLevel". Unspecified @a minLevel uses the -6dB default.
     * @param maxLevel The maximum gain level for the equalizer bands in integer dB. Corresponds to
     *        "equalizer.maxLevel". Unspecified @a maxLevel uses the +6dB default.
     * @param defaultBandLevels The default or reset state of the equalizer bands. Corresponds to the
     *        "equalizer.defaultState.bands" config branch. Unspecified or empty @a defaultBandLevels omits the config
     *        branch.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createEqualizerControllerConfig(
        const std::vector<EqualizerBand>& supportedBands = {},
        int minLevel = -6,
        int maxLevel = 6,
        const std::vector<EqualizerBandLevel>& defaultBandLevels = {});

    /**
     * Factory method used to programmatically generate auth provider configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "authProvider": {
     *       "providers": [<LIST_OF_PROVIDER_NAMES_STRINGS>]
     *     }
     *   }
     * }
     * @endcode
     *
     * @param providerNames The list of auth providers
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createAuthProviderConfig(
        const std::vector<std::string>& providerNames);

    /**
     * Factory method used to programmatically generate music ducking configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "audio": {
     *       "audioOutputType.music": {
     *         "ducking": {
     *           "enabled": [true|false]
     *         }
     *       }
     *     }
     *   }
     * }
     * @endcode
     *
     * @param duckingEnabled @c true if ducking is enabled
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createDuckingConfig(bool duckingEnabled);

    /**
     * Factory method used to programmatically generate media player fingerprint configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *     "mediaPlayerFingerprint": {
     *       "package": "<package>",
     *       "buildType": "<buildType>",
     *       "versionNumber": "<versionNumber>"
     *     }
     *   }
     * }
     * @endcode
     *
     * @see https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/audioplayer.html#capability-assertion
     *
     * @param package Unique identifier for the audio player software loaded on the device.
     * @param buildType The audio player build type.
     * @param versionNumber Identifies the version of the audio player loaded on your device.
     * @return std::shared_ptr<aace::core::config::EngineConfiguration>
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createMediaPlayerFingerprintConfig(
        const std::string& package,
        const std::string& buildType,
        const std::string& versionNumber);
};

}  // namespace config
}  // namespace alexa
}  // namespace aace

#endif  // AACE_ALEXA_ALEXA_CONFIGURATION_H
