/*
 * pawsmainwidget.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.
 *
 */
// pawsmainwidget.cpp: implementation of the pawsMainWidget class.
//
//////////////////////////////////////////////////////////////////////
#include <psconfig.h>

#include <iutil/vfs.h>
#include <iutil/document.h>
#include <iutil/evdefs.h>
#include <iutil/object.h>
#include <csutil/xmltiny.h>
#include <csutil/event.h>
#include <csgeom/transfrm.h>
#include <iengine/camera.h>
#include <iengine/sector.h>
#include <iengine/mesh.h>
#include <ivideo/fontserv.h>

#include "psmainwidget.h"

#include "chatwindow.h"
#include "paws/pawsmanager.h"
#include "inventorywindow.h"
#include "pawsinfowindow.h"
#include "util/log.h"
#include "util/strutil.h"
#include "util/psxmlparser.h"
#include "pscamera.h"
#include "pscharcontrol.h"
#include "effects/pseffectmanager.h"
#include "actionhandler.h"
#include "paws/pawsprefmanager.h"
#include "pawsgameboard.h"

#define DEFAULT_CONFIG_FILE_NAME       "/this/data/options/entityinter_def.xml"
#define CONFIG_FILE_NAME       "/this/data/options/entityinter.xml"
#define FONT_SIZE              13  // Size for the font at 800x600

//////////////////////////////////////////////////////////////////////
//                  entity types
//////////////////////////////////////////////////////////////////////

psEntityType::psEntityType(csString id, csString label, int dflt, csString commandsStr, csString labelsStr)
{
    this->id = id;
    this->label = label;
    this->dflt = dflt;
    usedCommand = dflt;
    Split(labelsStr, this->labels);
    Split(commandsStr, this->commands);
}

psEntityTypes::psEntityTypes(iObjectRegistry* objReg)
{
    this->objReg = objReg;
    vfs = CS_QUERY_REGISTRY( objReg, iVFS );
    
    types.Push(new psEntityType("items", "Items", 2,
               "use|combine|pickup|unlock|examine", 
               "Use|Combine|Pick up|Unlock|Examine"));
    types.Push(new psEntityType("players", "Players", 0,
               "playerdesc|exchange|attack", 
               "Description|Exchange|Attack"));
    types.Push(new psEntityType("merchants", "Merchants", 0,
               "buysell|give|playerdesc|attack", 
               "Buy/Sell|Give|Description|Attack"));
    types.Push(new psEntityType("aliveNPCs", "Alive NPCs", 3,
               "buysell|give|playerdesc|attack", 
               "Buy/Sell|Give|Description|Attack"));
    types.Push(new psEntityType("deadNPCs", "Dead NPCs", 0,
               "loot|playerdesc", 
               "Loot|Description"));
    types.Push(new psEntityType("petNPCs", "Pet NPCs", 0,
               "playerdesc", 
               "Description"));
    /*If you add a new psEntityType don't forget to add the widgets in 
    data/gui/configentityinter.xml!!!*/
}

bool psEntityTypes::LoadConfigFromFile()
{
    // Check if there have been created a custom file
    // else use the default file.
    csString fileName = CONFIG_FILE_NAME;
    if (!vfs->Exists(fileName))
    {
        fileName = DEFAULT_CONFIG_FILE_NAME;
    }

    csRef<iDocument> doc = ParseFile(objReg, fileName);
    if (!doc) 
    {
        Error2("Parse error in %s", fileName.GetData());
        return false;
    }
    csRef<iDocumentNode> rootNode = doc->GetRoot();
    if(!rootNode)
    {
        Error2("No XML root in %s", fileName.GetData());
        return false;
    }
    csRef<iDocumentNode> topNode = rootNode->GetNode("EntityInteraction");
    if(!topNode)
    {
        Error2("No <EntityInteraction> tag in %s", fileName.GetData());
        return false;
    }
    for (size_t i=0; i < types.Length(); i++)
    {
        csRef<iDocumentNode> entNode = topNode->GetNode(types[i]->id);
        if (entNode != NULL)
        {
            types[i]->usedCommand = entNode->GetAttributeValueAsInt("value");
        }
        else
        {
            types[i]->usedCommand = types[i]->dflt;
        }
    }
    return true;
}

