/*
 * Licensed under BSD license.  See LICENCE.TXT  
 *
 * Produced by:	Jeff Lait
 *
 *      	7DRL Development
 *
 * NAME:        general.cpp ( 1812 Hunt Library, C++ )
 *
 * COMMENTS:
 *	Defines AI strategy!
 */

#include "general.h"
#include "map.h"

using MARKER = OVERMAP::MARKER;

void
GENERAL::assignWaypoint(MAP *map, int side, int unit, int x, int y)
{
    myWaypoints[unit].x() = x;
    myWaypoints[unit].y() = y;

    map->issueOrder(nullptr, side, ORDER_MARCH, unit, x, y);
    myWaypointTimes[unit] = map->getTime();
}

void
GENERAL::assignWaypointIfNeeded(MAP *map, int side, int unit, int x, int y)
{
    if (x == myWaypoints[unit].x() && y == myWaypoints[unit].y())
	return;

    assignWaypoint(map, side, unit, x, y);
}

bool
GENERAL::atWaypoint(int side, MAP *map, int unit) const
{
    MARKER cur = map->getOvermap()->findMarker(side, side, unit);

    if (cur.x() == myWaypoints[unit].x() &&
	cur.y() == myWaypoints[unit].y())
    {
	return true;
    }
    return false;
}

bool
GENERAL::unitDead(int side, MAP *map, int unit, int team) const
{
    if (map->findCommander(team, unit))
	return false;
    return true;
}

void
GENERAL::resendWaypointOrders(int side, MAP *map)
{
    int time = map->getTime();
    const int RESEND_TIME = 60 * 10;		// Resend every 10 minutes
    for (int unit = 0; unit < 10; unit++)
    {
	// If zero time, never sent
	if (!myWaypointTimes[unit])
	    continue;

	// Check time since last waypoint.
	if (time - myWaypointTimes[unit] > RESEND_TIME)
	{
	    map->issueOrder(nullptr, side, ORDER_MARCH, unit, myWaypoints[unit].x(), myWaypoints[unit].y());
	    myWaypointTimes[unit] = time;
	}
    }
}


void
GENERAL::updateDwellAtWaypoints(int side, MAP *map)
{
    for (int unit = 0; unit < 10; unit++)
    {
	if (atWaypoint(side, map, unit))
	{
	    myDwellAtWaypoint[unit]++;
	}
	else
	{
	    myDwellAtWaypoint[unit] = 0;
	}
    }
}

// Note it takes 60 turns likely to *reach* the center of a way point
// from when a troop has hit the edge.
bool
GENERAL::marchFormationTo(int side, MAP *map, int goalx, int goaly, int stride, int dwell)
{
    OVERMAP *overmap = map->getOvermap();
    // Check if everyone is at their waypoint.
    for (int unit = 0; unit < 10; unit++)
    {
	// See if dead.
	if (unitDead(side, map, unit, side))
	    continue;
	if (!atWaypoint(side, map, unit))
	{
	    return false;
	}

	// If we haven't dwelled long enough.
	if (myDwellAtWaypoint[unit] < dwell)
	    return false;
    }

    MARKER self = overmap->findMarker(side, side, 0);
    int		dx = goalx - (self.x() - myFormation[0].x());
    int		dy = goaly - (self.y() - myFormation[0].y());

    if (!dx && !dy)
	return true;

    // Clamp to our stride.
    dx = SIGN(dx) * (MIN(ABS(dx), stride));
    dy = SIGN(dy) * (MIN(ABS(dy), stride));

    for (int unit = 0; unit < 10; unit++)
    {
	MARKER cur = overmap->findMarker(side, side, unit);
	// Charge to the opposing general's tent!
	// But keep our formation intact!
	assignWaypoint(map, side, unit, 
		self.x() + dx + myFormation[unit].x() - myFormation[0].x(),
		self.y() + dy + myFormation[unit].y() - myFormation[0].y());
    }

    return false;
}

bool
GENERAL::marchUnitTo(int side, MAP *map, 
			int unit,
			int goalx, int goaly, int stride, int dwell)
{
    OVERMAP *overmap = map->getOvermap();
    // Check if everyone is at their waypoint.
    // See if dead.
    if (unitDead(side, map, unit, side))
	return false;
    if (!atWaypoint(side, map, unit))
    {
	return false;
    }

    // If we haven't dwelled long enough.
    if (myDwellAtWaypoint[unit] < dwell)
	return false;

    MARKER self = overmap->findMarker(side, side, 0);
    int		dx = goalx - self.x();
    int		dy = goaly - self.y();

    if (!dx && !dy)
	return true;

    // Clamp to our stride.
    dx = SIGN(dx) * (MIN(ABS(dx), stride));
    dy = SIGN(dy) * (MIN(ABS(dy), stride));

    MARKER cur = overmap->findMarker(side, side, unit);
    // Charge to the opposing general's tent!
    // But keep our formation intact!
    assignWaypoint(map, side, unit, 
	    self.x() + dx + myFormation[unit].x(),
	    self.y() + dy + myFormation[unit].y());

    return false;
}

