/*
 * 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_ENGINE_INTERFACES_H
#define AACE_ALEXA_ALEXA_ENGINE_INTERFACES_H

#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <limits>

/** @file */

namespace aace {
namespace alexa {

/**
 * AlexaSpeakerEngineInterface
 */
class AlexaSpeakerEngineInterface {
public:
    virtual ~AlexaSpeakerEngineInterface() = default;

    /**
     * Specifies the type of the AlexaSpeaker to control
     */
    enum class SpeakerType {

        /**
         * The Speaker type that is controlled by AVS
         */
        ALEXA_VOLUME,

        /**
         * The Speaker type that is controlled locally by the platform
         */
        ALERTS_VOLUME
    };

    virtual void onLocalSetVolume(SpeakerType type, int8_t volume) = 0;
    virtual void onLocalAdjustVolume(SpeakerType type, int8_t delta) = 0;
    virtual void onLocalSetMute(SpeakerType type, bool mute) = 0;
};

inline std::ostream& operator<<(std::ostream& stream, const AlexaSpeakerEngineInterface::SpeakerType& type) {
    switch (type) {
        case AlexaSpeakerEngineInterface::SpeakerType::ALEXA_VOLUME:
            stream << "ALEXA_VOLUME";
            break;
        case AlexaSpeakerEngineInterface::SpeakerType::ALERTS_VOLUME:
            stream << "ALERTS_VOLUME";
            break;
    }
    return stream;
}

/**
 * SpeechRecognizerEngineInterface
 */
class SpeechRecognizerEngineInterface {
public:
    virtual ~SpeechRecognizerEngineInterface() = default;

    /**
     * Describes type of event that initiated the speech request.
     */
    enum class Initiator {

        /**
         * Hold-to-talk speech initiator type.
         */
        HOLD_TO_TALK,
        /**
         * Tap-to-talk speech initiator type.
         */
        TAP_TO_TALK,
        /**
         * Wakeword speech initiator type.
         */
        WAKEWORD
    };

    /*
     * Defines an unspecified value for the speech recognizer's audio index.
     */
    static constexpr uint64_t UNSPECIFIED_INDEX = std::numeric_limits<uint64_t>::max();

    virtual bool onStartCapture(
        Initiator initiator,
        uint64_t keywordBegin,
        uint64_t keywordEnd,
        const std::string& keyword) = 0;
    virtual bool onStopCapture() = 0;
};

inline std::ostream& operator<<(std::ostream& stream, const SpeechRecognizerEngineInterface::Initiator& initiator) {
    switch (initiator) {
        case SpeechRecognizerEngineInterface::Initiator::HOLD_TO_TALK:
            stream << "HOLD_TO_TALK";
            break;
        case SpeechRecognizerEngineInterface::Initiator::TAP_TO_TALK:
            stream << "TAP_TO_TALK";
            break;
        case SpeechRecognizerEngineInterface::Initiator::WAKEWORD:
            stream << "WAKEWORD";
            break;
    }
    return stream;
}

/**
 * PlaybackControllerEngineInterface
 */
class PlaybackControllerEngineInterface {
public:
    virtual ~PlaybackControllerEngineInterface() = default;

    /**
     * Describes the playback button type
     */
    enum class PlaybackButton {
        /**
         * 'Play' button.
         */
        PLAY,
        /**
         * 'Pause' button.
         */
        PAUSE,
        /**
         * 'Next' button.
         */
        NEXT,
        /**
         * 'Previous' button.
         */
        PREVIOUS,
        /**
         * 'Skip Forward' button.
         */
        SKIP_FORWARD,
        /**
         * 'Skip Backward' button.
         */
        SKIP_BACKWARD
    };

    /**
     * Describes the playback button type
     */
    enum class PlaybackToggle {
        /**
         * 'Shuffle' toggle.
         */
        SHUFFLE,
        /**
         * 'Loop' toggle.
         */
        LOOP,
        /**
         * 'Repeat' toggle.
         */
        REPEAT,
        /**
         * 'Thumbs Up' toggle.
         */
        THUMBS_UP,
        /**
         * 'Thumbs Down' toggle.
         */
        THUMBS_DOWN
    };