bool psEntityTypes::SaveConfigToFile()
{
    csString xml;
    xml = "<EntityInteraction>\n";
    for (size_t i=0; i < types.Length(); i++)
        xml += csString().Format("\t<%s value='%i'/>\n", types[i]->id.GetData(), types[i]->usedCommand);
    xml += "</EntityInteraction>";
    return vfs->WriteFile(CONFIG_FILE_NAME, xml.GetData(), xml.Length());
}

csString psEntityTypes::BuildDfltBehaviors()
{
    csString dflt;
    for (size_t i=0; i < types.Length(); i++)
    {
        if (i > 0)
            dflt += "|";
        dflt += types[i]->commands[types[i]->usedCommand];
    }
    return dflt;
}

//////////////////////////////////////////////////////////////////////
//                  psMainWidget
//////////////////////////////////////////////////////////////////////

psMainWidget::psMainWidget() : entTypes(PawsManager::GetSingleton().GetObjectRegistry()),
               psCmdBase( NULL,NULL,  PawsManager::GetSingleton().GetObjectRegistry() )
{
    cel = NULL;
    chatWindow = NULL;
    locked = false;
    entTypes.LoadConfigFromFile();
    lastWidget = 0;
}

psMainWidget::~psMainWidget()
{
    if(msgqueue)
        msgqueue->Unsubscribe(this, MSGTYPE_SYSTEM);
}

bool psMainWidget::OnDoubleClick( int button, int keyModifier, int x, int y )
{
    // Check to see if an entity was selected.
    pawsWidget* underlaying = WidgetAt( x, y );

    if ( psengine->GetPSCamera() && underlaying == this )
    {
        GEMClientObject* over = FindMouseOverObject( x, y );

        if ( psengine->GetMouseBinds()->CheckBind( "EntitySelect", button, keyModifier) )
        {
            if ( over )
            {
                psUserActionMessage action( 0, over->GetEntity()->GetID(), "dfltBehavior", entTypes.BuildDfltBehaviors() );
                action.SendMessage();
            }
        }
    }
    return false;
}

bool psMainWidget::OnKeyDown( int keyCode, int key, int modifiers )
{
 if (!locked)
 {
    if (pawsMainWidget::OnKeyDown(keyCode,key,modifiers))
    {
        return true;
    }
    
    // Check for tabing in and out of chat window.
    psCharController* charctrl = psengine->GetCharControl();
    if(!charctrl)
        return true;

    if ( charctrl->MatchTrigger("Toggle chat",psControl::KEYBOARD,keyCode,modifiers) )
    {
        if ( chatWindow == NULL )
        {
            chatWindow = (pawsChatWindow*)FindWidget("ChatWindow");           
        }
               
        if ( chatWindow )
        {
            if ( chatWindow->InputActive() )
            {
                if ( lastWidget )
                {
                    BringToTop(lastWidget);                
                    PawsManager::GetSingleton().SetCurrentFocusedWidget( lastWidget );
                }                    
            }                                
            else
            {
                lastWidget = PawsManager::GetSingleton().GetCurrentFocusedWidget();
                chatWindow->Show();
                BringToTop( chatWindow );
                PawsManager::GetSingleton().SetCurrentFocusedWidget( chatWindow->FindWidget("InputText") );
            }
        }
        return true;
    }
    
    if ( charctrl->MatchTrigger("Reply tell",psControl::KEYBOARD,keyCode,modifiers) )
    {
        if ( chatWindow == NULL )
        {
            chatWindow = (pawsChatWindow*)FindWidget("ChatWindow");
        }
        if (chatWindow && HasFocus())
        {
            chatWindow->Show();
            BringToTop( chatWindow );
            PawsManager::GetSingleton().SetCurrentFocusedWidget( chatWindow->FindWidget("InputText") );
            chatWindow->AutoReply();
        }
        return true;
    }
    if ( charctrl->MatchTrigger("Close",psControl::KEYBOARD,keyCode,modifiers) )
    {
        pawsWidget* widget = PawsManager::GetSingleton().GetCurrentFocusedWidget();
        if (widget != this)
        {
            //Keep checking for a parent untill the main widget is found
            for (pawsWidget* parent = widget->GetParent();parent != this;parent=widget->GetParent())
                widget = parent;
            widget->Close();
        }
        else
        {
            pawsControlWindow* ctrlWindow =  dynamic_cast<pawsControlWindow*>(FindWidget("ControlWindow"));
            if(!ctrlWindow)
                return false; // Should never happen, but no way to be sure.
            ctrlWindow->Toggle();
        }
        return true;
    }
    return false;
   
 }
 //If we return true, it will not parse the keycode to CharControl
 return true;
    
}

