#include "gen_bot_header.h"

// ==================================================================
// Code of CSPBot - Begin
// ==================================================================

void CSPBot::Spawn()
{
	CBot::Spawn();

	// Give the bot his special settings
	if (m_iStartPL >= 1)
	{
		SetMaxPowerLevel(m_iStartPL);
		SetPowerLevel(m_iStartPL);
	}
	if (m_iBotMaxHealth >= 1)
	{
		pev->max_health = pev->health = m_iBotMaxHealth;
		SetMaxHealth(m_iBotMaxHealth);
	}

	m_bRespawning = false;
}

void CSPBot::BotThink()
{
	CBot::BotThink();
}

void CSPBot::BotKilled(entvars_t *pAttacker)
{
	// Update the m_pKiller variabele
	CBot::BotKilled(pAttacker);

	m_iLives--;
	if (m_iLives == 0)
	{
		if (m_pKiller)
			m_pKiller->IncreasePL(m_iExtraPLGainForKiller);

		// Trigger target on death
		if (m_szDeathTrigger && *m_szDeathTrigger)
		{
			CBaseEntity *pTarget = UTIL_FindEntityByTargetname(NULL, m_szDeathTrigger);
			if (pTarget)
				pTarget->Use(this, this, USE_TOGGLE, 0.0f);
		}

		// Bot has no lifes anymore, remove it
		BotManager.KickBot(STRING(pev->netname));
		return;
	}
}

CSPBot *CSPBot::CreateMe(byte Team, byte Class, int StartPL, int StartForm, int MaxHealth,
						 int MaxSpeed, const char *PersoFile, int Lifes,
						 int ExtraPLGainForKiller, bool Chat, const char *ChatFile,
						 const char *DeathTrigger, const char *Name, byte Skill,
						 CBaseEntity *pSpawnPoint)
{

	/* TODO
	- PersoFile support
	*/

	edict_t *BotEnt;
	char c_name[BOT_NAME_LEN+1];
	int i, j, length;

	if ((Name != NULL) && (*Name != 0))
    {
		strncpy(c_name, Name, BOT_NAME_LEN-1);
		c_name[BOT_NAME_LEN] = 0;  // make sure c_name is null terminated
    }
	else
	{
		if (BotManager.m_sBotNameCount > 0)
			BotManager.PickBotName(c_name);
		else
			strcpy(c_name, "Bot");
    }

	if ((Skill < 1) || (Skill > 5))
		Skill = CBotManager::sv_bot_skill.value + 1;

    length = strlen(c_name);

	// remove any illegal characters from name...
    for (i = 0; i < length; i++)
    {
		if ((c_name[i] < ' ') || (c_name[i] > '~') || (c_name[i] == '"'))
		{
			for (j = i; j < length; j++)  // shuffle chars left (and null)
				c_name[j] = c_name[j+1];
			length--;
		}               
    }

    BotEnt = (*g_engfuncs.pfnCreateFakeClient)( c_name );

    if (FNullEnt( BotEnt ))
    {
		cout << "Can't create bot!\n";
	}
    else
    {
		char ptr[128];  // allocate space for message from ClientConnect
		
		ALERT(at_console, "Creating bot...\n");

		FREE_PRIVATE(BotEnt); // IMPORTANT: Fix the kick bot problem 
		BotEnt->pvPrivateData = NULL;

		CSPBot *pBot = GetClassPtr( (CSPBot *) VARS(BotEnt) );
		if (!pBot)
		{
			ALERT(at_console, "Error ! Couldn't create bot\n");
			return NULL;
		}

		pBot->m_pSpawnPoint = pSpawnPoint;
		BotEnt->v.frags = 0;

		// Connect the bot to the server
		ClientConnect( BotEnt, c_name, "127.0.0.1", ptr );

		// Pieter van Dijk - use instead of DispatchSpawn() - Hip Hip Hurray!
		ClientPutInServer( BotEnt );

		pBot->m_iStartPL = StartPL;
		pBot->m_iStartingForm = StartForm;
		pBot->m_iBotMaxHealth = MaxHealth;
		pBot->m_iBotMaxSpeed = MaxSpeed;
		pBot->m_iLives = Lifes;
		pBot->m_iExtraPLGainForKiller = ExtraPLGainForKiller;
		pBot->m_bChat = Chat;

		if (ChatFile && (*ChatFile))
			strcpy(pBot->m_szChatFileToUse, ChatFile);
		else
			pBot->m_szChatFileToUse[0] = 0;

		if (DeathTrigger && (*DeathTrigger))
			strcpy(pBot->m_szDeathTrigger, DeathTrigger);
		else
			pBot->m_szDeathTrigger[0] = 0;

		pBot->CreateInit(Team, Class, Skill, NULL, -1, DB_UNASSIGNED);
		return pBot;
	}

	return NULL;
}

