//---------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "SDIMain.h"
#include "About.h"
#include "CharacterClass.h"
#include "CharacterUnit.h"
#include "SettingsUnit.h"
#include "PlayerUnit.h"
//---------------------------------------------------------------------
#pragma resource "*.dfm"
//---------------------------------------------------------------------
#define LETHAL_ATTACK 7
//---------------------------------------------------------------------

TBeTestWin *BeTestWin;
ID Character::cAutoNumber = 0;
CharacterNode CharListHead = NULL;
GroupNode     GroupListHead = NULL;
Character *Location = NULL;
bool gbSilentMode = false;
int step = 0;
bool ForceScans = true;
void UserTurn(Character *);

//---------------------------------------------------------------------
__fastcall TBeTestWin::TBeTestWin(TComponent *AOwner)
	: TForm(AOwner)
{
	if(InitActionLib((int(*)(void*))DefaultFn))
    {
//    	Stop("BE initialized OK");
    }
    else
    	Stop("BE wasn't loaded properly");
    SetDefaultMemberCheckFn((int(*)(void*))CheckMember);
}
//---------------------------------------------------------------------
void __fastcall TBeTestWin::ShowHint(TObject *Sender)
{
	StatusBar->SimpleText = Application->Hint;
}
//---------------------------------------------------------------------
void __fastcall TBeTestWin::ExitItemClick(TObject *Sender)
{
	Close();
}
//---------------------------------------------------------------------
void __fastcall TBeTestWin::OpenItemClick(TObject *Sender)
{

	if(OpenDialog->Execute())
    {
        if(SettingsForm->ClearOnLoadChk->Checked)
            ClearBtnClick(NULL);
        RestoreAllCharacters( OpenDialog->FileName.c_str() );
        ForceScans = false;
        lstPrint("(Events counter reset to 0)");
    	//MsgList->Items->LoadFromFile( OpenDialog->FileName );
    }
    Update();

}
//---------------------------------------------------------------------
void __fastcall TBeTestWin::SaveItemClick(TObject *Sender)
{
	if(BBESave->Execute())
    {
    	if ( strchr(BBESave->FileName.c_str(), '.' ) )
            SaveAllCharacters(BBESave->FileName.c_str());
        else
        {
        	strcat( BBESave->FileName.c_str(), ".bbe");
            SaveAllCharacters(BBESave->FileName.c_str());
        }
    }
    Update();
}
//---------------------------------------------------------------------
void __fastcall TBeTestWin::About1Click(TObject *Sender)
{
	AboutBox->ShowModal();
}
//--------------------------------------------------------------------- 
void __fastcall TBeTestWin::FormCreate(TObject *Sender)
{
	Application->OnHint = ShowHint;
}
//---------------------------------------------------------------------
void lstPrint(char *argMsg)
{
    if(gbSilentMode)
        return;
    BeTestWin->MsgList->Items->Add(argMsg);
    BeTestWin->MsgList->TopIndex = BeTestWin->MsgList->Items->Count - 1;
    BeTestWin->MsgList->Update();
}

//---------------------------------------------------------------------------
void __fastcall TBeTestWin::ClearBtnClick(TObject *Sender)
{
	MsgList->Clear();
}

//---------------------------------------------------------------------------
void __fastcall TBeTestWin::CharactersItemClick(TObject *Sender)
{
	CharacterForm->ShowModal();
    step = 0;
    ForceScans = true;
}

//---------------------------------------------------------------------------
void __fastcall TBeTestWin::LocationItemClick(TObject *Sender)
{
	CharacterForm->Caption = "Location Sheet";
    CharacterForm->ItemBtn->Visible = false;
	CharacterForm->ShowModal();
    CharacterForm->ItemBtn->Visible = true;
	CharacterForm->Caption = "Character Sheet";
    ForceScans = true;
}

