#include "Main.h"

void ProgramArmy::Init()
{
    _CurState = ProgArmySelectArmy;
    Name = String("Army");
}

ProgramResultType ProgramArmy::ExecuteStep()
{
    FrameHUDManager &FrameHUD = g_Context->Managers.FrameHUD;
    FrameUnitManager &FrameUnit = g_Context->Managers.FrameUnit;
    KnowledgeManager &Knowledge = g_Context->Managers.Knowledge;
    ArmyInfo &CurArmy = g_Context->Managers.Knowledge.Army();
    UINT ScoutGroupIndex = g_Context->Managers.ControlGroup.FindControlGroupIndex(ControlGroupBuildingSupport);
    UINT ArmyGroupIndex = g_Context->Managers.ControlGroup.FindControlGroupIndex(ControlGroupCombatAll);
    if(_CurState == ProgArmySelectArmy)
    {
        LogThreadEvent("Selecting army", LogStep);
        g_Context->Managers.KeyboardMouse.SendKey(KEY_0 + ArmyGroupIndex);
        _CurState = ProgArmyOrderArmy;
        CurArmy.NextMacroTime = GameTime() + 2.5;
        return ProgramResultStillExecuting;
    }
    if(_CurState == ProgArmyOrderArmy)
    {
        ActionButtonStateType State;
        if(!FrameHUD.ActionButtonPresent("Attack", State))
        {
            LogThreadEvent("Attack button not found", LogError);
            return ProgramResultFail;
        }

        Knowledge.UpdateArmyAction();

        Vec2f BaseLocation;
        if(CurArmy.State == ArmyStateIdleInBase)
        {
            BaseLocation = g_Context->Managers.Knowledge.RandomMusterLocation();
        }
        else if(CurArmy.State == ArmyStateMovingToDefend)
        {
            BaseLocation = CurArmy.AttackTarget;
        }
        else if(CurArmy.State == ArmyStateMovingToAttack)
        {
            BaseLocation = CurArmy.AttackTarget;
        }
        else if(CurArmy.State == ArmyStateGathering)
        {
            BaseLocation = g_Context->Managers.Minimap.ArmyLocation();
        }

        /*if(GameTime() > 450.0f)
        {
            BaseLocation = g_Context->Managers.Knowledge.EnemyLocation();
        }
        else
        {
            BaseLocation = g_Context->Managers.Knowledge.RandomMusterLocation();
        }*/
        Vec2i ScreenCoord = g_Context->Constants.MinimapCoordToScreenCoord(BaseLocation);
        g_Context->Managers.KeyboardMouse.SendKey(KEY_A);
        g_Context->Managers.KeyboardMouse.Click(ScreenCoord, MouseButtonLeft, ModifierNone);
        LogThreadEvent("Ordering army", LogDone);
        if(CurArmy.State == ArmyStateIdleInBase)
        {
            _CurState = ProgArmySelectScoutGroup;
            return ProgramResultStillExecuting;
        }
        else
        {
            return ProgramResultSuccess;
        }
    }
    if(_CurState == ProgArmySelectScoutGroup)
    {
        LogThreadEvent("Selecting Scout Group", LogStep);
        g_Context->Managers.KeyboardMouse.SendKey(KEY_0 + ScoutGroupIndex);
        _CurState = ProgArmySelectScout;
        return ProgramResultStillExecuting;
    }
    if(_CurState == ProgArmySelectScout)
    {
        if(FrameHUD.SoloPortrait() != NULL)
        {
            LogThreadEvent("No scouts found", LogStep);
            _CurState = ProgArmySelectArmyForNewScout;
            return ProgramResultStillExecuting;
        }
        Vector<UINT> ValidIndices;
        for(UINT Index = 0; Index < BubblePortraitCount; Index++)
        {
            Texture *CurTexture = FrameHUD.BubblePortraits(Index).Tex;
            if(CurTexture != NULL && CurTexture->ID() == Knowledge.ScoutUnit()->Name)
            {
                ValidIndices.PushEnd(Index);
            }
        }
        if(ValidIndices.Length() >= 2)
        {
            LogThreadEvent("Multiple scouts found", LogError);
        }
        if(ValidIndices.Length() == 0)
        {
            LogThreadEvent("No scouts found", LogStep);
            _CurState = ProgArmySelectArmyForNewScout;
            return ProgramResultStillExecuting;
        }
        else
        {
            Vec2i ScreenCoord = FrameHUD.ScreenCoordFromBubblePortraitIndex(ValidIndices.RandomElement());
            LogThreadEvent(String("Selecting scout: ") + ScreenCoord.CommaSeparatedString(), LogStep);
            g_Context->Managers.KeyboardMouse.Click(ScreenCoord, MouseButtonLeft, ModifierNone);
            _CurState = ProgArmyCenterScout;
            return ProgramResultStillExecuting;
        }
    }
    if(_CurState == ProgArmySelectArmyForNewScout)
    {
        LogThreadEvent("Selecting army for scout", LogStep);
        g_Context->Managers.KeyboardMouse.SendKey(KEY_0 + ArmyGroupIndex);
        _CurState = ProgArmySelectNewScout;
        return ProgramResultStillExecuting;
    }
    if(_CurState == ProgArmySelectNewScout)
    {
        if(FrameHUD.SoloPortrait() != NULL)
        {
            LogThreadEvent("Single unit selected", LogError);
            return ProgramResultFail;
        }
        Vector<UINT> ValidIndices;
        for(UINT Index = 0; Index < BubblePortraitCount; Index++)
        {
            Texture *CurTexture = FrameHUD.BubblePortraits(Index).Tex;
            if(CurTexture != NULL && CurTexture->ID() == Knowledge.ScoutUnit()->Name)
            {
                ValidIndices.PushEnd(Index);
            }
        }
        if(ValidIndices.Length() == 0)
        {
            LogThreadEvent("No scouts found in army", LogError);
            return ProgramResultFail;
        }
        else
        {
            Vec2i ScreenCoord = FrameHUD.ScreenCoordFromBubblePortraitIndex(ValidIndices.RandomElement());
            LogThreadEvent(String("Selecting new scout: ") + ScreenCoord.CommaSeparatedString(), LogStep);
            g_Context->Managers.KeyboardMouse.Click(ScreenCoord, MouseButtonLeft, ModifierNone);
            _CurState = ProgArmyCenterScout;
            return ProgramResultStillExecuting;
        }
    }
    if(_CurState == ProgArmyCenterScout)
    {
        if(FrameHUD.SoloPortrait() == NULL || FrameHUD.SoloPortrait()->ID() != Knowledge.ScoutUnit()->Name)
        {
            LogThreadEvent("Scout selection failed", LogError);
            return ProgramResultFail;
        }
        LogThreadEvent(String("Adding ") + FrameHUD.SoloPortrait()->ID() + String(" to scout group ") + String(ScoutGroupIndex), LogStep);
        g_Context->Managers.KeyboardMouse.SendShiftKey(KEY_0 + ScoutGroupIndex);
        
        g_Context->Managers.KeyboardMouse.SendCtrlKey(KEY_F);
        LogThreadEvent("Centering on scout", LogStep);
        _CurState = ProgArmyOrderScout;
        return ProgramResultStillExecuting;
    }
    if(_CurState == ProgArmyOrderScout)
    {
        if(FrameHUD.SoloPortrait() == NULL || FrameHUD.SoloPortrait()->ID() != Knowledge.ScoutUnit()->Name)
        {
            LogThreadEvent("Scout selection failed", LogError);
            return ProgramResultFail;
        }
        Knowledge.ReportCenteredOnScout();
        Vec2i ClickPos = g_Context->Constants.MinimapCoordToScreenCoord(Knowledge.MinimapScoutLocation());

        bool SkipScoutOrder = false;
        UINT AlliedUnitCount = 0;
        UINT EnemyUnitCount = 0;
        for(UINT UnitIndex = 0; UnitIndex < FrameUnit.UnitsThisFrame().Length(); UnitIndex++)
        {
            const FrameUnitInfo &CurUnit = *FrameUnit.UnitsThisFrame()[UnitIndex];
            if(CurUnit.Owner == PlayerAlly && CurUnit.Clickable())
            {
                AlliedUnitCount++;
            }
            if(CurUnit.Owner == PlayerEnemy)
            {
                EnemyUnitCount++;
            }
        }

        String Verb = "Scouting ";
        if(AlliedUnitCount > 5)
        {
            SkipScoutOrder = true;
            LogThreadEvent("Scouting skipped; allies nearby", LogDone);
        }
        else if(EnemyUnitCount > AlliedUnitCount)
        {
            Verb = "Retreating ";
            ClickPos = g_Context->Constants.MinimapCoordToScreenCoord(Knowledge.RandomMusterLocation());
        }

        if(!SkipScoutOrder)
        {
            LogThreadEvent(Verb + FrameHUD.SoloPortrait()->ID() + String(":") + ClickPos.CommaSeparatedString(), LogDone);
            g_Context->Managers.KeyboardMouse.SendKey(KEY_M);
            g_Context->Managers.KeyboardMouse.Click(ClickPos, MouseButtonLeft, ModifierNone);
            return ProgramResultSuccess;
        }
    }
    HANDLE_CRITICAL_FAILURE;
}