/*
 * creationmanager.cpp - author: Andrew Craig
 *
 * Copyright (C) 2003 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) 
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation (version 2 of the License)
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
#include <psconfig.h>


#include "psserver.h"
#include "globals.h"
#include "creationmanager.h"
#include "util/psdatabase.h"
#include "util/serverconsole.h"
#include "util/eventmanager.h"
#include "iserver/idal.h"
#include "net/msghandler.h"
#include "net/charmessages.h"
#include "client.h"
#include "clients.h"
#include "psserverchar.h"
#include "cachemanager.h"
#include "bulkobjects/psraceinfo.h"
#include "bulkobjects/pscharacterloader.h"

//////////////////////////////////////////////////////////////////////////////
// SFC MACROS
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////



psCharCreationManager::psCharCreationManager()
{    
    raceCPValues = 0;
    raceCPValuesLength = 0;
}

psCharCreationManager::~psCharCreationManager()
{
    delete[] raceCPValues;    
}


bool psCharCreationManager::Initialize( )
{

    if ( !( LoadCPValues() && 
            LoadCreationChoices() && 
            LoadLifeEvents()) )
        return false;
        
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CHAR_CREATE_CP,REQUIRE_ANY_CLIENT);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CHAR_CREATE_PARENTS,REQUIRE_ANY_CLIENT);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CHAR_CREATE_CHILDHOOD,REQUIRE_ANY_CLIENT);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CHAR_CREATE_LIFEEVENTS,REQUIRE_ANY_CLIENT);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CHAR_CREATE_TRAITS,REQUIRE_ANY_CLIENT);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CHAR_CREATE_NAME,REQUIRE_ANY_CLIENT);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CHAR_DELETE,REQUIRE_ANY_CLIENT);
   
    // Other loaders are here.        
    return true;
}

void psCharCreationManager::HandleMessage(MsgEntry *pMsg,Client *client)
{
    switch ( pMsg->GetType() )
    {
        /* This is a simple message requesting a CP value for a race
         * so we can handle the raw message data here.
         */
        case MSGTYPE_CHAR_CREATE_CP:
        {            
            MsgEntry *msg = new MsgEntry( 100 );
            msg->SetType(MSGTYPE_CHAR_CREATE_CP);   
            msg->clientnum      = pMsg->clientnum;            
            
            int raceID =  pMsg->GetInt32();
            // Add race ID            
            msg->Add((int32_t)raceID);
            
            // Add Race CP points
            
            if (raceID>=0  &&  raceID<raceCPValuesLength)
                msg->Add( (int32_t)raceCPValues[raceID].value );
            else
            {
                Error2("Invalid raceID %i", raceID);
                msg->Add((uint32_t)0);
            }

            msg->ClipToCurrentSize();
            
            if (msg->overrun)
            {
                Bug1("psCharCreationManager::HandleMessage() would have overflowed message buffer.\n");
            }
            else
                psserver->GetEventManager()->SendMessage(msg);
            return;
        }  
       
        case MSGTYPE_CHAR_DELETE:
        {
            HandleCharDelete( pMsg, client );
            return;
        }

        case MSGTYPE_CHAR_CREATE_NAME:
        {
            HandleName(pMsg,client);
            return;
        }
        case MSGTYPE_CHAR_CREATE_PARENTS:
        {
            HandleParents(pMsg,client);
            return;            
        }
        
        case MSGTYPE_CHAR_CREATE_CHILDHOOD:
        {
            HandleChildhood(pMsg,client);
            return;
        }

        case MSGTYPE_CHAR_CREATE_LIFEEVENTS:
        {
            HandleLifeEvents(pMsg,client);
            return;
        }
      
        case MSGTYPE_CHAR_CREATE_TRAITS:
        {
            HandleTraits(pMsg,client);
            return;
        }
                    
    }
}