//---------------------------------------------------------------------------
/*		TBeTestWin::EnvironmentChange()
	scan everybody by everybody - happens when new characters arrive
    or old ones depart
*/
void TBeTestWin::EnvironmentChange()
{
    CharacterNode cp,incp;
    ID ItemsArrayLen;
    ItemPtrArr Inventory = NULL;
    char szOutBuf[200];
    bool GoalAssigned;
    UpdateAssumptionPopulation();

    //don't worry about self-scan  -  it is disabled
    for(cp = CharListHead; cp != NULL ; cp = cp->next )
    {
        if(cp->ch.BEStuff.IsDead())
            continue;
        GoalAssigned = false;
        cp->ch.BEStuff.ResetFromOriginal();
        for(incp = CharListHead; (incp != NULL) && !GoalAssigned; incp = incp->next )
        {
            if((incp->ch.CharID != cp->ch.CharID) && !incp->ch.BEStuff.IsDead())
            {
                ItemsArrayLen = incp->ch.InvListToArray(&Inventory);
                if( cp->ch.BEStuff.Scan(
                            &(incp->ch.BEStuff),Inventory,
                            ItemsArrayLen, GOAL_NONE))
                {
                    GoalAssigned = true;
                }
                if((!SettingsForm->NoVerboseOutput->Checked || GoalAssigned)
                        && cp->ch.IsActive)
                {
                    sprintf(szOutBuf,"Looking at %s, %s thinks:",incp->ch.szName,
                        cp->ch.szName);
                    lstPrint(szOutBuf);
                    sprintf(szOutBuf,"\"%s\"",GetLastThought());
                    lstPrint(szOutBuf);
                }
                if(Inventory != NULL)
                {
                    delete Inventory;
                    Inventory = NULL;
                }
            }
        }
    }
}

//---------------------------------------------------------------------------
void TBeTestWin::UpdateAssumptionPopulation()
{
    Agent **People;
    ID PeopleNo,i = 0;
    ItemPtrArr* Inventories;
    CharacterNode cp;
    ID *ItemArraysLen;

    PeopleNo = MakePopulationArray(&People);
    if(PeopleNo == 0)
        return;

    Inventories = new ItemPtrArr[PeopleNo];
    ItemArraysLen = new unsigned short[PeopleNo];
    for(cp = CharListHead; cp != NULL ; cp = cp->next )
    {
        Inventories[i] = NULL;
        ItemArraysLen[i] = cp->ch.InvListToArray(&(Inventories[i]));
        i++;
    }

    SetDynamicTACAssumptionPopulation(People,PeopleNo,Inventories,
		    ItemArraysLen);

    //clean up all the mess
    for(i = 0; i < PeopleNo ; i++ )
    {
        delete []Inventories[i];
    }
    delete []Inventories;
    delete []ItemArraysLen;
    delete []People;
}

