#include "Main.h"
#include "Strats.h"

void ProductionThread::Reset()
{
    _Priority = 0;
    _Name = "Production";
}

void ProductionThread::Update()
{
    UpdateProductionGoals();
    ControlGroupInfo &SupportGroup = g_Context->Managers.ControlGroup.FindControlGroup(ControlGroupBuildingSupport);
    KnowledgeManager &Knowledge = g_Context->Managers.Knowledge;
    for(UINT Index = 0; Index < _ActiveProductionGoals.Length(); Index++)
    {
        UnitEntry *CurEntry = _ActiveProductionGoals[Index];
        if(CurEntry == NULL)
        {
            g_Context->Files.Assert << "CurEntry == NULL\n";
            return;
        }
        if(g_Context->Managers.FrameHUD.HaveResourcesForUnit(*CurEntry))
        {
            if(CurEntry->BuildClass == BuildClassResearch)
            {
                _Upgrade.Init(CurEntry);
                _ProgramToExecute = &_Upgrade;
                _CurAction = String("Research") + CurEntry->Name;
                _Priority = 2;
                return;
            }
            if(CurEntry->BuildClass == BuildClassLarvae)
            {
                _Larvae.Init(CurEntry);
                _ProgramToExecute = &_Larvae;
                _CurAction = String("Produce") + CurEntry->Name;
                _Priority = 2;
                return;
            }
            if(CurEntry->BuildClass == BuildClassWorker)
            {
                if(CurEntry->SecondaryType == UnitSecondaryExtractor)
                {
                    _Extractor.Init();
                    _ProgramToExecute = &_Extractor;
                    _CurAction = String("ProduceExtractor");
                    _Priority = 2;
                    return;
                }
                else if(CurEntry->SecondaryType == UnitSecondaryBase)
                {
                    _Building.Init(CurEntry);
                    _ProgramToExecute = &_Building;
                    _CurAction = String("Produce") + CurEntry->Name;
                    _Priority = 2;
                    return;
                }
                else
                {
                    _Building.Init(CurEntry);
                    _ProgramToExecute = &_Building;
                    _CurAction = String("Produce") + CurEntry->Name;
                    _Priority = 2;
                    return;
                }
            }
            if(CurEntry->BuildClass == BuildClassMorphBuilding)
            {
                if(CurEntry->Name == "Lair" || CurEntry->Name == "Hive")
                {
                    _ZergBaseUpgrade.Init(CurEntry);
                    _ProgramToExecute = &_ZergBaseUpgrade;
                    _CurAction = String("Upgrade") + CurEntry->Name;
                    _Priority = 2;
                    return;
                }
            }
            if(CurEntry->BuildClass == BuildClassQueue)
            {
                //
                // This is flawed; units in production != available production structures
                //
                int ProductionBuildingCount = SupportGroup.CountPrerequisiteUnitsMatchingName(CurEntry->BuiltAt);
                int UnitsInProduction = Knowledge.CountUnitsInProduction(CurEntry->Name);
                if(ProductionBuildingCount - UnitsInProduction > 0)
                {
                    if(CurEntry->Name == "Queen")
                    {
                        _Queen.Init();
                        _ProgramToExecute = &_Queen;
                        _CurAction = "ProduceQueen";
                        _Priority = 2;
                        return;
                    }
                    else
                    {
                        _Queue.Init(CurEntry);
                        _ProgramToExecute = &_Queue;
                        _CurAction = "ProduceQueue";
                        _Priority = 2;
                        return;
                    }
                }
            }
        }
    }
}

UINT SupplyForesightFromSupplyCap(UINT SupplyCap)
{
    if(SupplyCap >= 100)
    {
        return 12;
    }
    if(SupplyCap >= 50)
    {
        return 8;
    }
    if(SupplyCap >= 30)
    {
        return 6;
    }
    if(SupplyCap >= 25)
    {
        return 4;
    }
    if(SupplyCap >= 18)
    {
        return 2;
    }
    if(g_Context->Managers.Knowledge.MyRace() == RaceProtoss)
    {
        return 1;
    }
    return 0;
}

UINT SupplyProductionCapFromSupplyCap(UINT SupplyCap)
{
    if(SupplyCap >= 100)
    {
        return 4;
    }
    if(SupplyCap >= 75)
    {
        return 3;
    }
    if(SupplyCap >= 50)
    {
        return 2;
    }
    if(SupplyCap >= 40)
    {
        return 1;
    }
    return 1;
}