bool psCharCreationManager::HandleCharDelete( MsgEntry* me, Client* client )
{
    psCharDeleteMessage msg(me);
    csString charName = msg.charName;

    if(!charName.Length())
    {
        CPrintf(CON_WARNING,"Client %u sent malformed character name to deletion code!",client->GetClientNum());
        return false; // No char
    }

    unsigned int characteruid = psserver->CharacterLoader.FindCharacterID( client->GetAccountID(), charName );
    csString error;

    if ( psserver->CharacterLoader.AccountOwner( charName, client->GetAccountID() ) )
    {
        // Found the char?
        if ( !characteruid)
        {
            psserver->SendSystemError(client->GetClientNum(),"Couldn't find character data!");
            return false;
        }

        // Can we delete it?
        if (!psserver->CharacterLoader.DeleteCharacterData(characteruid,error))
        {
            psserver->SendSystemError(client->GetClientNum(),"Error: %s",error.GetData());
            return false;
        }

        // Remove cached objects to make sure that the client gets a fresh character
        // list from the database.
        iCachedObject *obj = CacheManager::GetSingleton().RemoveFromCache(CacheManager::GetSingleton().MakeCacheName("list",client->GetAccountID()));
        if (obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }
        obj = CacheManager::GetSingleton().RemoveFromCache(CacheManager::GetSingleton().MakeCacheName("auth",client->GetAccountID()));
        if (obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }

        // Ok, we deleted it
        psCharDeleteMessage response( charName, me->clientnum );
        response.SendMessage();
    }
    else
    {
        CPrintf(CON_WARNING,"Character %s not deleted because of non-ownership\n", charName.GetData() );
        psserver->SendSystemError(client->GetClientNum(),"You do not own that character!");
        return false;
    }

    return true;
}


bool psCharCreationManager::LoadCPValues()
{
    Result result(db->Select("SELECT race_id,initial_cp from race_info"));
    if ( !result.IsValid() || result.Count() == 0 )
        return false;
    
    raceCPValuesLength = result.Count();
    raceCPValues = new RaceCP[ raceCPValuesLength ];
    
    for (unsigned int x = 0; x < result.Count(); x++ )
    {
        raceCPValues[x].id    = result[x].GetInt(0);
        raceCPValues[x].value = result[x].GetInt(1);
    }
    
    return true;     
}

bool psCharCreationManager::LoadLifeEvents()
{
    Result events( db->Select("SELECT * from char_create_life") );
    
    if ( !events.IsValid() || events.Count() == 0 )
        return false;

    for ( unsigned int x = 0; x < events.Count(); x++ )
    {
        LifeEventChoiceServer* choice = new LifeEventChoiceServer;
        choice->id = events[x].GetInt(0);
        choice->name = events[x][1];
        choice->description = events[x][2];
        choice->cpCost = events[x].GetInt(3);
        choice->eventScript = events[x][4];
        
        csString common = events[x][5];
        choice->common = common.GetAt(0);
        
        Result adds( db->Select("SELECT adds_choice from char_create_life_relations WHERE adds_choice IS NOT NULL AND choice=%d", choice->id) );
        if (!adds.IsValid())
            return false;

        for ( unsigned int addIndex = 0; addIndex < adds.Count(); addIndex++ )
        {
            choice->adds.Push( adds[addIndex].GetInt(0) );
        }
        
        Result removes( db->Select("SELECT removes_choice from char_create_life_relations WHERE removes_choice IS NOT NULL AND choice=%d", choice->id) );
        if (!removes.IsValid())
            return false;

        for ( unsigned int removesIndex = 0; removesIndex < removes.Count(); removesIndex++ )
        {
            choice->removes.Push( removes[removesIndex].GetInt(0) );
        }
        
        lifeEvents.Push( choice );
    }  
    
    return true;                      
}

bool psCharCreationManager::LoadCreationChoices()
{
    Result result( db->Select("SELECT * from character_creation") );
    
    if ( !result.IsValid() || result.Count() == 0 )
        return false;
               
    /* For all the possible choices:    
        Create them 
        Find out into what data set they should be pushed
     */
    for ( unsigned int x = 0; x < result.Count(); x++ )
    {
        CreationChoice* choice = new CreationChoice;
        choice->id   = result[x].GetInt(0);
        choice->name = result[x][1];
        choice->description = result[x][2];
        choice->cpCost = result[x].GetInt(3);
        choice->eventScript = result[x][4];
        choice->choiceArea = ConvertAreaToInt( result[x][5] );
        
        switch (choice->choiceArea)
        {
            case FATHER_JOB:
            case MOTHER_JOB:
            case RELIGION:
            {
                parentData.Push( choice );
                break;
            }
            
               
            case BIRTH_EVENT:
            case CHILD_ACTIVITY:
            case CHILD_HOUSE:            
            case CHILD_SIBLINGS:
            case ZODIAC:
            {
                childhoodData.Push( choice );
                break;
            }
        }
    }   
    
    return true;             
}
 
 
int psCharCreationManager::ConvertAreaToInt( const char* area )
{
    csString str( area );
    
    if ( str == "ZODIAC" )
        return ZODIAC;
    if ( str == "FATHER_JOB" )
        return FATHER_JOB;
        
    if ( str == "MOTHER_JOB" ) 
        return MOTHER_JOB;        
    
    if ( str == "RELIGION" ) 
        return RELIGION;   
           
    if ( str == "BIRTH_EVENT" )
        return BIRTH_EVENT;
        
    if ( str == "CHILD_ACTIVITY" )
        return CHILD_ACTIVITY;
        
    if ( str == "CHILD_HOUSE" )
        return CHILD_HOUSE;
    
    if ( str == "CHILD_SIBLINGS" )
        return CHILD_SIBLINGS;
                
    return -1;
}