//---------------------------------------------------------------------------
void TBeTestWin::NextEvent()
{
	CharacterNode cpi; //, cpj;
    GroupNode     gpi;
    Character   *CurActObj;
    Action *pNextA;
    ActionDefNodeP pActDef;
    Agent **People = NULL; //, *bsCurrentLocation;
    unsigned short *ItemArraysLen,i = 0;
    ID PeopleNo;
    ItemPtrArr *Inventories;
    char szOutBuf[200],szActionName[21],szObjName[30];
    InteractionParam *act_parm;

    act_parm = new InteractionParam;
    PeopleNo = MakePopulationArray(&People);
    Inventories = new ItemPtrArr[PeopleNo];
    ItemArraysLen = new unsigned short[PeopleNo];
    for(cpi = CharListHead; cpi != NULL ; cpi = cpi->next )
    {
        Inventories[i] = NULL;
        ItemArraysLen[i] = cpi->ch.InvListToArray(&(Inventories[i]));
        i++;
    }

/*
    if(Location != NULL)
        bsCurrentLocation = &(Location->BEStuff.GetCore());
    else
        bsCurrentLocation = NULL;
*/
    for(cpi = CharListHead; cpi != NULL ; cpi = cpi->next )
    {
        szOutBuf[0] = '\0';
        if(cpi == NULL)
            continue;
        if(cpi->ch.BEStuff.IsDead() == TRUE)
        //"body bags" will be emptied later
            continue;

        if(cpi->ch.IsActive == FALSE)
        {
            sprintf(szOutBuf,"%s is unconscious",cpi->ch.szName);
            lstPrint(szOutBuf);
            cpi->ch.IsActive = TRUE;
            //let them recover after one round
            continue;
        }

        if(cpi->ch.IsPlayerCharacter)
        {
            UserTurn(&(cpi->ch));
        }
        pNextA = cpi->ch.BEStuff.CurrentStrategy.GetNextAction();
        strcpy(szObjName,"action objective");
        if(pNextA)
        {
            act_parm->Initiator = &(cpi->ch);
            act_parm->ToBeExecuted = pNextA;
            act_parm->Squad = NULL;
            pActDef = GetActionNode(pNextA->GetID());
            if(pActDef != NULL)
                strncpy(szActionName,pActDef->Verb,MAX_ACT_NAME_LEN);
            else
                strcpy(szActionName,"");
            if(pNextA->GetObjective())
            {
                CurActObj = GetCharacter(pNextA->GetObjective()->attId);
                if(CurActObj != NULL)
                    strcpy(szObjName,CurActObj->szName);
            }
        }
        else
        {
            act_parm->Initiator = NULL;
            act_parm->ToBeExecuted = NULL;
        }

    	switch(cpi->ch.BEStuff.ExecuteNextAction(act_parm,People,PeopleNo,
            Inventories,
            ItemArraysLen))
            //bsCurrentLocation))
        {
            case ACTION_SUCCESS:    case ACTION_FAILURE:
                if(!SettingsForm->NoVerboseOutput->Checked)
                {
                    sprintf(szOutBuf,"(%s accomplished %s)",
                            cpi->ch.szName,szActionName);
                }
                cpi->ch.BEStuff.UpdateOriginalCore();
                //everything else is done through HandleExecution()
                //and functions being called from Behavior Engine
                break;
            case ACTION_NOT_PRIMED:
//                strncpy(szOutBuf,GetLastThought(),200);
//                if(szOutBuf[0] == '\0')
                if(*GetLastThought())
                    sprintf(szOutBuf,"%s thinks: \"%s\"",
                        cpi->ch.szName,GetLastThought());
                else
                    if(!SettingsForm->NoVerboseOutput->Checked)
                        sprintf(szOutBuf,"%s has no action to execute",
                            cpi->ch.szName);
                    else
                        szOutBuf[0] = '\0';
                break;
            case LO_WICK:
                sprintf(szOutBuf,"%s decided that action %s is too evil",cpi->ch.szName,szActionName);
                break;
            case HI_WICK:
                sprintf(szOutBuf,"%s decided that s/he is too evil to %s",cpi->ch.szName,szActionName);
                break;
            case LO_ANA:
                sprintf(szOutBuf,"%s decided that s/he is too chaotic to %s",cpi->ch.szName,szActionName);
                break;
            case HI_ANA:
                sprintf(szOutBuf,"%s decided that action %s is too chaotic",cpi->ch.szName,szActionName);
                break;
            case LO_INT:
                sprintf(szOutBuf,"%s decided that action %s is too stupid to be executed",
                    cpi->ch.szName,szActionName);
                break;
            case HI_INT:
                sprintf(szOutBuf,"%s decided that action %s is too smart to be executed",
                    cpi->ch.szName,szActionName);
                break;
            case TOO_FOREIGN:
                sprintf(szOutBuf,"%s decided that %s is too alien to %s",cpi->ch.szName,szObjName,szActionName);
                break;
            case NOT_INTERESTED_IN_DEAL:
                sprintf(szOutBuf,"%s decided that %s is too wealthy to be interested in deal",cpi->ch.szName,szObjName);
                break;
            case LO_COU:
                if(SettingsForm->SoloActMode->Checked)
                    sprintf(szOutBuf,"%s decided that %s is too scary to %s",
                        cpi->ch.szName,szObjName,szActionName);
                else
                    sprintf(szOutBuf,"%s decided that in current environment %s is too dangerous to %s",
                        cpi->ch.szName,szObjName,szActionName);
                break;
            case BAD_COMBAT_ODDS:
                if(SettingsForm->SoloActMode->Checked)
                    sprintf(szOutBuf,"%s decided that %s is too tough to %s",
                        cpi->ch.szName,szObjName,szActionName);
                else
                    sprintf(szOutBuf,"%s decided that in current environment it is too dangerous to %s %s",
                        cpi->ch.szName,szActionName,szObjName);
                break;
            case WOULD_NOT_BELIEVE:
                sprintf(szOutBuf,"%s decided that %s would not believe the statement",cpi->ch.szName,szObjName);
                break;
            case LO_ATT:
                sprintf(szOutBuf,"%s's attitude to %s is too low to %s the latter",
                    cpi->ch.szName,szObjName,szActionName);
                break;
            case HI_ATT:
                sprintf(szOutBuf,"%s's attitude to %s is too high to %s the latter",
                    cpi->ch.szName,szObjName,szActionName);
                break;
            case NO_CANDIDATES:
                sprintf(szOutBuf,"%s was unable to find candidates for %s",cpi->ch.szName,szActionName);
                break;
            case PAST_FAILURE:
                sprintf(szOutBuf,"In the last moment %s recalls past failed attempt to %s",cpi->ch.szName,szActionName);
                break;
            case STRATEGY_ABORTED:
                sprintf(szOutBuf,"%s thinks the strategy cannot be completed and decides to abort it.",
                        cpi->ch.szName);
            default:
                break;
        }
        if(szOutBuf[0])
            lstPrint(szOutBuf);
        if(*GetVerbalOutput())
        {
            sprintf(szOutBuf,"%s says: \"%s\"",cpi->ch.szName,GetVerbalOutput());
            lstPrint(szOutBuf);
        }

    }

    //group / squad strategies support
    for(gpi = GroupListHead; gpi != NULL ; gpi = gpi->next )
    {
        szOutBuf[0] = '\0';
        if(gpi->g.ActivityMode() == FALSE)
        //the squad is inactive
            continue;
        strcpy(szObjName,"action objective");
        act_parm->Squad = gpi;
        pNextA = gpi->g.CurrentStrategy.GetNextAction();
        if(pNextA)
        {
            act_parm->Initiator = GetCharacter((ID)
                gpi->g.GetLeaderCoreAddress()->attId);
            act_parm->ToBeExecuted = pNextA;
            pActDef = GetActionNode(pNextA->GetID());
            if(pActDef != NULL)
                if(gpi->g.ActivityMode() == MEMBERS_SELECT)
                    strncpy(szActionName,"members selection",
                        MAX_ACT_NAME_LEN);
                else
                    strncpy(szActionName,pActDef->Verb,MAX_ACT_NAME_LEN);
            else
                strcpy(szActionName,"");
            if(pNextA->GetObjective())
            {
                CurActObj = GetCharacter(pNextA->GetObjective()->attId);
                if(CurActObj)
                    strcpy(szObjName,CurActObj->szName);
            }
        }
        else
        {
            act_parm->Initiator = NULL;
            act_parm->ToBeExecuted = NULL;
        }

        szOutBuf[0] = '\0';
        //GROUP support
    	switch(gpi->g.ExecuteNextAction(act_parm,People,PeopleNo,
            Inventories,
            ItemArraysLen))
            //bsCurrentLocation))
        {
            case ACTION_SUCCESS: case ACTION_FAILURE:
                if(!SettingsForm->NoVerboseOutput->Checked)
                {
                    sprintf(szOutBuf,"(%s accomplished %s)",
                        gpi->szName,szActionName);
                }
                gpi->g.UpdateOriginalCore();
                //everything else is done through HandleExecution()
                //and functions being called from Behavior Engine
                //EnvironmentChange();
                break;
            case ACTION_NOT_PRIMED:
                strncpy(szOutBuf,GetLastThought(),200);
//                if(szOutBuf[0] == '\0')
                    if(!SettingsForm->NoVerboseOutput->Checked)
                        sprintf(szOutBuf,"%s has no action to execute",
                            gpi->szName);
                break;
            default:
                if(!SettingsForm->NoVerboseOutput->Checked)
                    sprintf(szOutBuf,"(%s couldn't accomplish %s)",
                        gpi->szName,szActionName);
        }
        if(szOutBuf[0])
            lstPrint(szOutBuf);
    }   //*/

    //clean up all the mess
    for(i = 0; i < PeopleNo ; i++ )
    {
        delete [](Inventories[i]);
    }
    delete []Inventories;
    delete []ItemArraysLen;
    delete []People;
    delete act_parm;
    DiscardInactiveGroups();
//    DiscardCorpses();   //remove dead from the list
}