void ProductionThread::AddMultipleBuilding(const String &Building, UINT BuildingIndex, UINT SupplyRequirement, bool &BuildingAdded)
{
    if(g_Context->Managers.FrameHUD.SupplyCurrent() < SupplyRequirement || BuildingAdded)
    {
        return;
    }
    UnitEntry *Entry = g_Context->Managers.Database.GetUnitEntry(Building);
    if(Entry == NULL)
    {
        g_Context->Files.Assert << "UnitEntry not found: " << Building << endl;
        return;
    }
    ControlGroupInfo &SupportGroup = g_Context->Managers.ControlGroup.FindControlGroup(ControlGroupBuildingSupport);
    if(Entry->Requirement != "None")
    {
        UINT PrerequisiteCount = SupportGroup.CountPrerequisiteUnitsMatchingName(Entry->Requirement);
        if(PrerequisiteCount == 0)
        {
            return;
        }
    }
    const UINT BuildingCount = SupportGroup.CountPrerequisiteUnitsMatchingName(Building);
    const UINT BuildingInProduction = g_Context->Managers.Knowledge.CountUnitsInProduction(Building);
    if(BuildingCount + BuildingInProduction >= BuildingIndex + 1)
    {
        return;
    }
    _ActiveProductionGoals.PushEnd(Entry);
    BuildingAdded = true;
}

void ProductionThread::AddResearch(const String &Research, UINT SupplyRequirement, bool &ResearchAdded)
{
    if(g_Context->Managers.FrameHUD.SupplyCurrent() < SupplyRequirement || ResearchAdded)
    {
        return;
    }
    UnitEntry *Entry = g_Context->Managers.Database.GetUnitEntry(Research);
    if(Entry == NULL)
    {
        return;
    }
    if(g_Context->Managers.FrameHUD.Gas() < int(Entry->GasCost))
    {
        return;
    }
    ControlGroupInfo &SupportGroup = g_Context->Managers.ControlGroup.FindControlGroup(ControlGroupBuildingSupport);
    if(Entry->Requirement != "None")
    {
        const UINT PrerequisiteCount = SupportGroup.CountPrerequisiteUnitsMatchingName(Entry->Requirement);
        if(PrerequisiteCount == 0)
        {
            return;
        }
    }
    if(Entry->BuiltAt != "None")
    {
        const UINT BuildAtCount = SupportGroup.CountPrerequisiteUnitsMatchingName(Entry->BuiltAt);
        if(BuildAtCount == 0)
        {
            return;
        }
    }
    const bool ResearchAlreadyDone = g_Context->Managers.Knowledge.ResearchCompleted(Research);
    if(ResearchAlreadyDone)
    {
        return;
    }
    _ActiveProductionGoals.PushEnd(Entry);
    ResearchAdded = true;
}

void ProductionThread::AddSingletonBuilding(const String &Building, UINT SupplyRequirement, bool &BuildingAdded)
{
    if(g_Context->Managers.FrameHUD.SupplyCurrent() < SupplyRequirement || BuildingAdded)
    {
        return;
    }
    UnitEntry *Entry = g_Context->Managers.Database.GetUnitEntry(Building);
    if(Entry == NULL)
    {
        g_Context->Files.Assert << "UnitEntry not found: " << Building << endl;
        return;
    }
    ControlGroupInfo &SupportGroup = g_Context->Managers.ControlGroup.FindControlGroup(ControlGroupBuildingSupport);
    if(Entry->Requirement != "None")
    {
        const UINT PrerequisiteCount = SupportGroup.CountPrerequisiteUnitsMatchingName(Entry->Requirement);
        if(PrerequisiteCount == 0)
        {
            return;
        }
    }
    if(Entry->BuiltAt != "None")
    {
        const UINT BuildAtCount = SupportGroup.CountPrerequisiteUnitsMatchingName(Entry->BuiltAt);
        if(BuildAtCount == 0)
        {
            return;
        }
    }
    if(Entry->Name == "Lair")
    {
        if(SupportGroup.CountUnitsMatchingName("Hive") > 0)
        {
            return;
        }
    }
    if(Entry->Name == "Spire")
    {
        if(SupportGroup.CountUnitsMatchingName("GreaterSpire") > 0)
        {
            return;
        }
    }
    const UINT BuildingCount = SupportGroup.CountUnitsMatchingName(Building);
    const UINT BuildingInProduction = g_Context->Managers.Knowledge.CountUnitsInProduction(Building);
    if(BuildingCount >= 1 || BuildingInProduction >= 1)
    {
        return;
    }
    _ActiveProductionGoals.PushEnd(Entry);
    BuildingAdded = true;
}