bool psMainWidget::OnMouseDown( int button, int keyModifier, int x, int y )
{
    pawsWidget* bar = FindWidget("ControlWindow");
    if (!bar)
        return false;// no gui loaded, so nothing here can be done.

    // Check to see if we are dropping an item
    if ( psengine->GetSlotManager() && psengine->GetSlotManager()->IsDragging() )
    {
        psengine->GetSlotManager()->DropItem();
        return true;
    }

    // Check to see if we are dropping a game piece
    pawsGameBoard *gameBoard = dynamic_cast<pawsGameBoard *>(FindWidget("GameBoardWindow"));
    if (gameBoard && gameBoard->IsDragging())
    {
        gameBoard->DropPiece();
    }

    // Close interact menu automatically
    pawsWidget* interactWindow = FindWidget("InteractWindow");
    if (interactWindow)
        interactWindow->Hide();

    if (button == csmbWheelUp)
    {
       psengine->GetPSCamera()->MoveDistance(-1.0f);
       return true;
    }
    if (button == csmbWheelDown)
    {
       psengine->GetPSCamera()->MoveDistance(1.0f);
       return true;
    }       

    // Check to see if an entity was selected
    pawsWidget* underlaying = WidgetAt(x,y);

    if ( psengine->GetPSCamera() && underlaying == this )
    {
        GEMClientObject* over = FindMouseOverObject( x, y );

        if (psengine->GetMouseBinds()->CheckBind("EntitySelect", button, keyModifier))
        {
        
            if ( over )
            {
                psengine->GetCharManager()->SetTarget(over,"select"); 
            }
            else 
            {
                // Deselect current target
                psengine->GetCharManager()->SetTarget(NULL, "select");

                //// Check for Action Location
                //{
                //    int poly = 0;
                //    csVector3 pos;

                //    iMeshWrapper* mesh  = psengine->GetPSCamera()->FindMeshUnder2D( x, y, &pos, &poly );

                //    if (mesh)
                //    {
                //        iSector* sector = psengine->GetPSCamera()->GetICamera()->GetSector();
                //        const char* sectorname = sector->QueryObject()->GetName();
                //        const char* meshname = mesh->QueryObject()->GetName();
                //        psengine->GetActionHandler()->Query( "SELECT", sectorname, meshname, poly, pos);
                //    }
                //}
            }
        }

        // Check Context Menu
        if (psengine->GetMouseBinds()->CheckBind("ContextMenu", button, keyModifier))
        {
            if ( over )
            {
                psengine->GetCharManager()->SetTarget(over, "context");
            }
            else
            {
                // Deselect current target
                psengine->GetCharManager()->SetTarget(NULL, "select");

                // Check for Action Location
                {
                    int poly = 0;
                    csVector3 pos;

                    iMeshWrapper* mesh  = psengine->GetPSCamera()->FindMeshUnder2D( x, y, &pos, &poly );

                    if (mesh)
                    {
                        iSector* sector = psengine->GetPSCamera()->GetICamera()->GetSector();
                        const char* sectorname = sector->QueryObject()->GetName();
                        const char* meshname = mesh->QueryObject()->GetName();

                        // See if it's worth quering
                        //bool meshRecorded = psengine->GetCelClient()->IsMeshSubjectToAction(sectorname,meshname);
                        //pawsWidget* action = PawsManager::GetSingleton().FindWidget("AddEditActionWindow");

                        //if((action && action->IsVisible()) || meshRecorded)
                            psengine->GetActionHandler()->Query( "SELECT", sectorname, meshname, poly, pos);
                    }
                }
            }
        }

    }
        
    return false;
}

bool psMainWidget::OnMouseUp( int button, int keyModifier, int x, int y )
{
    return false;
}

void psMainWidget::SetCamera( iCelEntity* entity )
{
    /* Let the main widget know about the camera since it needs it
     * to locate other entities using hitbeam.
     */ 
//    csRef<iPcCamera> pccamera = CEL_QUERY_PROPCLASS(entity->GetPropertyClassList(),
//                                                    iPcCamera );
//    guiCamera = pccamera->GetCamera();
}