void psCharCreationManager::HandleName( MsgEntry* me, Client *client )
{
    psNameCheckMessage name;
    name.FromClient( me );
    /// Check in migration/reserve table
    csString query;
    csString escape;
    if ( name.firstName.Length() == 0 )
    {
        psNameCheckMessage response( me->clientnum, false, "Cannot have an empty first name!");      
        response.SendMessage();
        return;
    }
    
    db->Escape( escape, name.firstName );
    query.Format( "SELECT * FROM migration m WHERE m.username='%s'", escape.GetData() );    
    Result result (db->Select( query ) );    
    if ( result.IsValid() && result.Count() == 1 )
    {
        int acctID = client->GetAccountID();
        int reservedName = psserver->GetCharManager()->IsReserved( name.firstName, acctID );
        if ( reservedName == NAME_RESERVED )
        {
            psNameCheckMessage response( me->clientnum, false, "Name is in reserved database"); 
            response.SendMessage();
            return;
        }            
    }
    
    // Check uniqueness of the names
    if(!psserver->GetCharManager()->IsUnique(name.firstName))
    {
        psNameCheckMessage response( me->clientnum, false, "First name is already in use"); 
        response.SendMessage();        
        return;
    }

    if(!psserver->GetCharManager()->IsLastNameUnique(name.lastName))
    {
        psNameCheckMessage response( me->clientnum, false, "Last name is already in use"); 
        response.SendMessage();        
        return;
    }

    csString playerName = name.firstName;
    csString lastName   = name.lastName;

    // Check if the name is banned
    if(psserver->GetCharManager()->IsBanned(playerName))
    {
        csString error;
        error.Format("The name %s is banned", playerName.GetData() );
        psNameCheckMessage reject(me->clientnum,
                                    false,
                                    error );
        reject.SendMessage();
        return;
    }
    
    if(psserver->GetCharManager()->IsBanned(lastName))
    {
        csString error;
        error.Format("The lastname %s is banned", lastName.GetData() );
        psNameCheckMessage reject(me->clientnum,
                                    false,
                                    error );
        reject.SendMessage();
        return;
    }

    psNameCheckMessage response( me->clientnum, true, "Ok"); 
    response.SendMessage();                                            
}

void psCharCreationManager::HandleChildhood( MsgEntry* me,Client *client )
{
    psCreationChoiceMsg message(me);
    
    if ( !message.valid )
    {
        Debug2( LOG_NET, me->clientnum,"Received unparsable Childhood request from client: %u\n", me->clientnum );
        return;
    }
    
    psCreationChoiceMsg response( me->clientnum, (int) childhoodData.Length(), MSGTYPE_CHAR_CREATE_CHILDHOOD  );
    
    for (size_t x = 0; x < childhoodData.Length(); x++ )
    {
        response.AddChoice( childhoodData[x]->id, 
                            childhoodData[x]->name,
                            childhoodData[x]->description,
                            childhoodData[x]->choiceArea,
                            childhoodData[x]->cpCost );
    }
    
    response.ConstructMessage();
    if (response.valid)
    {
        response.SendMessage();
    }
    else
    {
        Bug2("Failed to construct a valid psCreationChoiceMsg for client id %u.\n",me->clientnum);
    }        
    
}

void psCharCreationManager::HandleParents( MsgEntry* me,Client *client )
{
    psCreationChoiceMsg message(me);
    if (!message.valid)
    {
        Debug2(LOG_NET,me->clientnum,"Received unparsable psCreationChoiceMsg from client id %u.\n",me->clientnum);
        return;
    }
        
    psCreationChoiceMsg response( me->clientnum, (int) parentData.Length(), MSGTYPE_CHAR_CREATE_PARENTS  );
    for (size_t x = 0; x < parentData.Length(); x++ )
    {
        response.AddChoice( parentData[x]->id, 
                            parentData[x]->name,
                            parentData[x]->description,
                            parentData[x]->choiceArea,
                            parentData[x]->cpCost );
    }
    
    response.ConstructMessage();
    if (response.valid)
        response.SendMessage();
    else
    {
        Bug2("Failed to construct a valid psCreationChoiceMsg for client id %u.\n",me->clientnum);
    }
}