bool ProductionThread::AddBuildings()
{
    switch(g_Context->Managers.Knowledge.Strat())
    {
    case StratZergDebug:
        return AddBuildingsZergDebug();
    case StratZergRoaches:
        return AddBuildingsZergRoaches();
    case StratZergMutalisks:
        return AddBuildingsZergMutalisks();
    case StratZergZerglings:
        return AddBuildingsZergZerglings();
    case StratZergRoachesAndHydralisks:
        return AddBuildingsZergRoachesAndHydralisks();
    case StratProtossZealots:
        return AddBuildingsProtossZealots();
    case StratProtossStalkers:
        return AddBuildingsProtossStalkers();
    case StratProtossGroundHybrid:
        return AddBuildingsProtossGroundHybrid();
    default:
        return AddBuildingsZergDebug();
    }
}

bool ProductionThread::AddUnits()
{
    switch(g_Context->Managers.Knowledge.Strat())
    {
    case StratZergDebug:
        return AddUnitsZergDebug();
    case StratZergRoaches:
        return AddUnitsZergRoaches();
    case StratZergMutalisks:
        return AddUnitsZergMutalisks();
    case StratZergZerglings:
        return AddUnitsZergZerglings();
    case StratZergRoachesAndHydralisks:
        return AddUnitsZergRoachesAndHydralisks();
    case StratProtossZealots:
        return AddUnitsProtossZealots();
    case StratProtossStalkers:
        return AddUnitsProtossStalkers();
    case StratProtossGroundHybrid:
        return AddUnitsProtossGroundHybrid();
    default:
        return AddUnitsZergDebug();
    }
}

void ProductionThread::UpdateProductionGoals()
{
    _ActiveProductionGoals.FreeMemory();
    
    if(g_Context->Managers.ControlGroup.WorkerCount() < 7)
    {
        _ActiveProductionGoals.PushEnd(g_Context->Managers.Knowledge.WorkerUnit());
        _ActiveProductionGoals.PushEnd(g_Context->Managers.Knowledge.SupplyUnit());
        return;
    }

    switch(g_Context->Managers.Knowledge.MyRace())
    {
    case RaceZerg:
        UpdateProductionGoalsZerg();
        break;
    case RaceProtoss:
        UpdateProductionGoalsProtoss();
        break;
    }
}

void ProductionThread::UpdateProductionGoalsZerg()
{
    bool QueenAdded = AddQueens();
    if(!QueenAdded)
    {
        bool BuildingAdded = AddBuildings();
        if(!BuildingAdded)
        {
            bool UnitsAdded = AddUnits();
            bool SupplyAdded = AddSupply();
            bool WorkersAdded = AddWorkers();
        }
    }
}

void ProductionThread::UpdateProductionGoalsProtoss()
{
    bool BuildingAdded = AddBuildings();
    bool SupplyAdded = AddSupply();
    if(!BuildingAdded)
    {
        bool WorkersAdded = AddWorkers();
        bool UnitsAdded = AddUnits();
    }
}

String ProductionThread::DescribeProductionGoals()
{
    String Result = "Goals: ";
    for(UINT Index = 0; Index < _ActiveProductionGoals.Length(); Index++)
    {
        Result += _ActiveProductionGoals[Index]->Name;
        if(Index != _ActiveProductionGoals.Length() - 1)
        {
            Result.PushEnd(',');
        }
    }
    return Result;
}

