/*******************************************************************
/*  VerbalUnit.cpp
/*  Author: Vadim Berman
/*
/*  Description:
/*  implementation of non-member functions related to verbal interaction
/*  of any kind
/*
/*  The contents of this file are subject to the Brainiac Public License.
/*  Version 1.0 (the "License"); you may not use this file except in
/*  compliance with the License. You may obtain a copy of the License at
/*  http://www.twilightminds.com
/*
/*  Software distributed under the License is distributed on an "AS IS"
/*  basis WITHOUT WARRANTY OF ANY KIND, either express or implied. See
/*  the License for the specific language governing rights and limitations
/*  under the License.
/*
/*  Copyright (C) 1999 Twilight Minds. All rights reserved.
/********************************************************************/
//---------------------------------------------------------------------------
#include "VerbalUnit.h"
//---------------------------------------------------------------------------
#pragma hdrstop
//---------------------------------------------------------------------------
char VerbalOutput[MAX_VERBAL_LEN];

//***************************************************************************
//GetVerbalOutput
//********** Description: **************
//return last verbal output string (for debugging purposes)
//********** Parameters:  **************
//none
//---------------------------------------------------------------------------
char *BBECALL GetVerbalOutput()
{ return VerbalOutput; }


//***************************************************************************
//AbuseGenerator
//********** Description: **************
//return a random abuse dependent on the Core attributes of the initiator and
//the objective
//********** Parameters:  **************
//Core& Initiator            - reference to the initiator's Core
//Core& Abused               - reference to the objective's ("abused") Core
//unsigned char RealOrSeemed - use SEEMED / TRUE / BOTH attributes
//---------------------------------------------------------------------------
char *BBECALL AbuseGenerator(Core& Initiator,Core& Abused, unsigned char RealOrSeemed)
/*
RealOrSeemed scores:
0 = seemed (also in the array)
1 = real (also in the array)
2 = both (some abuses may be generated due to differences)
*/
{
    unsigned char UseTrue;
    char ScoreImportance[SCORES_NUM], tmp;
    long i = 0,j;
    if(RealOrSeemed)
        UseTrue = TRUE;
    else
        UseTrue = FALSE;
    while(i < SCORES_NUM)
    {
        ScoreImportance[i] = (char)(i + 6);
        i++;
    }

    i = 0;
    while(i < SCORES_NUM-1)
    {
        j = i + 1;
        while(j < SCORES_NUM)
        {
            if(ScoreVal(ScoreImportance[i],Initiator) <
                    ScoreVal(ScoreImportance[j],Initiator))
            {
                SWAP(ScoreImportance[i],ScoreImportance[j],tmp)
            }
            j++;
        }
        i++;
    }

    for(i = 0; i < SCORES_NUM ; i++)
    {
        if(ScoreVal(ScoreImportance[i],Initiator,UseTrue) >
                ScoreVal(ScoreImportance[i],Abused,UseTrue))
            switch(ScoreImportance[i])
            {
                case MEANNESS:
                        return "wimp";
                case WEALTH:
                        return "tinman";
                case BEAUTY:
                        return "ugly S.O.B.";
                case INT:
                        return "idiot";
                case COU:
                        return "coward";
            }
        if(RealOrSeemed == 2)
            if(ScoreVal(ScoreImportance[i],Abused,TRUE) <
                ScoreVal(ScoreImportance[i],Abused,FALSE))
            {
                switch(ScoreImportance[i])
                {
                    case MEANNESS:
                        return "mean-looking wimp";
                    case WEALTH:
                        return "wealthy-looking tinman";
                    case INT:
                        return "smart-looking idiot";
                    case COU:
                        return "hero-looking coward";
                }
            }
    }

    switch(Random(1,10))
    {
        case 1:
            return "son of a bitch";
        case 2:
            return "asshole";
        case 3:
            return "bastard";
        case 4:
            return "motherfucker";
        case 5:
            return "S.O.B.";
        case 6:
            return "jerk";
        case 7:
            return "dork";
        case 8:
            return "fucker";
        case 9:
            return "piece of shit";
        case 10:
            return "dumbass";
    }
    return "motherfucker";
}

