/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DEF_I18N
#define DEF_I18N

#include "Tools/Format.hpp"
#include <vector>
#include <string>
#include <cstddef>

/// Macros to shorten the code required to produce a translation
#define _(ix) I18n::translate(I18n::ix)
#define _f(ix, ...) I18n::translateFormat(I18n::ix, __VA_ARGS__)

namespace I18n
{
    enum : std::size_t
    {
        MenuPlay                               = 0,
        MenuVersus                             = 1,
        MenuMultiplayer                        = 2,
        MenuOptions                            = 3,
        MenuExit                               = 4,
        Menu2Players                           = 5,
        Menu3Players                           = 6,
        Menu4Players                           = 7,
        Menu5Players                           = 8,
        Menu6Players                           = 9,
        MenuSingleRound                        = 10,
        Menu2Rounds                            = 11,
        Menu3Rounds                            = 12,
        Menu4Rounds                            = 13,
        Menu5Rounds                            = 14,
        HeaderBack                             = 15,
        CharacterSelectionTitle                = 16,
        CharacterSelectionSubtitle             = 17,
        CharacterSelectionNoName               = 18,
        CharacterSelectionAIName               = 19,
        ArenaSelectionTitle                    = 20,
        ArenaSelectionSubtitle                 = 21,
        ArenaSelectionValidate                 = 22,
        ClientLobbyTitle                       = 23,
        ClientLobbySubtitle                    = 24,
        ClientLobbyAddress                     = 25,
        ClientLobbyPort                        = 26,
        ClientLobbyUsername                    = 27,
        ClientLobbyConnect                     = 28,
        ClientLobbyConnecting                  = 29,
        ClientLobbyUnresolvable                = 30,
        DialogOK                               = 31,
        DialogCancel                           = 32,
        KickedConnectionLost                   = 33,
        KickedVersionMismatch                  = 34,
        KickedServerIsFull                     = 35,
        KickedServerNoCharacter                = 36,
        KickedInactivity                       = 37,
        KickedIllegalCommand                   = 38,
        KickedUnintelligibleByServer           = 39,
        KickedUnintelligibleByClient           = 40,
        KickedConnectionTimeout                = 41,
        KickedClientNoArena                    = 42,
        KickedClientNoCharacter                = 43,
        KickedClientDecision                   = 44,
        KickedENetError                        = 45,
        KickedUnknownReason                    = 46,
        OptionsTitle                           = 47,
        OptionsSubtitle                        = 48,
        OptionsGeneral                         = 49,
        OptionsControls                        = 50,
        OptionsDisplay                         = 51,
        OptionsRefreshRate                     = 52,
        OptionsAudio                           = 53,
        OptionsLocale                          = 54,
        OptionsMiscellaneous                   = 55,
        OptionsDisplayMode                     = 56,
        OptionsDisplayModeFullscreenBorderless = 57,
        OptionsDisplayModeFullscreenNative     = 58,
        OptionsDisplayModeWindowed             = 59,
        OptionsDisplayResolution               = 60,
        OptionsDisplayRevertToDefault          = 61,
        OptionsDisplayApply                    = 62,
        OptionsDisplayAppliedAtNextRestart     = 63,
        OptionsRefreshRateVerticalSync         = 64,
        OptionsRefreshRateFramesPerSecond      = 65,
        OptionsAudioMaster                     = 66,
        OptionsAudioMusic                      = 67,
        OptionsAudioAmbient                    = 68,
        OptionsAudioSoundEffects               = 69,
        OptionsLocaleLanguage                  = 70,
        OptionsDynamicCamera                   = 71,
        OptionsShakingCamera                   = 72,
        OptionsShowFrameRate                   = 73,
        OptionsShowHitboxes                    = 74,
        OptionsControlsBinderID                = 75,
        OptionsControlsActiveDevice            = 76,
        OptionsControlsResetJoystick           = 77,
        OptionsControlsKeyboard                = 78,
        OptionsControlsPunch                   = 79,
        OptionsControlsKick                    = 80,
        OptionsControlsThrow                   = 81,
        OptionsControlsJump                    = 82,
        OptionsControlsMoveLeft                = 83,
        OptionsControlsMoveRight               = 84,
        OptionsControlsCrouch                  = 85,
        KeyJoyButton                           = 86,
        KeyJoyAxisNegative                     = 87,
        KeyJoyAxisPositive                     = 88,
        KeyJoyHatUp                            = 89,
        KeyJoyHatDown                          = 90,
        KeyJoyHatLeft                          = 91,
        KeyJoyHatRight                         = 92,
        KeyArrowUp                             = 93,
        KeyArrowDown                           = 94,
        KeyArrowLeft                           = 95,
        KeyArrowRight                          = 96,
        KeySpacebar                            = 97,
        KeyBackspace                           = 98,
        KeyReturn                              = 99,
        FpsCounterValue                        = 100,
        RoundAnnouncerReady                    = 101,
        RoundAnnouncerNumberedRound            = 102,
        RoundAnnouncerFinalRound               = 103,
        RoundAnnouncerFight                    = 104,
        RoundResultDrawGame                    = 105,
        RoundResultNetworkedWinner             = 106,
        RoundResultLocalWinner                 = 107,
        LocalGamePauseTitle                    = 108,
        LocalGamePauseRestart                  = 109,
        LocalGameEndReplay                     = 110,
        NetworkedGamePauseTitle                = 111,
        NetworkedGamePauseDisconnect           = 112,
        PauseResume                            = 113,
        PauseToMainMenu                        = 114,
        PeerInfoCharacter                      = 115,
        PeerInfoUsername                       = 116,
        PeerInfoPing                           = 117,
        PeerInfoSpectators                     = 118,
        ServerEventGameScheduled               = 119,
        ServerEventMatchmakingStack            = 120,
        ServerEventMatchmakingRandom           = 121,
        ServerEventMatchmakingChallenger       = 122,
        ServerEventPeerJoined                  = 123,
        ServerEventPeerLeft                    = 124,
        ServerEventPeerKickedForInactivity     = 125,
        ServerEventRulesVersus                 = 126,
        ServerEventRulesDefault                = 127,