    virtual void onButtonPressed(PlaybackButton button) = 0;
    virtual void onTogglePressed(PlaybackToggle toggle, bool action) = 0;
};

inline std::ostream& operator<<(std::ostream& stream, const PlaybackControllerEngineInterface::PlaybackToggle& toggle) {
    switch (toggle) {
        case PlaybackControllerEngineInterface::PlaybackToggle::SHUFFLE:
            stream << "SHUFFLE";
            break;
        case PlaybackControllerEngineInterface::PlaybackToggle::LOOP:
            stream << "LOOP";
            break;
        case PlaybackControllerEngineInterface::PlaybackToggle::REPEAT:
            stream << "REPEAT";
            break;
        case PlaybackControllerEngineInterface::PlaybackToggle::THUMBS_UP:
            stream << "THUMBS_UP";
            break;
        case PlaybackControllerEngineInterface::PlaybackToggle::THUMBS_DOWN:
            stream << "THUMBS_DOWN";
            break;
    }
    return stream;
}

inline std::ostream& operator<<(std::ostream& stream, const PlaybackControllerEngineInterface::PlaybackButton& button) {
    switch (button) {
        case PlaybackControllerEngineInterface::PlaybackButton::PLAY:
            stream << "PLAY";
            break;
        case PlaybackControllerEngineInterface::PlaybackButton::PAUSE:
            stream << "PAUSE";
            break;
        case PlaybackControllerEngineInterface::PlaybackButton::NEXT:
            stream << "NEXT";
            break;
        case PlaybackControllerEngineInterface::PlaybackButton::PREVIOUS:
            stream << "PREVIOUS";
            break;
        case PlaybackControllerEngineInterface::PlaybackButton::SKIP_FORWARD:
            stream << "SKIP_FORWARD";
            break;
        case PlaybackControllerEngineInterface::PlaybackButton::SKIP_BACKWARD:
            stream << "SKIP_BACKWARD";
            break;
    }
    return stream;
}

/**
 * AuthProviderEngineInterface
 */
class AuthProviderEngineInterface {
public:
    virtual ~AuthProviderEngineInterface() = default;

    /**
     * Describes the state of client authorization with AVS
     */
    enum class AuthState {

        /**
         * Authorization has not yet been acquired.
         */
        UNINITIALIZED,

        /**
         * Authorization has been refreshed.
         */
        REFRESHED,

        /**
         * Authorization has expired.
         */
        EXPIRED,

        /**
         * Authorization has failed in a manner that cannot be corrected by retrying.
         */
        UNRECOVERABLE_ERROR
    };

    /**
     * Describes an error during an attempt to authorize with AVS
     */
    enum class AuthError {

        /**
         * No error encountered
         */
        NO_ERROR,

        /**
         * An error was encountered, but no error description can be determined.
         */
        UNKNOWN_ERROR,

        /**
         * The client authorization failed.
         */
        AUTHORIZATION_FAILED,

        /**
         * The client is not authorized to use authorization codes.
         */
        UNAUTHORIZED_CLIENT,

        /**
         * The server encountered a runtime error.
         */
        SERVER_ERROR,

        /**
         * The request is missing a required parameter, has an invalid value, or is otherwise malformed.
         */
        INVALID_REQUEST,

        /**
         * One of the values in the request was invalid.
         */
        INVALID_VALUE,

        /**
         * The authorization code is invalid, expired, revoked, or was issued to a different client.
         */
        AUTHORIZATION_EXPIRED,

        /**
         * The client specified the wrong token type.
         */
        UNSUPPORTED_GRANT_TYPE,

        /**
         * Invalid code pair provided in Code-based linking token request.
         */
        INVALID_CODE_PAIR,

        /**
         * Waiting for user to authorize the specified code pair.
         */
        AUTHORIZATION_PENDING,

        /**
         * Client should slow down in the rate of requests polling for an access token.
         */
        SLOW_DOWN,

        /**
         * Internal error in client code.
         */
        INTERNAL_ERROR,

        /**
         * Client ID not valid for use with code based linking.
         */
        INVALID_CBL_CLIENT_ID
    };