//***************************************************************************
//AcceptDealProposal
//********** Description: **************
//return the importance of accomplishing a deal
//********** Parameters:  **************
//Action *aContainer - Action containing proposed (Reported1) and
//                      requested (Reported2) Actions
//---------------------------------------------------------------------------
percentage BBECALL AcceptDealProposal(Action *aContainer)
{
    Core *Initiator, *Objective;
    short ProposedValue, RequestedValue, MaxDiff;
    Action *aProposed, *aRequested;

    if(aContainer == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }

    aRequested = aContainer->GetReported2Ptr();
    if(aRequested == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }

    aProposed = aContainer->GetReported1Ptr();
    if(aProposed == NULL)
    //Request model is maintained other way.
    // That's why aProposed is used for parameter
        return AcceptRequest(aProposed,aContainer->GetInitiator());
    Initiator = aContainer->GetInitiator();
    Objective = aContainer->GetObjective();
    if(Initiator == NULL ||
            Objective == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }

    //make sure the actions are primed CORRECTLY:
    //Reported1 = proposed action
    //Reported2 = requested action
    if(aRequested->GetObjItem())
        aRequested->InitTargets(Objective,Initiator);

    if(aProposed->GetObjItem())
        aProposed->InitTargets(Initiator,Objective);
    //end addition

    Initiator = aProposed->GetInitiator();
    Objective = aProposed->GetObjective();

    ProposedValue = (short)(PersonalEvaluate(*aProposed,*Objective));
    RequestedValue = (short)(PersonalEvaluate(*aRequested,*Objective));

    if(ProposedValue + RequestedValue > 0)
        return (percentage)(ProposedValue + RequestedValue);
    else
        if(ProposedValue == RequestedValue)
            return 1;
        else
        {
            MaxDiff = MaxDiffWealth(Objective,Initiator);
            MaxDiff = (percentage)(MaxDiff + RequestedValue + ProposedValue);
            if(MaxDiff == 0)
                MaxDiff = 1;
            if(MaxDiff > 0)
                return (percentage)MaxDiff;
            else
                return 0;
        }
}

//***************************************************************************
//AcceptDiffWealth
//********** Description: **************
//return whether the negative gap in the Wealth points is accepted (ignored) due
//to a positive attitude or combat overall difference
//********** Parameters:  **************
//Core *Evaluator     - pointer to the evaluating Agent's Core
//Core *AnotherPerson - pointer to the evaluated Agent's Core
//short nDiff         - the gap in the Wealth points; the gap is
//                     (ABS(RequestedValue) - ABS(ProposedValue)) - if negative,
//                     TRUE is returned without any checks
//---------------------------------------------------------------------------
BOOL BBECALL AcceptDiffWealth(Core *Evaluator,Core *AnotherPerson,short nDiff)
{
    percentage AttTo, EvMean, AnMean;
    float Agree;
    if(Evaluator == NULL || AnotherPerson == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }
    if(nDiff <= 0)
        return TRUE;

    AttTo = GetAttitude(AnotherPerson->attId,Evaluator->attPersonal,TRUE);
    EvMean = Evaluator->Meanness[1];
    if(!EvMean)
        EvMean = Evaluator->Meanness[0];
    AnMean = AnotherPerson->Meanness[1];
    if(!AnMean)
        AnMean = AnotherPerson->Meanness[1];

    if(AttTo <= 0)
        Agree = (float)(1.*EvMean/AnMean);
    else
        Agree = (float)(1.*nDiff/AttTo + EvMean/AnMean);

    if(Agree > AGREE_FACTOR_YES_LIMIT)
    {
        if(Agree > AGREE_FACTOR_ATTCHG_LIMIT)
            ModifyAttitude(AnotherPerson->attId,
                (percentage)(BAD_REQUEST_MODIFY *
                    Evaluator->AttitudeChangeUnit),
                Evaluator->attPersonal,2);
        return FALSE;
    }
    else
        return TRUE;
}

