#include	"../bot/gen_bot_header.h"
#include	"monsters.h"
#include	"talkmonster.h"
#include	"schedule.h"
#include	"defaultai.h"
#include	"scripted.h"
#include	"weapons.h"
#include	"nodes.h"
#include    "animation.h"
#include	"../engine/studio.h"
#include	"classes.h"
#include	"playermelee.h"
#include	"buddy_tasks.h"
#include	"buddy_aura.h"
#include	"buddy.h"
#include	"buddy_classes.h"
#include	"buddy_weapons.h"
#include	"hltv.h"
#include	"candy.h"
#include	"decals.h"

// --==*************************************************************==--
//
// Template start
//
// --==*************************************************************==--
//CBuddyTask_KiBlastFire::CBuddyTask_KiBlastFire()
//{
//}
//
//CBuddyTask_KiBlastFire::~CBuddyTask_KiBlastFire()
//{
//}
//
//CBuddyTask *CBuddyTask_KiBlastFire::ManageThreat(CBuddy *pBuddy)
//{
//}
//
//CBuddyTask *CBuddyTask_KiBlastFire::TaskThink(CBuddy *pBuddy)
//{	
//}
//void *CBuddyTask_KiBlastFire::DebugPrint(CBuddy *pBuddy)
//{	
//}
// --==*************************************************************==--
//
// Template end
//
// --==*************************************************************==--

// --==*************************************************************==--
//
// Template start
//
// --==*************************************************************==--
void CBuddyTask_HomeThroughMap::AvoidNearFriends(CBuddy *pBuddy)
{
	m_fAvoidFriendsDelay = gpGlobals->time + 1.0f;
	bool Avoid = false;
	CBaseMonster *pFriend = NULL, *pEnt = NULL, *pNearestFriend = NULL;
	float flDist, flNearestDist = 9999.0f;

	while ((pEnt = (CBaseMonster *)UTIL_FindEntityInSphere(pEnt, pBuddy->pev->origin, 250.0f)) != NULL)
	{
		if (IsNull(pEnt))
			continue;

		// Skip our owner
		if (pEnt == pBuddy->m_pOwner)
			continue;

		pFriend = (CBaseMonster *)pEnt;

		// Is it a friend?
		if (pFriend->IsPlayer() || FStrEq(STRING(pFriend->pev->classname), "player_buddy"))
		{
			// Skip if other team
			if (g_teamplay && (pFriend->pev->team != pBuddy->pev->team))
				continue;
		}
		else
			continue;

		if (!pBuddy->FInViewCone(pFriend) || !pBuddy->FVisible(pFriend))
			continue;

		flDist = UTIL_Distance(pBuddy->pev->origin, pFriend->pev->origin);
		if (flDist < flNearestDist)
		{
			flNearestDist = flDist;
			pNearestFriend = pFriend;
		}
	}

	if (pNearestFriend)
	{
		// Randomly turn around from him
		Vector v_Face = WrapAngles(UTIL_VecToAngles(pNearestFriend->GetGunPosition() - pBuddy->GetGunPosition()));
		
		switch(RANDOM_LONG(0, 1))
		{
		case 0:
			// Turn randomly left
			v_Face.y = UTIL_WrapAngle(v_Face.y + RANDOM_LONG(40, 180));
			break;
		case 1:
			// Turn randomly right
			v_Face.y = UTIL_WrapAngle(v_Face.y - RANDOM_LONG(40, 180));
			break;
		}
		m_fDontAvoidWallTime = gpGlobals->time + 0.25f;
	}
}

