/*
 * Copyright (C) 2020 BfaCore
 *
 * This program 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 2 of the License, or (at your
 * option) any later version.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __BATTLEGROUNDAB_H
#define __BATTLEGROUNDAB_H

#include "Battleground.h"
#include "BattlegroundScore.h"
#include "Object.h"

enum BG_AB_WorldStates
{
    BG_AB_OP_OCCUPIED_BASES_HORDE       = 1778,
    BG_AB_OP_OCCUPIED_BASES_ALLY        = 1779,
    BG_AB_OP_RESOURCES_ALLY             = 1776,
    BG_AB_OP_RESOURCES_HORDE            = 1777,
    BG_AB_OP_RESOURCES_MAX              = 1780,
    BG_AB_OP_RESOURCES_WARNING          = 1955
/*
    BG_AB_OP_STABLE_ICON                = 1842,             //Stable map icon (NONE)
    BG_AB_OP_STABLE_STATE_ALIENCE       = 1767,             //Stable map state (ALIENCE)
    BG_AB_OP_STABLE_STATE_HORDE         = 1768,             //Stable map state (HORDE)
    BG_AB_OP_STABLE_STATE_CON_ALI       = 1769,             //Stable map state (CON ALIENCE)
    BG_AB_OP_STABLE_STATE_CON_HOR       = 1770,             //Stable map state (CON HORDE)
    BG_AB_OP_FARM_ICON                  = 1845,             //Farm map icon (NONE)
    BG_AB_OP_FARM_STATE_ALIENCE         = 1772,             //Farm state (ALIENCE)
    BG_AB_OP_FARM_STATE_HORDE           = 1773,             //Farm state (HORDE)
    BG_AB_OP_FARM_STATE_CON_ALI         = 1774,             //Farm state (CON ALIENCE)
    BG_AB_OP_FARM_STATE_CON_HOR         = 1775,             //Farm state (CON HORDE)

    BG_AB_OP_BLACKSMITH_ICON            = 1846,             //Blacksmith map icon (NONE)
    BG_AB_OP_BLACKSMITH_STATE_ALIENCE   = 1782,             //Blacksmith map state (ALIENCE)
    BG_AB_OP_BLACKSMITH_STATE_HORDE     = 1783,             //Blacksmith map state (HORDE)
    BG_AB_OP_BLACKSMITH_STATE_CON_ALI   = 1784,             //Blacksmith map state (CON ALIENCE)
    BG_AB_OP_BLACKSMITH_STATE_CON_HOR   = 1785,             //Blacksmith map state (CON HORDE)
    BG_AB_OP_LUMBERMILL_ICON            = 1844,             //Lumber Mill map icon (NONE)
    BG_AB_OP_LUMBERMILL_STATE_ALIENCE   = 1792,             //Lumber Mill map state (ALIENCE)
    BG_AB_OP_LUMBERMILL_STATE_HORDE     = 1793,             //Lumber Mill map state (HORDE)
    BG_AB_OP_LUMBERMILL_STATE_CON_ALI   = 1794,             //Lumber Mill map state (CON ALIENCE)
    BG_AB_OP_LUMBERMILL_STATE_CON_HOR   = 1795,             //Lumber Mill map state (CON HORDE)
    BG_AB_OP_GOLDMINE_ICON              = 1843,             //Gold Mine map icon (NONE)
    BG_AB_OP_GOLDMINE_STATE_ALIENCE     = 1787,             //Gold Mine map state (ALIENCE)
    BG_AB_OP_GOLDMINE_STATE_HORDE       = 1788,             //Gold Mine map state (HORDE)
    BG_AB_OP_GOLDMINE_STATE_CON_ALI     = 1789,             //Gold Mine map state (CON ALIENCE
    BG_AB_OP_GOLDMINE_STATE_CON_HOR     = 1790,             //Gold Mine map state (CON HORDE)
*/
};

const uint32 BG_AB_OP_NODESTATES[5] =    {1767, 1782, 1772, 1792, 1787};

const uint32 BG_AB_OP_NODEICONS[5]  =    {1842, 1846, 1845, 1844, 1843};

/* Note: code uses that these IDs follow each other */
enum BG_AB_NodeObjectId
{
    BG_AB_OBJECTID_NODE_BANNER_0    = 227420,       // Stables banner
    BG_AB_OBJECTID_NODE_BANNER_1    = 227522,       // Blacksmith banner
    BG_AB_OBJECTID_NODE_BANNER_2    = 227536,       // Farm banner
    BG_AB_OBJECTID_NODE_BANNER_3    = 227544,       // Lumber mill banner
    BG_AB_OBJECTID_NODE_BANNER_4    = 227538        // Gold mine banner
};