//***************************************************************************
//AcceptDiffWealth
//********** Description: **************
//return the maximum accepted negative gap in the Wealth points
//********** Parameters:  **************
//Core *Evaluator     - pointer to the evaluating Agent's Core
//Core *AnotherPerson - pointer to the evaluated Agent's Core
//---------------------------------------------------------------------------
percentage BBECALL MaxDiffWealth(Core *Evaluator,Core *AnotherPerson)
{
    percentage AttTo, EvMean, AnMean, nDiff;
    if(Evaluator == NULL || AnotherPerson == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }

    AttTo = GetAttitude(AnotherPerson->attId,Evaluator->attPersonal,TRUE);
    EvMean = Evaluator->Meanness[1];
    if(!EvMean)
        EvMean = Evaluator->Meanness[0];
    AnMean = AnotherPerson->Meanness[1];
    if(!AnMean)
        AnMean = AnotherPerson->Meanness[1];

    if(AttTo <= 0)
    {
        nDiff = 0;
    }
    else
    {
        nDiff = (AGREE_FACTOR_YES_LIMIT - EvMean/AnMean)*AttTo;
    }

    return nDiff;   //the result is always unsigned
}

//***************************************************************************
//AcceptRequest
//********** Description: **************
//return the importance of satisfying a request
//********** Parameters:  **************
//Action *aRequested - pointer to the requested Action
//Core *pbsRequester - pointer to the requesting Agent's Core
//---------------------------------------------------------------------------
percentage BBECALL AcceptRequest(Action *aRequested,Core *pbsRequester)
{
    Core *Initiator, *Objective;
    short ActValue,Diff;
    Initiator = aRequested->GetInitiator();
    Objective = aRequested->GetObjective();
    if(Initiator == NULL || Objective == NULL)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }

    ActValue = (short)PersonalEvaluate(*aRequested,*Initiator);
        //that is, value
    ActValue = VALID_PERCENTAGE(ActValue);
    Diff = MaxDiffWealth(Initiator,pbsRequester);
    Diff += ActValue;
    Diff = VALID_PERCENTAGE(Diff);
    if(Diff == 0)
        Diff = 1;
    if(Diff >  0)
        return (percentage)Diff;
    else
        return 0;
}

//***************************************************************************
//AcceptStatement
//********** Description: **************
//return whether the evaluator believes the statement
//********** Parameters:  **************
//Action *Stated   - pointer to the reported Action
//Core *pEvaluator - pointer to the evaluating Agent's Core
//---------------------------------------------------------------------------
ActionError BBECALL AcceptStatement(Action *Stated, Core *pEvaluator)
{
    Core Initiator, Objective;
    Action EvaluatorThought;
    ActionError aeCode;

    PostError(0);

    if(Stated == NULL)
    {
        PostError(INVALID_PARAM);
        return ACTION_NOT_PRIMED;
    }

    if(Stated->GetInitiator() == NULL ||
            Stated->GetObjective() == NULL)
    {
        PostError(INVALID_PARAM);
        return ACTION_NOT_PRIMED;
    }

    Initiator = *(Stated->GetInitiator());
    Objective = *(Stated->GetObjective());
    CalcOpinion(pEvaluator,&Initiator);

    //That is, conduct a thought experiment. The main advantage is
    //returned exact errorcode, telling us the reason of disbelief
    EvaluatorThought.Init(Stated);
    EvaluatorThought.InitTargets(&Initiator,&Objective,NULL);
    aeCode = EvaluatorThought.Execute(NULL,TRUE);
    if(aeCode == ACTION_SUCCESS && pEvaluator != NULL)
    {
        //modify attitude to the initiator
        PersonalAttitude2Action(EvaluatorThought,*pEvaluator,TRUE);
    }

    return aeCode;
}