    virtual void onAuthStateChanged(AuthState state, AuthError error) = 0;
};

inline std::ostream& operator<<(std::ostream& stream, const AuthProviderEngineInterface::AuthState& state) {
    switch (state) {
        case AuthProviderEngineInterface::AuthState::UNINITIALIZED:
            stream << "UNINITIALIZED";
            break;
        case AuthProviderEngineInterface::AuthState::REFRESHED:
            stream << "REFRESHED";
            break;
        case AuthProviderEngineInterface::AuthState::EXPIRED:
            stream << "EXPIRED";
            break;
        case AuthProviderEngineInterface::AuthState::UNRECOVERABLE_ERROR:
            stream << "UNRECOVERABLE_ERROR";
            break;
    }
    return stream;
}

inline std::ostream& operator<<(std::ostream& stream, const AuthProviderEngineInterface::AuthError& error) {
    switch (error) {
        case AuthProviderEngineInterface::AuthError::NO_ERROR:
            stream << "NO_ERROR";
            break;
        case AuthProviderEngineInterface::AuthError::UNKNOWN_ERROR:
            stream << "UNKNOWN_ERROR";
            break;
        case AuthProviderEngineInterface::AuthError::AUTHORIZATION_FAILED:
            stream << "AUTHORIZATION_FAILED";
            break;
        case AuthProviderEngineInterface::AuthError::UNAUTHORIZED_CLIENT:
            stream << "UNAUTHORIZED_CLIENT";
            break;
        case AuthProviderEngineInterface::AuthError::SERVER_ERROR:
            stream << "SERVER_ERROR";
            break;
        case AuthProviderEngineInterface::AuthError::INVALID_REQUEST:
            stream << "INVALID_REQUEST";
            break;
        case AuthProviderEngineInterface::AuthError::INVALID_VALUE:
            stream << "INVALID_VALUE";
            break;
        case AuthProviderEngineInterface::AuthError::AUTHORIZATION_EXPIRED:
            stream << "AUTHORIZATION_EXPIRED";
            break;
        case AuthProviderEngineInterface::AuthError::UNSUPPORTED_GRANT_TYPE:
            stream << "UNSUPPORTED_GRANT_TYPE";
            break;
        case AuthProviderEngineInterface::AuthError::INVALID_CODE_PAIR:
            stream << "INVALID_CODE_PAIR";
            break;
        case AuthProviderEngineInterface::AuthError::AUTHORIZATION_PENDING:
            stream << "AUTHORIZATION_PENDING";
            break;
        case AuthProviderEngineInterface::AuthError::SLOW_DOWN:
            stream << "SLOW_DOWN";
            break;
        case AuthProviderEngineInterface::AuthError::INTERNAL_ERROR:
            stream << "INTERNAL_ERROR";
            break;
        case AuthProviderEngineInterface::AuthError::INVALID_CBL_CLIENT_ID:
            stream << "INVALID_CBL_CLIENT_ID";
            break;
    }
    return stream;
}

/**
 * ExternalMediaAdapterEngineInterface
 */
class ExternalMediaAdapterEngineInterface {
public:
    virtual ~ExternalMediaAdapterEngineInterface() = default;

    /**
     * Describes a discovered external media player app
     */
    class DiscoveredPlayerInfo {
    public:
        /// The opaque token that uniquely identifies the local external player app
        std::string localPlayerId;
        /// The only spiVersion that currently exists is "1.0"
        std::string spiVersion;
        /** Validation methods :
         *  1. "SIGNING_CERTIFICATE"
         *  2. "GENERATED_CERTIFICATE"
         *  3. "NONE"
         */
        std::string validationMethod;
        /** Validation data :
         *  1. Device platform issued app signing certificate. A list of certificates may be attached.
         *  2. In some cases validation is performed locally. The certificate is transmitted as validationData during discovery to announce the activated app's identity in order to allow app activation to be revoked.
         *  3. empty
         */
        std::vector<std::string> validationData;
    };

    virtual void onReportDiscoveredPlayers(const std::vector<DiscoveredPlayerInfo>& discoveredPlayers) = 0;
    virtual void onRequestToken(const std::string& localPlayerId) = 0;
    virtual void onLoginComplete(const std::string& localPlayerId) = 0;
    virtual void onLogoutComplete(const std::string& localPlayerId) = 0;
    virtual void onPlayerEvent(const std::string& localPlayerId, const std::string& eventName) = 0;
    virtual void onPlayerError(
        const std::string& localPlayerId,
        const std::string& errorName,
        long code,
        const std::string& description,
        bool fatal) = 0;
    virtual void onSetFocus(const std::string& playerId) = 0;
    virtual void onRemoveDiscoveredPlayer(const std::string& localPlayerId) = 0;
};

/**
 * LocalMediaSourceEngineInterface
 */
class LocalMediaSourceEngineInterface {
public:
    virtual ~LocalMediaSourceEngineInterface() = default;