//---------------------------------------------------------------------------

void __fastcall TBeTestWin::FormDestroy(TObject *Sender)
{
        if( Location != NULL )
            delete Location;
    //call this function to delete lists
    //containing goals/actions/strategies
    //definitions
	    FreeLibLists();
        FreeCharList();
        FreeDynamicTACAssumptionPopulation();
}
//---------------------------------------------------------------------------
void __fastcall TBeTestWin::FormActivate(TObject *Sender)
{
    AboutBox->ShowModal();
    SetSoloAssumptionMode(SettingsForm->SoloMode->Checked);
    SetSoloActionMode(SettingsForm->SoloActMode->Checked);
    SetMaxFailures((unsigned short)SettingsForm->MaxFailures->Text.ToInt());
    SetHistoryUse(!SettingsForm->NoLearning->Checked);
    SetSuddenChangeUse(SettingsForm->UseSuddenChange->Checked);
}

//---------------------------------------------------------------------------
void __fastcall TBeTestWin::ClearCharactersListItemClick(TObject *Sender)
{
    if(CharListHead != NULL)
    {
        if(ChoiceYN("Are you sure?","Delete all characters"))
        {
            FreeCharList();
            lstPrint("************************");
            lstPrint("All characters deleted.");
        }
    }
    else
    {
        lstPrint("No characters to delete.");
    }
}