//***************************************************************************
//SelectItemForDealProposal
//********** Description: **************
//select the most suitable inventory item for the deal by its value to the
//evaluator
//********** Parameters:  **************
//ItemPtrArr InventoryPtrs  - array of pointers to the inventory items
//short nInvSize            - number of items in the inventory
//Core& Evaluator           - reference to the evaluator's Core
//percentage MinWantedValue - minimum wanted item value
//percentage MaxWantedValue - maximum wanted item value
//---------------------------------------------------------------------------
short BBECALL SelectItemForDealProposal(ItemPtrArr InventoryPtrs,short nInvSize,
        Core& Evaluator, percentage MinWantedValue,
        percentage MaxWantedValue)
{
    short nMinFitItemIdx = -1, i;
    percentage MinVal = ERROR_PERCENTAGE, CurrItemVal;
    if(nInvSize <= 0 || InventoryPtrs == NULL)
    {
        PostError(INVALID_PARAM);
        return -1;
    }

    for(i = 0; i < nInvSize; i++)
    {
        CurrItemVal = ItemPersonalEval(Evaluator, *(InventoryPtrs[i]));
        if(CurrItemVal < MinVal)
            if((CurrItemVal >= MinWantedValue)
                && (CurrItemVal <= MaxWantedValue))
            {
                MinVal = CurrItemVal;
                nMinFitItemIdx = i;
            }
    }

    return nMinFitItemIdx;
}

//***************************************************************************
//AcceptThreat
//********** Description: **************
//return whether the given threat's conditions are accepted
//********** Parameters:  **************
//Action *aThreatContainer - Action containing the threat conditions
//ID idPreventiveGoal      - Goal that should be set in order to prevent the
//                           actualization of the threat
//Agent *ActionBoy         - person that should adopt the preventive strategy
//---------------------------------------------------------------------------
ActionError BBECALL AcceptThreat(Action *aThreatContainer, ID idPreventiveGoal,
        Agent *ActionBoy )
{
    ActionError ae;
    Core *Initiator,*Objective,*pbsThrInit,*pbsThrObj;
    Action *aThreat,*aDemanded;
    percentage ThreatEval,DemandEval,SignifLimit,VitalLimit;
    GoalDefNodeP gp;

    //some trivial checks 1st...
    PostError(0);

    Initiator = aThreatContainer->GetInitiator();
    Objective = aThreatContainer->GetObjective();
    if(Objective == NULL || Initiator == NULL)
    {
        PostError(INVALID_PARAM);
        return ACTION_NOT_PRIMED;
    }

    aThreat = aThreatContainer->GetReported1Ptr();
    //remove item target
    pbsThrInit = aThreat->GetInitiator();
    pbsThrObj  = aThreat->GetObjective();
    aThreat->ResetTargets();
    aThreat->InitTargets(pbsThrInit,pbsThrObj);
    aDemanded = aThreatContainer->GetReported2Ptr();
    //2nd, check if the threat is hollow
    ae = AcceptStatement(aThreat, Objective);
    if(LastBEErrorCode())
        return ACTION_NOT_PRIMED;//and leave the last error
    if(ae != ACTION_SUCCESS)
        return WOULD_NOT_BELIEVE;

    SignifLimit = -50;
    VitalLimit  = -80;

    DemandEval = PersonalEvaluate(*aDemanded,*Objective);
    if(DemandEval < SignifLimit)
        return ACTION_SUCCESS;

    ThreatEval = PersonalEvaluate(*aThreat,*Objective);
    if(ThreatEval > SignifLimit)
    {
    //so the neutral and good actions won't cause acception
        return NOT_INTERESTED_IN_DEAL;
    }

    if(DemandEval >= ThreatEval
            && ThreatEval <= VitalLimit)
        return ACTION_SUCCESS;

    //the threatening side demands more than it promises to
    //accomplish. Before agreement, a strategy must be adopted
    if(ActionBoy != NULL && idPreventiveGoal != GOAL_NONE)
    {
        gp = GetGoalNode(idPreventiveGoal);
        if(gp != NULL)
            ActionBoy->PickStrategy(gp->goal,Initiator);
    }

    if(ThreatEval <= VitalLimit)
    {
        return ACTION_SUCCESS;
    }

    return NOT_INTERESTED_IN_DEAL;
}

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