    virtual void onPlayerEvent(const std::string& eventName, const std::string& sessionId) = 0;
    virtual void onPlayerError(
        const std::string& errorName,
        long code,
        const std::string& description,
        bool fatal,
        const std::string& sessionId) = 0;
    virtual void onSetFocus(bool focusAcquire = true) = 0;
};

/**
 * DoNotDisturbEngineInterface
 */
class DoNotDisturbEngineInterface {
public:
    virtual ~DoNotDisturbEngineInterface() = default;

    /**
     * @internal
     * Notifies the Engine that A DND change has been initiated by the client
     *
     * @param [in] doNotDisturb The DND Setting value
     * @return true if successful, false if change was rejected
     */
    virtual bool onDoNotDisturbChanged(const bool doNotDisturb) = 0;
};

/**
 * EqualizerControllerEngineInterface
 */
class EqualizerControllerEngineInterface {
public:
    virtual ~EqualizerControllerEngineInterface() = default;

    /**
     * Describes the equalizer bands supported by Alexa. The platform implementation may support a subset of these.
     */
    enum class EqualizerBand {
        /// Bass equalizer band
        BASS,
        /// Mid-range equalizer band
        MIDRANGE,
        /// Treble equalizer band
        TREBLE
    };

    /**
     * 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.
     */
    using EqualizerBandLevel = std::pair<EqualizerBand, int>;

    /**
     * @internal
     * Notifies the Engine that gain levels for one or more equalizer bands are being set directly on the device. If
     * unsupported levels are provided, the Engine should truncate the settings to the configured range.
     *
     * @param [in] bandLevels The equalizer bands to change and their gain settings as integer dB values.
     */
    virtual void onLocalSetBandLevels(const std::vector<EqualizerBandLevel>& bandLevels) = 0;

    /**
     * @internal
     * Notifies the Engine that relative adjustments to equalizer band gain levels are being made directly on the
     * device. If adjustments put the band level settings beyond the configured dB range, the Engine should truncate
     * the settings to the configured range.
     *
     * @param [in] bandAdjustments The equalizer bands to adjust and their relative gain adjustments as integer dB
     *             values.
     */
    virtual void onLocalAdjustBandLevels(const std::vector<EqualizerBandLevel>& bandAdjustments) = 0;

    /**
     * @internal
     * Notifies the Engine that the gain levels for the equalizer bands are being reset to their defaults.
     *
     * @param [in] bands The equalizer bands to reset. Empty @a bands resets all supported equalizer bands.
     */
    virtual void onLocalResetBands(const std::vector<EqualizerBand>& bands) = 0;
};

/**
 * Provides a string representation for an @c EqualizerControllerEngineInterface::EqualizerBand
 *
 * @param band The @c EqualizerBand
 * @return A string representation for the @c EqualizerBand
 */
inline std::string equalizerBandToString(const EqualizerControllerEngineInterface::EqualizerBand& band) {
    switch (band) {
        case EqualizerControllerEngineInterface::EqualizerBand::BASS:
            return "BASS";
        case EqualizerControllerEngineInterface::EqualizerBand::MIDRANGE:
            return "MIDRANGE";
        case EqualizerControllerEngineInterface::EqualizerBand::TREBLE:
            return "TREBLE";
    }
    return "UNKNOWN";
}

/**
 * Write an @c EqualizerControllerEngineInterface::EqualizerBand value to an @c ostream as a string
 *
 * @param stream The stream to write to
 * @param band The @c EqualizerBand value to write to the @c ostream
 * @return The @c ostream argument that was written to
 */
inline std::ostream& operator<<(std::ostream& stream, const EqualizerControllerEngineInterface::EqualizerBand& band) {
    stream << equalizerBandToString(band);
    return stream;
}

/**
 * AudioPlayerEngineInterface
 */
class AudioPlayerEngineInterface {
public:
    virtual ~AudioPlayerEngineInterface() = default;

    /**
     * Returns the current playback position of the audio player.
     * If the audio source is not playing, the most recent position played
     * should be returned.
     *
     * @return The audio player's playback position in milliseconds,
     * or @c TIME_UNKNOWN if the current media position is unknown or invalid.
     */
    virtual int64_t onGetPlayerPosition() = 0;