enum BG_AB_ObjectType
{
    BG_AB_OBJECT_BANNER                  = 0,
    //gates
    BG_AB_OBJECT_GATE_A                  = 5,
    BG_AB_OBJECT_GATE_H                  = 6,
    //buffs
    BG_AB_OBJECT_SPEEDBUFF_STABLES       = 7,
    BG_AB_OBJECT_REGENBUFF_STABLES       = 8,
    BG_AB_OBJECT_BERSERKBUFF_STABLES     = 9,
    BG_AB_OBJECT_SPEEDBUFF_BLACKSMITH    = 10,
    BG_AB_OBJECT_REGENBUFF_BLACKSMITH    = 11,
    BG_AB_OBJECT_BERSERKBUFF_BLACKSMITH  = 12,
    BG_AB_OBJECT_SPEEDBUFF_FARM          = 13,
    BG_AB_OBJECT_REGENBUFF_FARM          = 14,
    BG_AB_OBJECT_BERSERKBUFF_FARM        = 15,
    BG_AB_OBJECT_SPEEDBUFF_LUMBER_MILL   = 16,
    BG_AB_OBJECT_REGENBUFF_LUMBER_MILL   = 17,
    BG_AB_OBJECT_BERSERKBUFF_LUMBER_MILL = 18,
    BG_AB_OBJECT_SPEEDBUFF_GOLD_MINE     = 19,
    BG_AB_OBJECT_REGENBUFF_GOLD_MINE     = 20,
    BG_AB_OBJECT_BERSERKBUFF_GOLD_MINE   = 21,
    BG_AB_OBJECT_MAX                     = 22
};

/* Object id templates from DB */
enum BG_AB_ObjectTypes
{
    BG_AB_OBJECTID_GATE_A               = 180255,
    BG_AB_OBJECTID_GATE_H               = 180256
};

enum BG_AB_Timers
{
    BG_AB_FLAG_CAPTURING_TIME           = 60000
};

enum BG_AB_Score
{
    BG_AB_WARNING_NEAR_VICTORY_SCORE    = 1200,
    BG_AB_MAX_TEAM_SCORE                = 1500
};

/* do NOT change the order, else wrong behaviour */
enum BG_AB_BattlegroundNodes
{
    BG_AB_NODE_STABLES          = 0,
    BG_AB_NODE_BLACKSMITH       = 1,
    BG_AB_NODE_FARM             = 2,
    BG_AB_NODE_LUMBER_MILL      = 3,
    BG_AB_NODE_GOLD_MINE        = 4,

    BG_AB_DYNAMIC_NODES_COUNT   = 5,                        // dynamic nodes that can be captured

    BG_AB_SPIRIT_ALIANCE        = 5,
    BG_AB_SPIRIT_HORDE          = 6,

    BG_AB_ALL_NODES_COUNT       = 7                         // all nodes (dynamic and static)
};

enum ArathiBannerSpellVisual
{
    SPELL_VISUAL_NEUTRAL            = 42975,
    SPELL_VISUAL_HORDE_CONTESTED    = 42976,
    SPELL_VISUAL_ALLIANCE_CONTESTED = 42978,
    SPELL_VISUAL_HORDE_OCCUPIED     = 42979,
    SPELL_VISUAL_ALLIANCE_OCCUPIED  = 42980
};

enum ArathiBannerWorldState
{
    AB_NEUTRAL            = 0,
    AB_HORDE_CONTESTED    = 2,
    AB_ALLIANCE_CONTESTED = 3,
    AB_HORDE_OCCUPIED     = 4,
    AB_ALLIANCE_OCCUPIED  = 5
};

enum BG_AB_BroadcastTexts
{
    BG_AB_TEXT_ALLIANCE_NEAR_VICTORY    = 10598,
    BG_AB_TEXT_HORDE_NEAR_VICTORY       = 10599,
};

struct ABNodeInfo
{
    uint32 NodeId;
    uint32 TextAllianceAssaulted;
    uint32 TextHordeAssaulted;
    uint32 TextAllianceTaken;
    uint32 TextHordeTaken;
    uint32 TextAllianceDefended;
    uint32 TextHordeDefended;
    uint32 TextAllianceClaims;
    uint32 TextHordeClaims;
};