void CBuddyTask_HomeThroughMap::CheckUpDown(CBuddy *pBuddy)
{
	if (!pBuddy->m_bFlying)
	{
		// Look up so the buddy will walk on slopes aswell(otherwise the buddy thinks the slope
		// is a wall
		m_vIdealLookAngles.x = 40.0;
		return;
	}

	bool direction_up = (GetCollisionDist(pBuddy, (dir_up | dir_forward), 100.0) < 100.0f);
	bool direction_down = (GetCollisionDist(pBuddy, (dir_down | dir_forward), 100.0) < 100.0f);

	m_fCheckUpDownTime = gpGlobals->time + 3.0;

	if ((direction_up) && (!direction_down))
	{
        m_vIdealLookAngles.x = RANDOM_FLOAT(-25.0, -35.0);  // look downwards
	}
	else if ((!direction_up) && (direction_down))
	{
        m_vIdealLookAngles.x = RANDOM_FLOAT(25.0, 35.0);  // look upwards
	}
    else if ((direction_up) && (direction_down))
	{
		m_vIdealLookAngles.x = 0; // look straight
	}
	else
	{
		int i = RANDOM_LONG(1, 3);
		if (i == 1)
            m_vIdealLookAngles.x = RANDOM_FLOAT(-25.0, -35.0);  // look downwards
		else if (i == 2)
            m_vIdealLookAngles.x = RANDOM_FLOAT(25.0, 35.0);  // look upwards
		else
            m_vIdealLookAngles.x = 0;  // look straight
	}
    return;
}

void CBuddyTask_HomeThroughMap::HomeThroughMap(CBuddy *pBuddy)
{
	// whenever homing we are flying -- it just makes sense!
	pBuddy->StartFly();

	if (m_fCheckUpDownTime <= gpGlobals->time)
		CheckUpDown(pBuddy);

	if (m_fAvoidFriendsDelay < gpGlobals->time)
		AvoidNearFriends(pBuddy);

	// check if we should be avoiding walls
	if (m_fDontAvoidWallTime <= gpGlobals->time)
	{
		if (m_fLookDelay < gpGlobals->time)
		{
			CheckFOV(pBuddy); // Check FOV
			m_fLookDelay = gpGlobals->time + RANDOM_FLOAT(0.8, 1.2);
		}

        CheckSkybox(pBuddy); // and after that look for and avoid skyboxes...
    }
	
	// TODO:: when do we use speed up and when do we not?? no one knows what the true answer to this is!
	// aka; fix!
	pBuddy->StartTurbo();

	pBuddy->SetMoveSpeed(pBuddy->GetMaxSpeed());
	pBuddy->pev->velocity = m_vIdealLookAngles.Normalize() * fabs(pBuddy->m_fMoveSpeed);
}