void psCharCreationManager::HandleLifeEvents( MsgEntry* me,Client *client )
{
    psLifeEventMsg message(me);

    if (!message.valid)
    {
        Debug2(LOG_NET,me->clientnum,"Received unparsable psLifeEventMsg from client id %u.\n",me->clientnum);
        return;
    }

    psLifeEventMsg response( me->clientnum );

    for (size_t x = 0; x < lifeEvents.Length(); x++ )
    {
        response.AddEvent( lifeEvents[x] );
    }

    response.ConstructMessage();
    if (response.valid)
        response.SendMessage();
    else
    {
        Bug2("Failed to construct a valid psLifeEventMsg for client id %u.\n",me->clientnum);
    }

}

void psCharCreationManager::HandleTraits( MsgEntry* me,Client *client )
{
    psCreationChoiceMsg message(me);

    if (!message.valid)
    {
        Debug2(LOG_NET,me->clientnum,"Received unparsable psCreationChoiceMsg from client id %u.\n",me->clientnum);
        return;
    }

   csString str = "<traits>";
   CacheManager::TraitIterator traits = CacheManager::GetSingleton().GetTraitIterator();
   while (traits.HasNext())
   {
      psTrait * trait = traits.Next();
      if (!trait->onlyNPC)
      {
         str += trait->ToXML();
      }
   }
   str += "</traits>";

   // printf("Handle traits: %s\n",str.GetData());

   psCharCreateTraitsMessage response(client->GetClientNum(), str);
   response.SendMessage();
}



psCharCreationManager::LifeEventChoiceServer* psCharCreationManager::FindLifeEvent( int id )
{
    for (size_t x = 0; x < lifeEvents.Length(); x++ )
    {
        if ( lifeEvents[x]->id == id )
            return lifeEvents[x];
    }
    
    return 0;
}


psCharCreationManager::CreationChoice* psCharCreationManager::FindChoice( int id )
{
    size_t x;
    for ( x = 0; x < childhoodData.Length(); x++ )
    {
        if ( childhoodData[x]->id == id )
            return childhoodData[x];
    }
    
    for ( x = 0; x < parentData.Length(); x++ )
    {
        if ( parentData[x]->id == id )
            return parentData[x];
    }
    
    return 0;
}



bool psCharCreationManager::Validate( psCharUploadMessage& mesg, csString& errorMsg )
{

    // Check for bad parent modifier.    
    if ( mesg.fatherMod > 3 || mesg.fatherMod < 1 )
    {   
        Notify1( LOG_NEWCHAR, "New character tried to use invalid father modification" );
        errorMsg = "Invalid father modification selected";
        return false;
    }    
        
    if ( mesg.motherMod > 3 || mesg.motherMod < 1 )
    {
        Notify1( LOG_NEWCHAR, "New character tried to use invalid mother modification" );    
        errorMsg = "Invalid mother modification selected";
        return false;
    }        
    
    int cpCost = CalculateCPLife( mesg.lifeEvents ) + 
                 CalculateCPChoices( mesg.choices, mesg.fatherMod, mesg.motherMod );
                 
     psRaceInfo* race;
     race = CacheManager::GetSingleton().GetRaceInfoByNameGender( (PSCHARACTER_RACE)mesg.race, 
                                                            (PSCHARACTER_GENDER)mesg.gender );                      

    if ( !race )
    {
        errorMsg = "No race selected";
        return false;
    }
    else
    {
        if ( cpCost > race->initialCP )
        {
            Notify1( LOG_NEWCHAR, "New character exceeded CP allowance" );   
            errorMsg = "CP allowance exceeded.";
            return false;
        }            
    }                                                            
    return true;
}


int psCharCreationManager::CalculateCPLife( csArray<uint32_t>& events )
{
    int cpCost = 0;
    for ( size_t li = 0; li < events.Length(); li++ )
    {
        psCharCreationManager::LifeEventChoiceServer *event;
        event = psserver->charCreationManager->FindLifeEvent( events[li] );
        
        if ( event )
        {
            cpCost+=event->cpCost;
        }        
    }    
    
    return cpCost;
}

int psCharCreationManager::CalculateCPChoices( csArray<uint32_t>& choices, int fatherMod, int motherMod )
{
    int cpCost = 0;
    for ( size_t ci = 0; ci < choices.Length(); ci++ )
    {
        psCharCreationManager::CreationChoice* choice = FindChoice( choices[ci] );
        if ( choice )
        {
            if ( choice->choiceArea == FATHER_JOB )
            {
                cpCost+= choice->cpCost*fatherMod;
            }
            else if ( choice->choiceArea == MOTHER_JOB )
            {
                cpCost+= choice->cpCost*motherMod;
            }    
            else
                cpCost+= choice->cpCost;        
        }        
    }
    
    return cpCost;
}