ABNodeInfo const ABNodes[BG_AB_DYNAMIC_NODES_COUNT] =
{
    { BG_AB_NODE_STABLES,     10199, 10200, 10203, 10204, 10201, 10202, 10286, 10287 },
    { BG_AB_NODE_BLACKSMITH,  10211, 10212, 10213, 10214, 10215, 10216, 10290, 10291 },
    { BG_AB_NODE_FARM,        10217, 10218, 10219, 10220, 10221, 10222, 10288, 10289 },
    { BG_AB_NODE_LUMBER_MILL, 10224, 10225, 10226, 10227, 10228, 10229, 10284, 10285 },
    { BG_AB_NODE_GOLD_MINE,   10230, 10231, 10232, 10233, 10234, 10235, 10282, 10283 }
};

enum BG_AB_NodeStatus
{
    BG_AB_NODE_TYPE_NEUTRAL             = 0,
    BG_AB_NODE_TYPE_CONTESTED           = 1,
    BG_AB_NODE_STATUS_ALLY_CONTESTED    = 1,
    BG_AB_NODE_STATUS_HORDE_CONTESTED   = 2,
    BG_AB_NODE_TYPE_OCCUPIED            = 3,
    BG_AB_NODE_STATUS_ALLY_OCCUPIED     = 3,
    BG_AB_NODE_STATUS_HORDE_OCCUPIED    = 4
};

enum BG_AB_Sounds
{
    BG_AB_SOUND_NODE_CLAIMED            = 8192,
    BG_AB_SOUND_NODE_CAPTURED_ALLIANCE  = 8173,
    BG_AB_SOUND_NODE_CAPTURED_HORDE     = 8213,
    BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE = 8212,
    BG_AB_SOUND_NODE_ASSAULTED_HORDE    = 8174,
    BG_AB_SOUND_NEAR_VICTORY            = 8456
};

enum BG_AB_Objectives
{
    AB_OBJECTIVE_ASSAULT_BASE           = 122,
    AB_OBJECTIVE_DEFEND_BASE            = 123
};

enum BG_AB_ExploitTeleportLocations
{
    AB_EXPLOIT_TELEPORT_LOCATION_ALLIANCE = 3705,
    AB_EXPLOIT_TELEPORT_LOCATION_HORDE    = 3706
};

#define BG_AB_NotABBGWeekendHonorTicks      260
#define BG_AB_ABBGWeekendHonorTicks         160
#define BG_AB_NotABBGWeekendReputationTicks 160
#define BG_AB_ABBGWeekendReputationTicks    120

#define AB_EVENT_START_BATTLE               9158 // Achievement: Let's Get This Done

const float BG_AB_NodePositions[BG_AB_DYNAMIC_NODES_COUNT][4] =
{
    {1166.785f, 1200.132f, -56.70859f, 0.9075713f},         // stables
    {977.0156f, 1046.616f, -44.80923f, -2.600541f},         // blacksmith
    {806.1821f, 874.2723f, -55.99371f, -2.303835f},         // farm
    {856.1419f, 1148.902f, 11.18469f, -2.303835f},          // lumber mill
    {1146.923f, 848.1782f, -110.917f, -0.7330382f}          // gold mine
};

// x, y, z, o, rot0, rot1, rot2, rot3
const float BG_AB_DoorPositions[2][8] =
{
    {1284.597f, 1281.167f, -15.97792f, 0.7068594f, 0.012957f, -0.060288f, 0.344959f, 0.93659f},
    {708.0903f, 708.4479f, -17.8342f, -2.391099f, 0.050291f, 0.015127f, 0.929217f, -0.365784f}
};

// Tick intervals and given points: case 0, 1, 2, 3, 4, 5 captured nodes
const uint32 BG_AB_TickIntervals[6] = {0, 12000, 9000, 6000, 3000, 1000};
const uint32 BG_AB_TickPoints[6] = {0, 10, 10, 10, 10, 30};

// WorldSafeLocs ids for 5 nodes, and for ally, and horde starting location
const uint32 BG_AB_GraveyardIds[BG_AB_ALL_NODES_COUNT] = {895, 894, 893, 897, 896, 898, 899};

// x, y, z, o
const float BG_AB_BuffPositions[BG_AB_DYNAMIC_NODES_COUNT][4] =
{
    {1185.71f, 1185.24f, -56.36f, 2.56f},                   // stables
    {990.75f, 1008.18f, -42.60f, 2.43f},                    // blacksmith
    {817.66f, 843.34f, -56.54f, 3.01f},                     // farm
    {807.46f, 1189.16f, 11.92f, 5.44f},                     // lumber mill
    {1146.62f, 816.94f, -98.49f, 6.14f}                     // gold mine
};