// Function: fire (to) many tracelines to determine what the best move direction is;
// The best direction is the spot whith the highest distance
void CBuddyTask_HomeThroughMap::CheckFOV(CBuddy *pBuddy)
{
    if (m_fDontAvoidWallTime > gpGlobals->time)
 	   return;

	TraceResult tr;

	Vector v_src = pBuddy->GetGunPosition();

	float angle;

    float distance = 0;
    float nearestdistance = 0;

    Vector DesiredVec = g_vecZero;
	float flMostLeftEndDist, flMostStraightEndDist, flMostRightEndDist;
	Vector vecForwardPlaneNormal;

    // scan 90 degrees of buddy's field of view...
	for (angle =-45; angle <=45; angle+=5) //-70 -- +70
	{
		v_src = WrapAngles(pBuddy->pev->v_angle); // restore bot's current v_angle
        v_src.y = UTIL_WrapAngle (v_src.y + angle); // pan it from left to right
		UTIL_MakeVectors(v_src);

        // trace line slightly under eyes level
        UTIL_TraceHull (pBuddy->pev->origin + pBuddy->pev->view_ofs,
                       (pBuddy->pev->origin + pBuddy->pev->view_ofs) + (gpGlobals->v_forward * 10000),
                        ignore_monsters, human_hull, pBuddy->edict(), &tr);

        distance = tr.flFraction * 10000; // store distance to obstacle

        // now take the most logical direction:
        // take the endVec with the largest distance
        if (distance > nearestdistance)
        {
            DesiredVec = tr.vecEndPos;
            nearestdistance = distance;
		}

		if (angle == -45)
			flMostLeftEndDist = distance;
		else if (angle == 0)
			flMostStraightEndDist = distance;
		else if (angle == 45)
			flMostRightEndDist = distance;
   }

   if (DesiredVec != g_vecZero)
   { 
	   m_vIdealLookAngles = DesiredVec;
   }

   // Now check for walls and other collisions...

   // first check if bot is in a corner
   if ((flMostLeftEndDist < 70) && (flMostRightEndDist < 70) &&
	   (flMostStraightEndDist < 70)) // he is..
   {
	   TraceResult tr;
	   Vector v_src, v_end;
/*
	   // check if we can jump up if when we're not flying and not under water...
	   if ((pev->gravity > 0.1) && (pev->waterlevel != 3) &&
		   (flMostStraightEndDist < 45.0) && (!(pev->flags & FL_ONGROUND)))
	   {
		   UTIL_MakeVectors(pev->v_angle);

		   v_src = pev->origin + Vector(0,0,200);
		   v_end = v_src + gpGlobals->v_forward * 80;

		   UTIL_TraceHull(v_src, v_end, ignore_monsters, human_hull, 
					  pev->pContainingEntity, &tr);

	       if (tr.flFraction >= 1.0)
		   {
			   pev->button |= IN_JUMP;
			   return;
		   }
	   }
*/
       TurnAround(pBuddy);
	   
	   m_fDontAvoidWallTime = gpGlobals->time + 1.0;

       m_fDontAvoidWallTime = gpGlobals->time + 1.0;
	   return;
   }

   // then check if he can't move forward...
   if (flMostStraightEndDist < 70) // something is blocking on eye height
   {
	   TraceResult tr;
	   Vector v_src, v_end;
/*
	   // check if the bot can jump when he's not flying and the bot is not in water
	   if ((pev->gravity > 0.1) && (pev->waterlevel != 3) &&
		   (!(pev->flags & FL_ONGROUND)))
	   {
		   UTIL_MakeVectors(pev->v_angle);

		   v_src = pev->origin + Vector(0,0,200);
		   v_end = v_src + gpGlobals->v_forward * 40;

		   UTIL_TraceHull(v_src, v_end, ignore_monsters, human_hull, 
					  pev->pContainingEntity, &tr);

	       if (tr.flFraction >= 1.0)
		   {
			   pev->button |= IN_JUMP;
			   return;
		   }
/* Disabled, maybe i work on it later
		   else // Otherwise try to walljump on it
		   {
			   MakeTask(pBot, TaskWallJump);
			   pev->button |= IN_JUMP;
			   return;
		   }
*//*
	   }
*/
	   TurnAtWall(&vecForwardPlaneNormal, pBuddy);

       m_fDontAvoidWallTime = gpGlobals->time + 1.0;
	   return;
   }

   return;
}