void CSPBot::CreateInit(short Team, short Class, short Skill, const char *szPerso, short SBotVal,
						EBotDBType BotDBType)
{
	pev->flags |= FL_FAKECLIENT;
    m_fCreateTime = gpGlobals->time;
    m_bNotStarted = true;  // hasn't joined game yet
	m_fStartUpDelay = gpGlobals->time + 0.15f;

	if (g_teamplay)
		m_eStartAction = START_TEAM_SELECT;
	else
		m_eStartAction = START_CLASS_SELECT;

	m_bNeedToInitialize = false;  // don't need to initialize yet

	m_pLastEnemy = NULL;
	m_pKiller = NULL;

    m_pBotSkill = &BotManager.m_BotSkills[Skill - 1];  // 0 based for array indexes
	m_bSkillNr = Skill - 1;

    m_sBotTeam = -1;
    m_sBotClass = -1;

	if ((Team == 1) || (Team == 2))
		m_sBotTeam = Team;

	if ((Class >= 1) && (Class <= 9))
		m_sBotClass = Class;

	if ((!szPerso) || (*szPerso == 0))
	{
		if (BotManager.m_szDefaultPersonality[0])
			szPerso = BotManager.m_szDefaultPersonality;
	}

	m_pPersonality = NULL;

	if ((szPerso) && (*szPerso) && (!FStrEq(szPerso, "random_perso")))
	{
		m_pPersonality = BotManager.FindPersoByName(szPerso);
	}

	if (!m_pPersonality)
		m_pPersonality = BotManager.GetRandPerso();

	if (!m_pPersonality)
		cout << "Error couldn't find personality!\n";

	m_lVisiblePlayers = m_lValidPlayers = 0;

	m_pCurrentBotTask = NULL;
	m_pMeleeComboInfoList = NULL;
	m_pBotCombo = NULL;

	if (SBotVal > 3)
		SBotVal = RANDOM_LONG(1, 3);
	m_sSBotType = SBotVal;

	if (SBotVal > 0)
		m_pSBotSettings = &BotManager.m_SBotSettings[SBotVal];

	m_bKicked = false;

	if (BotManager.IsDBMode())
	{
		if (BotDBType != DB_UNASSIGNED)
			m_eBotDBType = BotDBType;
		else
			BotDBManager.AssignBotRole(this);
	}
}


// ==================================================================
// Code of CSPBot - End
// ==================================================================

// ==================================================================
// Code of CBotTeacher - Begin
// ==================================================================