//---------------------------------------------------------------------------
void __fastcall TBeTestWin::SaveScreenOutputItemClick(TObject *Sender)
{
	if(SaveDialog->Execute())
    {
    	if ( strchr(SaveDialog->FileName.c_str(), '.' ) )
    		MsgList->Items->SaveToFile( SaveDialog->FileName );
        else
        {
        	strcat( SaveDialog->FileName.c_str(), ".log");
    		MsgList->Items->SaveToFile( SaveDialog->FileName );
        }
    }
    Update();
}
//---------------------------------------------------------------------------
//SettingsItemClick
//takes care of everything related to settings
void __fastcall TBeTestWin::SettingsItemClick(TObject *Sender)
{
    int SavRescanFreq, SavMaxFailures;
    bool SavSoloAssumMode,SavASoloMode,SavNoVerboseOutput,
            SavHistoryCOnsiderations,SavUseSuddenChange,
            SavClearOnLoadChk;
    if(SettingsForm->RescanFreq->Text.c_str()[0] == '\0')
        SavRescanFreq = 0xFFFF;
    else
        SavRescanFreq = SettingsForm->RescanFreq->Text.ToInt();
    SavSoloAssumMode = SettingsForm->SoloMode->Checked;
    SavNoVerboseOutput = SettingsForm->NoVerboseOutput->Checked;
    SavASoloMode = SettingsForm->SoloActMode->Checked;
    SavHistoryCOnsiderations = SettingsForm->NoLearning->Checked;
    SavUseSuddenChange = SettingsForm->UseSuddenChange->Checked;
    SavClearOnLoadChk = SettingsForm->ClearOnLoadChk->Checked;
    if(SettingsForm->MaxFailures->Text.c_str()[0] == '\0')
        SavMaxFailures = 0xFFFF;
    else
        SavMaxFailures = SettingsForm->MaxFailures->Text.ToInt();

    if(SettingsForm->ShowModal() == mrCancel)
    //restore settings. Cannot "just reset it", because the window
    //is not being destroyed between the successive calls
    {
        SettingsForm->RescanFreq->Text = AnsiString(SavRescanFreq);
        SettingsForm->NoVerboseOutput->Checked = SavNoVerboseOutput;
        SettingsForm->SoloMode->Checked = SavSoloAssumMode;
        SettingsForm->SoloActMode->Checked = SavASoloMode;
        SettingsForm->MaxFailures->Text = AnsiString(SavMaxFailures);
        SettingsForm->NoLearning->Checked = SavHistoryCOnsiderations;
        SettingsForm->UseSuddenChange->Checked = SavUseSuddenChange;
        SettingsForm->ClearOnLoadChk->Checked = SavClearOnLoadChk;
    }
    SetSoloAssumptionMode(SettingsForm->SoloMode->Checked);
    SetSoloActionMode(SettingsForm->SoloActMode->Checked);
    SetMaxFailures((unsigned short)SettingsForm->MaxFailures->Text.ToInt());
    SetHistoryUse(!SettingsForm->NoLearning->Checked);
    SetSuddenChangeUse(SettingsForm->UseSuddenChange->Checked);
}