const float BG_AB_SpiritGuidePos[BG_AB_ALL_NODES_COUNT][4] =
{
    {1200.03f, 1171.09f, -56.47f, 5.15f},                   // stables
    {1017.43f, 960.61f, -42.95f, 4.88f},                    // blacksmith
    {833.00f, 793.00f, -57.25f, 5.27f},                     // farm
    {775.17f, 1206.40f, 15.79f, 1.90f},                     // lumber mill
    {1207.48f, 787.00f, -83.36f, 5.51f},                    // gold mine
    {1354.05f, 1275.48f, -11.30f, 4.77f},                   // alliance starting base
    {714.61f, 646.15f, -10.87f, 4.34f}                      // horde starting base
};

struct BattlegroundABScore final : public BattlegroundScore
{
    friend class BattlegroundAB;

    protected:
        BattlegroundABScore(ObjectGuid playerGuid, uint32 team) : BattlegroundScore(playerGuid, team), BasesAssaulted(0), BasesDefended(0) { }

        void UpdateScore(uint32 type, uint32 value) override
        {
            switch (type)
            {
                case SCORE_BASES_ASSAULTED:
                    BasesAssaulted += value;
                    break;
                case SCORE_BASES_DEFENDED:
                    BasesDefended += value;
                    break;
                default:
                    BattlegroundScore::UpdateScore(type, value);
                    break;
            }
        }

        void BuildPvPLogPlayerDataPacket(WorldPackets::Battleground::PVPMatchStatistics::PVPMatchPlayerStatistics& playerData) const override
        {
            BattlegroundScore::BuildPvPLogPlayerDataPacket(playerData);

            playerData.Stats.emplace_back(AB_OBJECTIVE_ASSAULT_BASE, BasesAssaulted);
            playerData.Stats.emplace_back(AB_OBJECTIVE_DEFEND_BASE, BasesDefended);
        }

        uint32 GetAttr1() const final override { return BasesAssaulted; }
        uint32 GetAttr2() const final override { return BasesDefended; }

        uint32 BasesAssaulted;
        uint32 BasesDefended;
};

class BattlegroundAB : public Battleground
{
    public:
        BattlegroundAB();
        ~BattlegroundAB();

        void AddPlayer(Player* player) override;
        void StartingEventCloseDoors() override;
        void StartingEventOpenDoors() override;
        void RemovePlayer(Player* player, ObjectGuid guid, uint32 team) override;
        void HandleAreaTrigger(Player* source, uint32 trigger, bool entered) override;
        bool SetupBattleground() override;
        void Reset() override;
        void EndBattleground(uint32 winner) override;
        WorldSafeLocsEntry const* GetClosestGraveYard(Player* player) override;
        WorldSafeLocsEntry const* GetExploitTeleportLocation(Team team) override;

        /* Scorekeeping */
        bool UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true) override;

        void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& packet) override;

        /* Nodes occupying */
        void EventPlayerClickedOnFlag(Player* source, GameObject* target_obj) override;

        /* achievement req. */
        bool IsAllNodesControlledByTeam(uint32 team) const override;
        bool CheckAchievementCriteriaMeet(uint32 /*criteriaId*/, Player const* /*player*/, Unit const* /*target*/ = nullptr, uint32 /*miscvalue1*/ = 0) override;

        uint32 GetPrematureWinner() override;
    private:
        void PostUpdateImpl(uint32 diff) override;
        /* Gameobject spawning/despawning */
        void _ChangeBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay);
        void _SendNodeUpdate(uint8 node);

        /* Creature spawning/despawning */
        /// @todo working, scripted peons spawning
        void _NodeOccupied(uint8 node, Team team);
        void _NodeDeOccupied(uint8 node);

        /* Nodes info:
            0: neutral
            1: ally contested
            2: horde contested
            3: ally occupied
            4: horde occupied     */
        ArathiBannerWorldState m_BannerWorldState[BG_AB_DYNAMIC_NODES_COUNT];
        uint8                  m_Nodes[BG_AB_DYNAMIC_NODES_COUNT];
        uint8                  m_prevNodes[BG_AB_DYNAMIC_NODES_COUNT];
        uint32                 m_NodeTimers[BG_AB_DYNAMIC_NODES_COUNT];
        uint32                 m_lastTick[BG_TEAMS_COUNT];
        uint32                 m_HonorScoreTics[BG_TEAMS_COUNT];
        uint32                 m_ReputationScoreTics[BG_TEAMS_COUNT];
        bool                   m_IsInformedNearVictory;
        uint32                 m_HonorTics;
        uint32                 m_ReputationTics;
        // need for achievements
        bool                   m_TeamScores500Disadvantage[BG_TEAMS_COUNT];
};
#endif