GEMClientObject* psMainWidget::FindMouseOverObject( int x, int y )
{
    if ( !cel )
    {
        cel = psengine->GetCelClient();
    }

    if (!psengine->GetPSCamera())
        return NULL;

    iMeshWrapper* sel = psengine->GetPSCamera()->FindMeshUnder2D(x,y);

        
    if ( sel )
    {
        if (!sel->QueryObject())
            return NULL;

        csRef<iCelEntity> entity = cel->GetPlLayer()->FindAttachedEntity( sel->QueryObject() );
        if ( entity )
        {
            GEMClientObject* target = cel->FindObject( entity->GetID() );
            return target;
        }
        else 
        {
            return NULL;
        }            
            
    }
    return NULL;
}

bool psMainWidget::SetupMain()
{
    psCmdBase::Setup( psengine->GetMsgHandler(), psengine->GetCmdHandler());

    float fontsize;
    //fontsize = float(FONT_SIZE) / float(1024) * ;

    fontsize = PawsManager::GetSingleton().GetFontFactor() * float(FONT_SIZE);
    
    printf("Using fontsize %d for resolution %dx%d\n",(int)fontsize,screenFrame.Width(),screenFrame.Height());

    mesgFont       = graphics2D->GetFontServer()->LoadFont("/this/data/ttf/verdana.ttf",(int)fontsize);
    mesgFirstFont  = graphics2D->GetFontServer()->LoadFont("/this/data/ttf/scurlock.ttf",(int)fontsize*2);

    msgqueue->Subscribe(this, MSGTYPE_SYSTEM);

    return true;
}


void psMainWidget::HandleMessage( MsgEntry* message )
{
    if(message->GetType() != MSGTYPE_SYSTEM)
        return;

    psSystemMessage mesg(message);

    int color = -1;

    if(mesg.type == MSG_ERROR)
        color = graphics2D->FindRGB(255,0,0);
    else if(mesg.type == MSG_RESULT)
        color = graphics2D->FindRGB(255,255,0);
    else if(mesg.type == MSG_OK)
        color = graphics2D->FindRGB(0,255,0);
    else if(mesg.type == MSG_ACK)
        color = graphics2D->FindRGB(0,0,255);

    if(color != -1)
        PrintOnScreen(mesg.msgline,color);
}

void psMainWidget::DrawChildren()
{
    // Call the widget's draw functions first
    pawsMainWidget::DrawChildren();

    for(size_t i = 0; i < onscreen.Length();i++)
    {
        ClipToParent();
        onscreen[i]->Draw();
    }
}

void psMainWidget::DeleteChild(pawsWidget* txtBox)
{
    for(size_t i = 0; i < onscreen.Length();i++)
    {
        if(txtBox == onscreen[i])
        {
            pawsWidget* widget = onscreen[i];
            onscreen.DeleteIndex(i);

            delete widget;
            return;
        }
    }

    pawsMainWidget::DeleteChild(txtBox);
}

void psMainWidget::PrintOnScreen( const char* text, int color, float ymod )
{
    if (strlen(text) < 2)
    {
        Error2("Tried to print text with less than 2 chars in a Fading way (%s)",text);
        return;
    }

    // Create a fading textbox widget
    pawsFadingTextBox* txtBox = (pawsFadingTextBox*)PawsManager::GetSingleton().CreateWidget("pawsFadingTextBox");
    if (!txtBox)
    {
        Error1("Couldn't create a pawsFadingTextBox for onscreen message!");
        return;
    }

    txtBox->SetParent(this);
    txtBox->SetText(text,mesgFirstFont,mesgFont,color); // Set options and enable
    txtBox->Show();

    onscreen.Push(txtBox); // Do like this to have full controll on the controlls :P

    // Set the right size
    int h=0,w=0;
    txtBox->GetSize(w,h);
    txtBox->Resize(w,h,0);

    int x,y;
    x = (ScreenFrame().Width() / 2) - (w / 2);

    y = (int)(ScreenFrame().Height() * ymod) - (h / 2);
    y = y - ScreenFrame().Height() / 8;

    txtBox->MoveTo(x,y);


    // Move the others down
    if (onscreen.Length() < 2)
        return;

    int j = 0;
    for (int i = (int)onscreen.Length()-2; i >= 0; i--)
    {
        j++;
        pawsFadingTextBox* widget = (pawsFadingTextBox*)onscreen[i];
        widget->MoveDelta(0,h);
        
        if (j > 2) // force fading if there is more than 2 elements
            widget->Fade();
    }
}

void psMainWidget::ClearFadingText()
{
    for (int i = (int)onscreen.Length()-1; i >= 0; i--)
    {
        pawsFadingTextBox* widget = (pawsFadingTextBox*)onscreen[i];
        widget->DeleteYourself();
    }
}