    /**
     * Returns the playback duration of the audio player.
     *
     * @return The audio player's playback duration in milliseconds,
     * or @c TIME_UNKNOWN if the duration is unknown or invalid.
     */
    virtual int64_t onGetPlayerDuration() = 0;

    /**
     * Sets the Alexa @c AudioPlayer interface as the active player the user sees on screen. This function is useful for 
     * scenarios in which the user most recently played a different Alexa-aware @c ExternalMediaPlayer media source, such
     * as a deep-linked media app or a local media source, and then manually returned the head unit visual focus to the
     * Alexa @c AudioPlayer GUI. Calling this function ensures the next VUI command or GUI interaction with the 
     * playback control buttons acts on the @c AudioPlayer source rather than the more recently played
     * @c ExternalMediaPlayer source.
     */
    virtual void onSetAsForegroundActivity() = 0;
};

/**
 * TemplateRuntimeEngineInterface
 */
class TemplateRuntimeEngineInterface {
public:
    virtual ~TemplateRuntimeEngineInterface() = default;

    /**
     * Notifies the Engine that a display card has been cleared from the screen. Upon getting
     * this notification, the @c TemplateRuntime will release the visual channel.
     */
    virtual void onDisplayCardCleared() = 0;
};

/**
 * AlexaClientEngineInterface
 */
class AlexaClientEngineInterface {
public:
    virtual ~AlexaClientEngineInterface() = default;

    virtual void onStopForegroundActivity() = 0;
};

/**
 * DeviceSetupEngineInterface
 */
class DeviceSetupEngineInterface {
public:
    virtual ~DeviceSetupEngineInterface() = default;

    virtual void onSetupCompleted() = 0;
};

/**
 * MediaPlaybackRequestorEngineInterface
 */
class MediaPlaybackRequestorEngineInterface {
public:
    /**
     * This enumeration represents the reason requestMediaPlayback API
     */
    enum class InvocationReason {
        /**
         * Initiating the media resume by the system
         */
        AUTOMOTIVE_STARTUP,

        /**
         * @note: This reason is not accepted yet.
         *
         * Initiating the media resume by the driver through physical button.
         */
        EXPLICIT_USER_ACTION
    };

    /**
     * Describes the response status of the @c requestMediaPlayback call for the media resume.
     */
    enum class MediaPlaybackRequestStatus {
        /**
         * Event call is successful
         */
        SUCCESS,
        /**
         * Event call is failed because Alexa is not connected, please retry.
         */
        FAILED_CAN_RETRY,
        /**
         * Event call is failed because of the threshold timeout.
         */
        FAILED_TIMEOUT,
        /**
         * Event call is failed because of an error.
         */
        ERROR
    };

public:
    virtual void onRequestMediaPlayback(InvocationReason invocationReason, long long int elapsedBootTime) = 0;
};

/**
 * Provides a string representation for an @c MediaPlaybackRequestorEngineInterface::InvocationReason
 *
 * @param invocationReason The @c InvocationReason
 * @return A string representation for the @c InvocationReason
 */
inline std::string invocationReasonToString(
    const MediaPlaybackRequestorEngineInterface::InvocationReason& invocationReason) {
    switch (invocationReason) {
        case MediaPlaybackRequestorEngineInterface::InvocationReason::AUTOMOTIVE_STARTUP:
            return "AUTOMOTIVE_STARTUP";
        case MediaPlaybackRequestorEngineInterface::InvocationReason::EXPLICIT_USER_ACTION:
            return "EXPLICIT_USER_ACTION";
    }
    return "UNKNOWN";
}

/**
 * Write an @c MediaPlaybackRequestorEngineInterface::InvocationReason value to an @c ostream as a string
 *
 * @param stream The stream to write to
 * @param band The @c InvocationReason value to write to the @c ostream
 * @return The @c ostream argument that was written to
 */
inline std::ostream& operator<<(
    std::ostream& stream,
    const MediaPlaybackRequestorEngineInterface::InvocationReason& invocationReason) {
    stream << invocationReasonToString(invocationReason);
    return stream;
}

/**
 * FeatureDiscoveryEngineInterface
 */
class FeatureDiscoveryEngineInterface {
public:
    virtual ~FeatureDiscoveryEngineInterface() = default;

    virtual bool onGetFeatures(const std::string& requestId, const std::string& discoveryRequests) = 0;
};

}  // namespace alexa
}  // namespace aace

#endif  // AACE_ALEXA_ALEXA_ENGINE_INTERFACES_H