void CBuddyTask_HomeThroughMap::TurnAtWall(Vector *vecPlaneNormal, CBuddy *pBuddy)
{
	Vector Normal;
    float Y, Y1, Y2, D1, D2, Z;
 
    // Find the normal vector from the trace result.  The normal vector will
    // be a vector that is perpendicular to the surface from the TraceResult.

    Normal = UTIL_VecToAngles(*vecPlaneNormal);

    // Since the bot keeps it's view angle in -180 < x < 180 degrees format,
    // and since TraceResults are 0 < x < 360, we convert the bot's view
    // angle (yaw) to the same format at TraceResult.

    Y = pBuddy->pev->v_angle.y;
    Y = Y + 180;
    if (Y > 359) Y -= 360;

    // Turn the normal vector around 180 degrees (i.e. make it point towards
    // the wall not away from it.  That makes finding the angles that the
    // bot needs to turn a little easier.

    Normal.y = Normal.y - 180;
    if (Normal.y < 0)
		Normal.y += 360;

    // Here we compare the bots view angle (Y) to the Normal - 90 degrees (Y1)
    // and the Normal + 90 degrees (Y2).  These two angles (Y1 & Y2) represent
    // angles that are parallel to the wall surface, but heading in opposite
    // directions.  We want the bot to choose the one that will require the
    // least amount of turning (saves time) and have the bot head off in that
    // direction.

    Y1 = Normal.y - 90;
    if (RANDOM_LONG(1, 100) <= 50)
    {
		Y1 = Y1 - RANDOM_FLOAT(5.0, 20.0);
    }
    if (Y1 < 0) Y1 += 360;

    Y2 = Normal.y + 90;
    if (RANDOM_LONG(1, 100) <= 50)
	{
		Y2 = Y2 + RANDOM_FLOAT(5.0, 20.0);
    }
    if (Y2 > 359) Y2 -= 360;

    // D1 and D2 are the difference (in degrees) between the bot's current
    // angle and Y1 or Y2 (respectively).

    D1 = abs(Y - Y1);
    if (D1 > 179) D1 = abs(D1 - 360);
    D2 = abs(Y - Y2);
    if (D2 > 179) D2 = abs(D2 - 360);

    // If difference 1 (D1) is more than difference 2 (D2) then the bot will
    // have to turn LESS if it heads in direction Y1 otherwise, head in
    // direction Y2.  I know this seems backwards, but try some sample angles
    // out on some graph paper and go through these equations using a
    // calculator, you'll see what I mean.

    if (D1 > D2)
		Z = Y1;
    else
		Z = Y2;

    // convert from TraceResult 0 to 360 degree format back to bot's
    // -180 to 180 degree format.

    if (Z > 180)
		Z -= 360;

    // set the direction to head off into...
    m_vIdealLookAngles.y = UTIL_WrapAngle(Z);
}

void CBuddyTask_HomeThroughMap::TurnAround(CBuddy *pBuddy)
{
	Vector LookVec = pBuddy->pev->v_angle;
    LookVec.y = UTIL_WrapAngle(LookVec.y + 180.0f);
    m_vIdealLookAngles = LookVec;
}

void CBuddyTask_HomeThroughMap::CheckSkybox(CBuddy *pBuddy)
{
   if (m_fDontAvoidWallTime > gpGlobals->time)
	   return;

   Vector v_src = WrapAngles(pBuddy->pev->v_angle); // store bot's current v_angle;
   UTIL_MakeVectors(v_src);
   Vector vecEnd = pBuddy->pev->origin + pBuddy->pev->view_ofs + (gpGlobals->v_forward * 400);

   // Skybox infront?
   if (UTIL_PointContents(vecEnd) == CONTENTS_SKY)
   {
       TurnAround(pBuddy);
	   m_fDontAvoidWallTime = gpGlobals->time + 1.0;
   }

   return;
}

CBuddyTask_HomeThroughMap::CBuddyTask_HomeThroughMap()
{
	m_fLookDelay = 0; // so we look right away
	TaskState = tsCantSwitch;
}

CBuddyTask_HomeThroughMap::~CBuddyTask_HomeThroughMap()
{
}

CBuddyTask *CBuddyTask_HomeThroughMap::ManageThreat(CBuddy *pBuddy)
{
	return NULL;
}

CBuddyTask *CBuddyTask_HomeThroughMap::TaskThink(CBuddy *pBuddy)
{	
	this->HomeThroughMap(pBuddy);

	return NULL;
}

void CBuddyTask_HomeThroughMap::DebugPrint(CBuddy *pBuddy)
{
	PrintAI("Current Task: HomeThroughMap\n");
	PrintAI("Curr Pos: " << pBuddy->pev->velocity.x << "," << pBuddy->pev->velocity.y << "," << pBuddy->pev->velocity.z << "\n");
}
// --==*************************************************************==--
//
// Template end
//
// --==*************************************************************==--