        /// Not a key for translation, utility to store the number of entries
        EntrySize                              = 128
    };

    /// Encodes a translation context as a comparable vector of named values
    /// Useful to provide several translations for a single entry based on the
    /// specific values that a named value can take
    /// (e.g. to handle plurals or genders)
    class TranslationContext
    {
        public:
            TranslationContext() = default;

            explicit TranslationContext(
                const std::vector<Format::NamedValue> &context) :
            m_context(context) {}

            bool operator==(const TranslationContext &other) const
            {
                if (m_context.size() != other.m_context.size())
                    return false;

                for (std::size_t i = 0; i < m_context.size(); i++)
                {
                    if (m_context[i].first != other.m_context[i].first ||
                        m_context[i].second != other.m_context[i].second)
                        return false;
                }

                return true;
            }

            /// Returns the number of matching name values
            /// with the \p other context
            std::size_t matches(const TranslationContext &other) const
            {
                std::size_t matchesCount = 0;

                for (const Format::NamedValue &rhs : m_context)
                {
                    for (const Format::NamedValue &lhs : other.m_context)
                    {
                        if (rhs.first == lhs.first && rhs.second == lhs.second)
                        {
                            matchesCount++;
                            break;
                        }
                    }
                }

                return matchesCount;
            }

            void addNamedValue(const std::string &name, const std::string &val)
            {
                m_context.emplace_back(name, val);
            }

            std::size_t size() const
            {
                return m_context.size();
            }

            bool empty() const
            {
                return m_context.empty();
            }

        private:
            std::vector<Format::NamedValue> m_context;
    };

    /// Browse the list of known locales to return
    /// the best supported match for \p localeName
    std::string findLocale(const std::string &localeName);

    /// Attempts to load the translation entries for the
    /// locale \p localeName from locale/[localeName].xml
    bool loadEntries(const std::string &localeName);

    /// Returns the translation for the entry of key \p index
    /// in the current locale, optionally in a specific \p context
    std::string translate(std::size_t index,
                          const TranslationContext &context = {});

    /// Returns the translation for the entry of key \p index
    /// that includes formatting placeholders and generate the formatted string
    template <typename... Ts>
    std::string translateFormat(std::size_t index, Ts&&... args)
    {
        const std::vector<Format::NamedValue> placeholders =
            Format::named_values(std::forward<Ts>(args)...);

        return Format::apply_format(
            translate(index, TranslationContext(placeholders)), placeholders);
    }

    /// Represents a supported locale
    struct Locale
    {
        std::string code; ///< ISO 639-1 language code for the locale
        std::string name; ///< Display name for the locale

        /// Other ISO 639-1 language codes covered by the locale,
        /// e.g., "nb" and "nn" for "no",
        /// if no translations exist specifically for "nb" or "nn"
        std::vector<std::string> covers;
    };

    /// Get the supported locales
    const std::vector<Locale> &getLocales();

    /// Get the user locale code
    std::string getUserLocale();
}

#endif