void ProductionThread::AddUnit(const String &Unit, UINT UnitCap, UINT SupplyRequirement, bool &UnitAdded)
{
    KnowledgeManager &Knowledge = g_Context->Managers.Knowledge;

    if(g_Context->Managers.FrameHUD.SupplyCurrent() < SupplyRequirement || UnitAdded)
    {
        return;
    }
    UnitEntry *Entry = g_Context->Managers.Database.GetUnitEntry(Unit);
    if(Entry == NULL)
    {
        g_Context->Files.Assert << "UnitEntry not found: " << Unit << endl;
        return;
    }
    if(g_Context->Managers.FrameHUD.Gas() < int(Entry->GasCost))
    {
        return;
    }
    ControlGroupInfo &SupportGroup = g_Context->Managers.ControlGroup.FindControlGroup(ControlGroupBuildingSupport);
    ControlGroupInfo &UnitGroup = g_Context->Managers.ControlGroup.FindControlGroup(*Entry);
    if(Entry->Requirement != "None")
    {
        const UINT PrerequisiteCount = SupportGroup.CountPrerequisiteUnitsMatchingName(Entry->Requirement);
        if(PrerequisiteCount == 0)
        {
            return;
        }
    }
    if(Entry->BuiltAt != "None")
    {
        UnitEntry *BuildAtEntry = g_Context->Managers.Database.GetUnitEntry(Entry->BuiltAt);
        if(BuildAtEntry != NULL)
        {
            ControlGroupInfo &BuildAtUnitGroup = g_Context->Managers.ControlGroup.FindControlGroup(*BuildAtEntry);
            const UINT BuildAtCount = BuildAtUnitGroup.CountPrerequisiteUnitsMatchingName(Entry->BuiltAt);
            if(BuildAtCount == 0)
            {
                return;
            }
        }
    }
    const UINT UnitCount = UnitGroup.CountUnitsMatchingName(Unit);
    const UINT UnitInProduction = g_Context->Managers.Knowledge.CountUnitsInProduction(Unit);
    if(UnitCount + UnitInProduction >= UnitCap)
    {
        return;
    }
    if(Entry->BuildClass == BuildClassQueue)
    {
        //
        // This is flawed; units in production != available production structures
        //
        int ProductionBuildingCount = SupportGroup.CountPrerequisiteUnitsMatchingName(Entry->BuiltAt);
        int UnitsInProduction = Knowledge.CountUnitsInProduction(Entry->Name);
        if(ProductionBuildingCount - UnitsInProduction <= 0)
        {
            return;
        }
    }
    _ActiveProductionGoals.PushEnd(Entry);
    UnitAdded = true;
}

bool ProductionThread::AddQueens()
{
    if(g_Context->Managers.Knowledge.MyRace() != RaceZerg)
    {
        return false;
    }
    ControlGroupManager &ControlGroup = g_Context->Managers.ControlGroup;
    UINT TotalBaseCount = ControlGroup.FindControlGroup(ControlGroupBuildingMain).LastObservedBaseUnits.Length();
    UINT TotalQueenCount = ControlGroup.FindControlGroup(ControlGroupBuildingSupport).CountUnitsMatchingName("Queen");
    UINT TotalSpawningPoolCount = ControlGroup.FindControlGroup(ControlGroupBuildingSupport).CountUnitsMatchingName("SpawningPool");
    if(TotalSpawningPoolCount >= 1 &&
        g_Context->Managers.FrameHUD.SupplyCurrent() >= 16 &&
        g_Context->Managers.Knowledge.CountUnitsInProduction("Queen") == 0 &&
        TotalQueenCount < TotalBaseCount)
    {
        _ActiveProductionGoals.PushEnd(g_Context->Managers.Database.GetUnitEntry("Queen"));
        return true;
    }
    return false;
}

bool ProductionThread::AddSupply()
{
    RaceType MyRace = g_Context->Managers.Knowledge.MyRace();

    UINT SupplyCurrent = g_Context->Managers.FrameHUD.SupplyCurrent();
    UINT SupplyCap = g_Context->Managers.FrameHUD.SupplyCap();
    UINT SupplyForesight = SupplyForesightFromSupplyCap(SupplyCap);
    UINT SupplyProductionCap = SupplyProductionCapFromSupplyCap(SupplyCap);
    if(SupplyCurrent + SupplyForesight >= SupplyCap)
    {
        UINT SupplyInProduction = g_Context->Managers.Knowledge.CountUnitsInProduction(g_Context->Managers.Knowledge.SupplyUnit()->Name);
        if(SupplyInProduction < SupplyProductionCap)
        {
            _ActiveProductionGoals.PushEnd(g_Context->Managers.Knowledge.SupplyUnit());
            return true;
        }
    }
    return false;
}

bool ProductionThread::AddWorkers()
{
    UINT WorkerCap = Math::Min(UINT(50), g_Context->Managers.Knowledge.MyBaseCount() * 24);
    if(g_Context->Managers.ControlGroup.WorkerCount() < WorkerCap)
    {
        _ActiveProductionGoals.PushEnd(g_Context->Managers.Knowledge.WorkerUnit());
        return true;
    }
    return false;
}

void ProductionThread::ProgramSucceeded()
{
    _WakeUpTime = GameTime() + 0.2f;
}

void ProductionThread::ProgramFailed()
{
    _WakeUpTime = GameTime() + 1.0f;
}