CBotTeacher *CBotTeacher::CreateMe(EBotTeacherType BotTeacherType, const char *ScriptFile,
								   const char *EndTrigger, CBaseEntity *pSpawnPoint)
{
	edict_t *BotEnt;
	char c_name[BOT_NAME_LEN+1];

	switch (BotTeacherType)
	{
	case BOTTEACHER_ROSHI:
		strcpy(c_name, "roshi");
		break;
	case BOTTEACHER_KINGKAI:
		strcpy(c_name, "king kai");
		break;
	default:
		strcpy(c_name, "teacher");
		break;
	}

    BotEnt = (*g_engfuncs.pfnCreateFakeClient)( c_name );

    if (FNullEnt( BotEnt ))
    {
		cout << "Can't create bot teacher!\n";
	}
    else
    {
		char ptr[128];  // allocate space for message from ClientConnect
		
		ALERT(at_console, "Creating bot teacher...\n");

		FREE_PRIVATE(BotEnt); // IMPORTANT: Fix the kick bot problem 
		BotEnt->pvPrivateData = NULL;

		CBotTeacher *pBot = GetClassPtr( (CBotTeacher *) VARS(BotEnt) );
		if (!pBot)
		{
			ALERT(at_console, "Error ! Couldn't create bot teacher\n");
			return NULL;
		}

		pBot->m_pSpawnPoint = pSpawnPoint;
		BotEnt->v.frags = 0;

		// Connect the bot to the server
		ClientConnect( BotEnt, c_name, "127.0.0.1", ptr );

		// Pieter van Dijk - use instead of DispatchSpawn() - Hip Hip Hurray!
		ClientPutInServer( BotEnt );

		if (ScriptFile && (*ScriptFile))
			strcpy(pBot->m_szScriptFile, ScriptFile);
		else
			pBot->m_szScriptFile[0] = 0;

		if (EndTrigger && (*EndTrigger))
			strcpy(pBot->m_szEndTrigger, EndTrigger);
		else
			pBot->m_szEndTrigger[0] = 0;

		pBot->CreateInit(BotTeacherType);
		return pBot;
	}

	return NULL;
}

void CBotTeacher::CreateInit(EBotTeacherType BotTeacherType)
{
	CSPBot::CreateInit(1, 1, 1, NULL, -1, DB_UNASSIGNED);
	m_eBotTeacherType = BotTeacherType;
	m_TaskList.DeleteAllNodes();
}

// Read script file and create a task list from it
void CBotTeacher::MakeTeacherTaskList()
{
	// First find out if we can actually read the script file...
	if (m_szScriptFile[0] == 0)
		return;

	FILE *pScriptFile = fopen(m_szScriptFile, "r");
	if (!pScriptFile)
		return;

	// File can be read, process it
	int ch;
	char cmd_line[256];
	int cmd_index;
	TLinkedList<char *> WordList; // Words in current line
	char *Word, *FirstWord;

	m_TaskList.DeleteAllNodes();

	while (pScriptFile != NULL)
	{
		WordList.DeleteAllNodes();

		cmd_index = 0;
		cmd_line[0] = 0;

		ch = fgetc(pScriptFile);

		// skip any leading blanks
		while ((ch == ' ') || (ch == '\t'))
			ch = fgetc(pScriptFile);

		while ((ch != EOF) && (ch != '\r') && (ch != '\n'))
   		{
			if (ch == '\t')  // convert tabs to spaces
				ch = ' ';

			cmd_line[cmd_index] = ch;

			ch = fgetc(pScriptFile);

			// skip multiple spaces in input file
			while ((cmd_line[cmd_index] == ' ') && (ch == ' '))
				ch = fgetc(pScriptFile);

		    cmd_index++;
   		}

		if (ch == '\r')  // is it a carriage return?
		{
		      ch = fgetc(pScriptFile);  // skip the linefeed
   		}

		// if reached end of file, then close it
		if (ch == EOF)
   		{
			fclose(pScriptFile);
			pScriptFile = NULL;
		}

		cmd_line[cmd_index] = 0;  // terminate the command line

		Word = FirstWord = strtok(cmd_line, " ");
		while (Word)
		{
			WordList.AddNode(Word);
			Word = strtok(cmd_line, " ");
		}

		if ((!FirstWord) || (FirstWord[0] == '#') || (FirstWord[0] == 0))
		    continue;  // skip if comment or blank line
	}
}

void CBotTeacher::KillCurrentTask()
{
	bool HadTask = (m_pCurrentBotTask != NULL);

	CBot::KillCurrentTask();

	if (!m_TaskList.Empty() && HadTask)
	{
		CBaseTeacherTask *pNewTask = m_TaskList.Pop();
		pNewTask->Activate();
		m_pCurrentBotTask = pNewTask;
	}
}

// ==================================================================
// Code of CBotTeacher - End
// ==================================================================