void
GENERAL::runStrategy(int side, MAP *map)
{
    int		foe = 0;
    bool	firstrun = false;
    OVERMAP 	*overmap = map->getOvermap();

    updateDwellAtWaypoints(side, map);

    if (myOrderDelay > 0)
    {
	resendWaypointOrders(side, map);
	myOrderDelay--;
	return;
    }
    // Re-evaluate.
    myOrderDelay = 120;

    int		forwardx = -1;
    if (side)
	forwardx = 1;

    // Last day, so I'm just going to hardwire formations here!
    // We assume orange, so canonical star ti
    // 7 4 1
    // 8 5 2 0
    // 9 6 3

    if (myStrategy == STRATEGY_NONE)
    {
	myStrategy = (STRATEGY_NAMES) (rand_choice(NUM_STRATEGYS - 1) + 1);
#if 0
	if (side)
	    myStrategy = STRATEGY_FLANK;
	else
	    myStrategy = STRATEGY_FLANK;
#endif
	firstrun = true;

	MARKER self = overmap->findMarker(side, side, 0);
	for (int unit = 0; unit < 10; unit++)
	{
	    MARKER cur = overmap->findMarker(side, side, unit);
	    myFormation[unit].x() = cur.x() - self.x();
	    myFormation[unit].y() = cur.y() - self.y();
	    myWaypoints[unit].x() = cur.x();
	    myWaypoints[unit].y() = cur.y();
	    myDwellAtWaypoint[unit] = 10000;
	}
    }

    if (!map->findCommander(side, 0))
    {
	// Oops, my head was cut off!
	return;
    }

    // Usually want to kill the other general.
    MARKER target = overmap->findMarker(side, !side, 0);
    switch (myStrategy)
    {
	case STRATEGY_CHARGE:
	{
	    MARKER self = overmap->findMarker(side, side, 0);
	    for (int unit = 0; unit < 10; unit++)
	    {
		MARKER cur = overmap->findMarker(side, side, unit);
		// Charge to the opposing general's tent!
		// But keep our formation intact!
		assignWaypointIfNeeded(map, side, unit, 
			target.x() + cur.x() - self.x(), 
			target.y() + cur.y() - self.y());
	    }
	    break;
	}
	case STRATEGY_HOLDCENTER:
	{
	    if (firstrun)
	    {
		// Adjust our formation
		// 7 4 1
		// 8 5 2 0
		// 9 6 3
		// to
		// 7 4   1  
		// 8 5 0 2
		// 9 6   3  
		myFormation[1] = IVEC2(-forwardx, forwardx);
		myFormation[2] = IVEC2(-forwardx, 0);
		myFormation[3] = IVEC2(-forwardx, -forwardx);
		myFormation[4] = IVEC2(forwardx, forwardx);
		myFormation[5] = IVEC2(forwardx, 0);
		myFormation[6] = IVEC2(forwardx, -forwardx);
		myFormation[7] = IVEC2(forwardx*2, forwardx);
		myFormation[8] = IVEC2(forwardx*2, 0);
		myFormation[9] = IVEC2(forwardx*2, -forwardx);
	    }
	    marchFormationTo(side, map,
			    overmap->width()/2,
			    overmap->height()/2,
			    4, 120);
	    // Turtle!
	    break;
	}

	case STRATEGY_PINCER:
	{
	    if (firstrun)
	    {
		// Adjust our formation
		// 7 4 1
		// 8 5 2 0
		// 9 6 3
		// to
		// 8 7
		//   4   
		//
		//
		//         1
		//       x 2 0
		//         3  
		//
		//
		//   6
		// 5 9
		myFormation[0] = IVEC2(- 2 *forwardx, 0);
		myFormation[1] = IVEC2(-forwardx, forwardx);
		myFormation[2] = IVEC2(-forwardx, 0);
		myFormation[3] = IVEC2(-forwardx, -forwardx);

		myFormation[4] = IVEC2(2*forwardx, 4*forwardx);
		myFormation[5] = IVEC2(3*forwardx, -5*forwardx);
		myFormation[6] = IVEC2(2*forwardx, -4*forwardx);
		myFormation[7] = IVEC2(2*forwardx, 5*forwardx);
		myFormation[8] = IVEC2(3*forwardx, 5*forwardx);
		myFormation[9] = IVEC2(2*forwardx, -5*forwardx);
	    }
	    marchFormationTo(side, map,
			    target.x(),
			    target.y(),
			    4, 120);
	    break;
	}
	case STRATEGY_LINE:
	{
	    if (firstrun)
	    {
		// Adjust our formation
		// 7 4 1
		// 8 5 2 0
		// 9 6 3
		// to  (Double y)
		// 7
		// 8
		// 4
		// 1
		// 2 x 0
		// 3
		// 6
		// 5
		// 9
		myFormation[0] = IVEC2(- forwardx, 0);
		myFormation[1] = IVEC2(forwardx, 2*forwardx);
		myFormation[2] = IVEC2(forwardx, 0);
		myFormation[3] = IVEC2(forwardx, -2*forwardx);

		myFormation[4] = IVEC2(forwardx, 4*forwardx);
		myFormation[5] = IVEC2(forwardx, -6*forwardx);
		myFormation[6] = IVEC2(forwardx, -4*forwardx);
		myFormation[7] = IVEC2(forwardx, 8*forwardx);
		myFormation[8] = IVEC2(forwardx, 6*forwardx);
		myFormation[9] = IVEC2(forwardx, -8*forwardx);
	    }
	    marchFormationTo(side, map,
			    target.x(),
			    target.y(),
			    4, 120);
	    break;
	}
	case STRATEGY_FLANK:
	{
	    if (firstrun)
	    {
		// Adjust our formation
		// 7 4 1
		// 8 5 2 0
		// 9 6 3
		// to  (Double x/y)
		// 7   0
		// 8 4
		// 9 5 1
		//   6 2
		// x   3
		if (rand_choice(2))
		{
		    // Flank up
		    myFormation[0] = IVEC2(- 4*forwardx, 8*forwardx);

		    myFormation[1] = IVEC2(- 4*forwardx, 4*forwardx);
		    myFormation[2] = IVEC2(- 4*forwardx, 2*forwardx);
		    myFormation[3] = IVEC2(- 4*forwardx, 0*forwardx);

		    myFormation[4] = IVEC2(-2*forwardx, 6*forwardx);
		    myFormation[5] = IVEC2(-2*forwardx, 4*forwardx);
		    myFormation[6] = IVEC2(-2*forwardx, 2*forwardx);

		    myFormation[7] = IVEC2(-0*forwardx, 8*forwardx);
		    myFormation[8] = IVEC2(-0*forwardx, 6*forwardx);
		    myFormation[9] = IVEC2(-0*forwardx, 4*forwardx);
		}
		else
		{
		    // x   1
		    //   4 2
		    // 7 5 3
		    // 8 6
		    // 9   0
		    myFormation[0] = IVEC2(- 4*forwardx, -8*forwardx);

		    myFormation[1] = IVEC2(- 4*forwardx, -0*forwardx);
		    myFormation[2] = IVEC2(- 4*forwardx, -2*forwardx);
		    myFormation[3] = IVEC2(- 4*forwardx, -4*forwardx);

		    myFormation[4] = IVEC2(-2*forwardx, -2*forwardx);
		    myFormation[5] = IVEC2(-2*forwardx, -4*forwardx);
		    myFormation[6] = IVEC2(-2*forwardx, -6*forwardx);

		    myFormation[7] = IVEC2(-0*forwardx, -4*forwardx);
		    myFormation[8] = IVEC2(-0*forwardx, -6*forwardx);
		    myFormation[9] = IVEC2(-0*forwardx, -8*forwardx);
		}
	    }
	    marchFormationTo(side, map,
			    target.x(),
			    target.y(),
			    4, 120);
	    break;
	}

	case STRATEGY_RESERVE:
	{
	    if (firstrun)
	    {
		// Adjust our formation
		// 7 4 1
		// 8 5 2 0
		// 9 6 3
		// to
		// 7
		//       4 1  
		// 8     5 2 0
		//       6 3  
		// 9
		myFormation[1] = IVEC2(forwardx, forwardx);
		myFormation[2] = IVEC2(forwardx, 0);
		myFormation[3] = IVEC2(forwardx, -forwardx);
		myFormation[4] = IVEC2(2*forwardx, forwardx);
		myFormation[5] = IVEC2(2*forwardx, 0);
		myFormation[6] = IVEC2(2*forwardx, -forwardx);
		myFormation[7] = IVEC2(forwardx*5, forwardx*2);
		myFormation[8] = IVEC2(forwardx*5, 0);
		myFormation[9] = IVEC2(forwardx*5, -forwardx*2);
	    }
	    marchFormationTo(side, map,
			    target.x(),
			    target.y(),
			    4, 120);
	    break;
	}
	case STRATEGY_PAIR:
	{
	    // Looks like charge, but reacts differently to
	    // enemy movement.
	    if (firstrun)
	    {
		for (int i = 0; i < 10; i ++)
		    myFormation[i] = IVEC2(0, 0);
	    }
	    for (int i = 0; i < 10; i ++)
	    {
		// We have to reverse our order as we are reversed!
		const int pairs[10] = 
		{
		    0,
		    3, 2, 1,
		    6, 5, 4,
		    9, 8, 7
		};
		MARKER pair = overmap->findMarker(side, !side, pairs[i]);
		if (unitDead(side, map, pairs[i], !side))
		{
		    pair = target;
		}
		marchUnitTo(side, map,
			    i,
			    pair.x(),
			    pair.y(),
			    4, 120);
	    }
	    break;
	}
    }
}
