#include "ProductionGoal.h"

#include "BuildingManager.h"
#include "The.h"

using namespace UAlbertaBot;

// An upgrade or research goal may already be done.
bool ProductionGoal::alreadyAchieved() const
{
    if (act.isTech())
    {
        return
            BWAPI::Broodwar->self()->hasResearched(act.getTechType()) ||
            BWAPI::Broodwar->self()->isResearching(act.getTechType());
    }

    if (act.isUpgrade())
    {
        int level = BWAPI::Broodwar->self()->getUpgradeLevel(act.getUpgradeType());
        int max = BWAPI::Broodwar->self()->getMaxUpgradeLevel(act.getUpgradeType());
        return
            level >= max ||
            level == max - 1 && BWAPI::Broodwar->self()->isUpgrading(act.getUpgradeType());
    }

    return false;
}

bool ProductionGoal::failure() const
{
	// The goal fails if no possible unit could become its parent,
	// including buildings not yet started by BuildingManager.

    return
        alreadyAchieved() ||
        !act.hasEventualProducer() && !BuildingManager::Instance().isBeingBuilt(act.whatBuilds());
}

ProductionGoal::ProductionGoal(const MacroAct & macroAct)
	: parent(nullptr)
	, attempted(false)
	, act(macroAct)
{
	UAB_ASSERT(act.isAddon() || act.isUpgrade() || act.isTech(), "unsupported goal");
    //BWAPI::Broodwar->printf("create goal %s", act.getName().c_str());
}

// Meant to be called once per frame to try to carry out the goal.
void ProductionGoal::update()
{
	// Clear any former parent that is lost.
	if (parent && !parent->exists())
	{
		parent = nullptr;
		attempted = false;
	}

	// Add a parent if necessary and possible.
	if (!parent)
	{
		std::vector<BWAPI::Unit> producers;
		act.getCandidateProducers(producers);
		if (!producers.empty())
		{
			parent = *producers.begin();		// we don't care which one
            //BWAPI::Broodwar->printf("%s has parent %s of %d",
            //    act.getName().c_str(), UnitTypeName(parent).c_str(), producers.size());
		}
	}

	// Achieve the goal if possible.
    if (parent && act.canProduce(parent))
    {
        //BWAPI::Broodwar->printf("attempt goal %s", act.getName().c_str());
        attempted = true;
        act.produce(parent);
    }
}

// Meant to be called once per frame to see if the goal is completed and can be dropped.
bool ProductionGoal::done()
{
    // TODO this check is approximate
	bool done = parent && (parent->getAddon() || parent->isUpgrading() || parent->isResearching());

	if (done)
	{
		if (attempted)
		{
			//BWAPI::Broodwar->printf("completed goal %s", act.getName().c_str());
		}
		else
		{
			// The goal was "completed" but not attempted. That means it hit a race condition:
			// It took the same parent as another goal which was attempted and succeeded.
			// The goal went bad and has to be retried with a new parent.
			//BWAPI::Broodwar->printf("retry goal %s", act.getName().c_str());
			parent = nullptr;
			done = false;
		}
	}
	// No need to check every frame whether the goal has failed.
	else if (BWAPI::Broodwar->getFrameCount() % 32 == 22 && failure())
	{
		//BWAPI::Broodwar->printf("failed goal %s", act.getName().c_str());
		done = true;
	}

	return done;
}