//---------------------------------------------------------------------------
void __fastcall TBeTestWin::NextStepItemClick(TObject *Sender)
{
    char MsgToAdd[30] = "";
    int nRescanFreq;
    sprintf(MsgToAdd,"+++++++++++++++++++++++++++++++++++");
    lstPrint(MsgToAdd);
    sprintf(MsgToAdd,"+ Time passes... Event %d:",step);
    lstPrint(MsgToAdd);
    if(SettingsForm->RescanFreq->Text.c_str()[0] == '\0')
        nRescanFreq = 0xFFFF;
    else
        nRescanFreq = SettingsForm->RescanFreq->Text.ToInt();

    if(!(step % nRescanFreq) || ForceScans)
    	EnvironmentChange();
    step++;
    ForceScans = false;
    NextEvent();
}

//---------------------------------------------------------------------------
void UserTurn(Character *PChar)
{
    if(PChar == NULL)
        return;
    ControlForm->ActiveCharacter = PChar;
    ControlForm->ShowModal();
    ControlForm->ActiveCharacter = NULL;
}

//---------------------------------------------------------------------------

void __fastcall TBeTestWin::BeRefClick(TObject *Sender)
{
    Application->HelpJump("");
}
//---------------------------------------------------------------------------
//CopyItemClick
//copies selected lines to clipboard
void __fastcall TBeTestWin::CopyItemClick(TObject *Sender)
{
    char *szSelected;
    TClipboard *clip;
    int i,nLen = 0;
    for(i = 0; i < MsgList->Items->Count; i++)
        if(MsgList->Selected[i])
            nLen += (MsgList->Items->Strings[i].Length() + 2);

    if(nLen == 0)
    {
        MessageBeep(MB_ICONHAND);
        return;
    }
    szSelected = new char[nLen];
    szSelected[0] = '\0';
    for(i = 0; i < MsgList->Items->Count; i++)
    {
        if(!MsgList->Selected[i])
            continue;
        strcat(szSelected,MsgList->Items->Strings[i].c_str());
        strcat(szSelected,"\n");
    }

    clip = Clipboard();
    if(clip == NULL)
    {
        Stop("Cannot access clipboard");
        return;
    }
    clip->SetTextBuf(szSelected);
    MessageBeep(MB_ICONASTERISK);
    delete []szSelected;
}
//---------------------------------------------------------------------------


