/*
 * glyphwindow.cpp - Author: Anders Reggestad
 *
 * 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>

// CS INCLUDES
#include <csutil/util.h>
#include <iutil/evdefs.h>
#include <propclass/mesh.h>

// COMMON INCLUDES
#include "net/message.h"
#include "net/msghandler.h"
#include "net/cmdhandler.h"
#include "util/strutil.h"
#include "util/log.h"

// CLIENT INCLUDES
#include "pscelclient.h"
#include "../globals.h"
#include "gui/psmainwidget.h"

// PAWS INCLUDES
#include "paws/pawstextbox.h"
#include "paws/pawsprefmanager.h"
#include "pawsglyphwindow.h"
#include "paws/pawsmanager.h"
#include "paws/pawsbutton.h"
#include "paws/pawsobjectview.h"
#include "paws/pawstexturemanager.h"
#include "paws/pawslistbox.h"


#define PURIFY_BUTTON 1000
#define SAVE_BUTTON   1001
#define CAST_BUTTON   1002



//////////////////////////////////////////////////////////////////////
//
//                      pawsGlyphWindow
//
//////////////////////////////////////////////////////////////////////

pawsGlyphWindow::pawsGlyphWindow()

{
    msgHandler = NULL;

    for (int i = 0; i < GLYPH_ASSEMBLER_SLOTS; i++)
        assembler[i] = NULL;
}


pawsGlyphWindow::~pawsGlyphWindow()
{
}

void pawsGlyphWindow::Show()
{
    pawsWidget::Show();

    // Ask the server to send us the glyphs
    psRequestGlyphsMessage outGoingMessage;
    msgHandler->SendMessage( outGoingMessage.msg );
}

void pawsGlyphWindow::Hide()
{
    pawsGlyphSlot * floatingSlot = dynamic_cast <pawsGlyphSlot*> (PawsManager::GetSingleton().GetDragDropWidget());
    if ( floatingSlot!=NULL )
        PawsManager::GetSingleton().SetDragDropWidget(NULL);

    pawsWidget::Hide();
}

bool pawsGlyphWindow::PostSetup()
{
    int i;

    msgHandler = psengine->GetMsgHandler();
    if ( !msgHandler ) return false;

    // Subscribe our message types that we are interested in.
    if ( !msgHandler->Subscribe( this, MSGTYPE_GLPYH_REQUEST ) ) return false;
    if ( !msgHandler->Subscribe( this, MSGTYPE_GLYPH_ASSEMBLE ) ) return false;
    
    
    description = dynamic_cast <pawsMessageTextBox*> (FindWidget("SpellDescription"));
    if ( !description ) return false;

    spellName = dynamic_cast <pawsTextBox*> (FindWidget("SpellName"));
    if ( !spellName ) return false;

    ways.SetLength(GLYPH_WAYS);

    ways[0] = dynamic_cast <pawsListBox*> (FindWidget("crystal"));
    if ( !ways[0] ) return false;

    ways[1] = dynamic_cast <pawsListBox*> (FindWidget("blue"));
    if ( !ways[1] ) return false;

    ways[2] = dynamic_cast <pawsListBox*> (FindWidget("azure"));
    if ( !ways[2] ) return false;

    ways[3] = dynamic_cast <pawsListBox*> (FindWidget("brown"));
    if ( !ways[3] ) return false;

    ways[4] = dynamic_cast <pawsListBox*> (FindWidget("red"));
    if ( !ways[4] ) return false;

    ways[5] = dynamic_cast <pawsListBox*> (FindWidget("dark"));
    if ( !ways[5] ) return false;

    for (int wayNum=0; wayNum < GLYPH_WAYS; wayNum++)
        for (i=0; i < 3; i++)
            NewGlyphSlotRow(wayNum);

    for (i=0; i < GLYPH_ASSEMBLER_SLOTS; i++)
    {
        csString slotName;
        slotName =  "assembler";
        slotName += i;
        assembler[i] = dynamic_cast <pawsGlyphSlot*> (FindWidget(slotName));
        if (assembler[i] == NULL)
            return false;
        assembler[i]->SetStatID( 0 );            
        assembler[i]->SetDrag(false);        
    }

    return true;
}

void pawsGlyphWindow::HandleMessage ( MsgEntry* me )
{
    switch( me->GetType() )
    {
        case MSGTYPE_GLPYH_REQUEST:
        {
            HandleGlyphList(me);
            break;
        }
        case MSGTYPE_GLYPH_ASSEMBLE:
        {
            HandleAssemble( me );
            break;
        }
    }
}

void pawsGlyphWindow::HandleGlyphList( MsgEntry* me )
{
    psRequestGlyphsMessage msg( me );
    
    csArray < pawsGlyphSlot* > usedSlots;

    pawsGlyphSlot * floatingSlot = dynamic_cast <pawsGlyphSlot*> (PawsManager::GetSingleton().GetDragDropWidget());

    for ( size_t x = 0; x < msg.glyphs.Length(); x++ )
    {
        if (msg.glyphs[x].way>=GLYPH_WAYS)
            continue;

        if (floatingSlot!=NULL  &&  floatingSlot->GetStatID()==msg.glyphs[x].statID  &&  floatingSlot->GetPurifyStatus()==msg.glyphs[x].purifiedStatus)
        {
            if (msg.glyphs[x].count > 0)
                msg.glyphs[x].count--;
            else
                PawsManager::GetSingleton().SetDragDropWidget(NULL);
        }

        if (msg.glyphs[x].purifiedStatus==2)
        {            
            int assemblerCount = CountTypeInAssembler(msg.glyphs[x].statID);
            if ((size_t)assemblerCount > msg.glyphs[x].count)
            {
                RemoveTypeFromAssembler(assemblerCount - msg.glyphs[x].count);
                assemblerCount = msg.glyphs[x].count;
            }
            msg.glyphs[x].count -= assemblerCount;
        }

        pawsGlyphSlot * itemSlot = FindAppropriateSlotForGlyph(msg.glyphs, msg.glyphs[x]);
        CS_ASSERT(itemSlot);
        itemSlot->PlaceItem(msg.glyphs[x].image, msg.glyphs[x].count);
        itemSlot->SetStatID(msg.glyphs[x].statID);
        itemSlot->SetPurifyStatus(msg.glyphs[x].purifiedStatus);
        itemSlot->SetToolTip( msg.glyphs[x].name );
                
        usedSlots.Push(itemSlot);
    }

    ClearUnusedGlyphs(usedSlots);

    for (int slotNum=0; slotNum < GLYPH_ASSEMBLER_SLOTS; slotNum++)
        if (GetGlyphTypeSlot(assembler[slotNum]->GetStatID(), assembler[slotNum]->GetPurifyStatus())  ==  NULL)
            assembler[slotNum]->Clear();

    if (floatingSlot!=NULL  &&  GetGlyphTypeSlot(floatingSlot->GetStatID(), floatingSlot->GetPurifyStatus()) == NULL)
        PawsManager::GetSingleton().SetDragDropWidget(NULL);  
}

int pawsGlyphWindow::CountTypeInAssembler(int statID)
{
    int count=0;

    for (int slotNum=0; slotNum < GLYPH_ASSEMBLER_SLOTS; slotNum++)
        if (assembler[slotNum]->GetStatID()==statID)
            count++;
    return count;
}

pawsGlyphSlot * pawsGlyphWindow::FindFreeAssemblerSlot()
{
    for (int slotNum=0; slotNum < GLYPH_ASSEMBLER_SLOTS; slotNum++)
        if (assembler[slotNum]->GetStatID() == 0)
            return assembler[slotNum];
    return NULL;
}

void pawsGlyphWindow::RemoveTypeFromAssembler(int statID)
{
    for (int slotNum=0; slotNum < GLYPH_ASSEMBLER_SLOTS; slotNum++)
        if (assembler[slotNum]->GetStatID() == statID)
        {
            assembler[slotNum]->Clear();
            assembler[slotNum]->SetDefaultToolTip();
            return;
        }
}

void pawsGlyphWindow::NewGlyphSlotRow(int wayNum)
{
    if (wayNum<0  ||  wayNum>=GLYPH_WAYS)
        return;

    ways[wayNum]->NewRow();
}

bool HasReceivedGlyph(const csArray<psRequestGlyphsMessage::NetworkGlyph> & recvGlyphs, int statID, int purifyStatus)
{
    size_t glyphNum;
    for (glyphNum=0; glyphNum < recvGlyphs.Length(); glyphNum++)
        if (recvGlyphs[glyphNum].statID==statID  &&  recvGlyphs[glyphNum].purifiedStatus==purifyStatus)
            return true;
    return false;
}

pawsGlyphSlot * pawsGlyphWindow::FindAppropriateSlotForGlyph(const csArray<psRequestGlyphsMessage::NetworkGlyph> & recvGlyphs, 
                                                             const psRequestGlyphsMessage::NetworkGlyph& glyph)
{
    pawsGlyphSlot * slot;

    slot = GetGlyphTypeSlot(glyph.statID, glyph.purifiedStatus);
    if (slot != NULL)
        return slot;

    if (glyph.purifiedStatus>0  &&  !HasReceivedGlyph(recvGlyphs, glyph.statID, glyph.purifiedStatus-1))
    {
        slot = GetGlyphTypeSlot(glyph.statID, glyph.purifiedStatus-1);
        if (slot != NULL)
            return slot;
    }

    return FindFreeSlot(glyph.way);
}

void pawsGlyphWindow::HandleAssemble( MsgEntry* me )
{
    psGlyphAssembleMessage mesg;
    mesg.FromServer( me );
    
    description->Clear();
    
    spellName->SetText(mesg.name);
    description->AddMessage( mesg.description );
    description->ResetScroll();
    
}


void pawsGlyphWindow::ClearUnusedGlyphs(const csArray <pawsGlyphSlot*> & usedSlots)
{
    int wayNum, rowNum, colNum;
    pawsListBox * way;
    pawsListBoxRow * row;
    pawsGlyphSlot * slot;

    for (wayNum=0; wayNum < GLYPH_WAYS; wayNum++)
    {
        way = ways[wayNum];
        for (rowNum=0; rowNum < way->GetRowCount(); rowNum++)
        {
            row = way->GetRow(rowNum);
            for (colNum=0; colNum < way->GetTotalColumns(); colNum++)
            {
                slot = dynamic_cast <pawsGlyphSlot*> (row->GetColumn(colNum));
                if (slot!=NULL  &&  usedSlots.Find(slot)==-1)
                {
                    slot->Clear();
                }
            }
        }
    }
}

pawsGlyphSlot * pawsGlyphWindow::FindFreeSlot(int wayNum)
{
    int rowNum, colNum;
    pawsListBox * way;
    pawsListBoxRow * row;
    pawsGlyphSlot * slot;

    way = ways[wayNum];
    for (rowNum=0; rowNum < way->GetRowCount(); rowNum++)
    {
        row = way->GetRow(rowNum);
        for (colNum=0; colNum < way->GetTotalColumns(); colNum++)
        {
            slot = dynamic_cast <pawsGlyphSlot*> (row->GetColumn(colNum));
            if (slot!=NULL  &&  slot->IsEmpty())
                return slot;
        }
    }

    // no free slot available ? then create new row
    NewGlyphSlotRow(wayNum);
    return dynamic_cast <pawsGlyphSlot*> (way->GetRow(way->GetRowCount()-1)->GetColumn(0));
}

pawsGlyphSlot * pawsGlyphWindow::GetGlyphTypeSlot(int statID, int purifyStatus)
{
    int wayNum, rowNum, colNum;
    pawsListBox * way;
    pawsListBoxRow * row;
    pawsGlyphSlot * slot;

    for (wayNum=0; wayNum < GLYPH_WAYS; wayNum++)
    {
        way = ways[wayNum];
        for (rowNum=0; rowNum < way->GetRowCount(); rowNum++)
        {
            row = way->GetRow(rowNum);
            for (colNum=0; colNum < way->GetTotalColumns(); colNum++)
            {
                slot = dynamic_cast <pawsGlyphSlot*> (row->GetColumn(colNum));
                if (slot!=NULL  &&  slot->GetStatID()==statID  &&  slot->GetPurifyStatus()==purifyStatus)
                    return slot;
            }
        }
    }
    return NULL;
}

bool pawsGlyphWindow::OnButtonPressed( int mouseButton, int keyModifier, pawsWidget* widget )
{
    pawsGlyphSlot * floatingSlot = dynamic_cast <pawsGlyphSlot*> (PawsManager::GetSingleton().GetDragDropWidget());
    pawsGlyphSlot * clickedSlot = dynamic_cast <pawsGlyphSlot*> (widget);
    csString slotName = widget->GetName();


    // Check to see if this was the purify button.
    if ( widget->GetID() == PURIFY_BUTTON )
    {
        if (floatingSlot!=NULL  && floatingSlot->GetPurifyStatus()==0)
        {
            psPurifyGlyphMessage mesg( floatingSlot->GetStatID() );
            msgHandler->SendMessage( mesg.msg );

            PawsManager::GetSingleton().SetDragDropWidget(NULL);
        }
        return true;
    }

    // Check to see if this was the save button.
    if ( widget->GetID() == SAVE_BUTTON )
    {
        if (!spellName->GetText())
            return true;

        if (strcmp(spellName->GetText(),"")!=0)
        {
            csString name(spellName->GetText());
            psSaveSpellMessage mesg( name );
            msgHandler->SendMessage( mesg.msg );
        }
        return true;
    }

    // Check to see if this was the cast button.
    if ( widget->GetID() == CAST_BUTTON )
    {
        if (!spellName->GetText())
            return true;

        if (strcmp(spellName->GetText(),"")!=0)
        {
            csString name(spellName->GetText());
            psSpellCastMessage mesg( name, psengine->GetKFactor() );
            msgHandler->SendMessage( mesg.msg );
        }
        return true;
    }


    if (clickedSlot == NULL)
        return true;

    // user clicked on one of the assembler slots
    if (slotName.Slice(0, 9) == "assembler")
    {
        if (floatingSlot == NULL)
        {
            if (clickedSlot->StackCount() > 0)
            {
                CreateFloatingSlot(clickedSlot);
                clickedSlot->Clear();
            }
        }
        else if (floatingSlot->GetPurifyStatus() == 2)
        {
            pawsGlyphSlot * slot = FindFreeAssemblerSlot();
            if (slot != NULL)
            {
                slot->PlaceItem(floatingSlot->ImageName(), 1);
                slot->SetStatID(floatingSlot->GetStatID());
                slot->SetPurifyStatus(floatingSlot->GetPurifyStatus());
                PawsManager::GetSingleton().SetDragDropWidget(NULL);
                SendAssembler();
            }
        }
        else
        {
            psSystemMessage error(0,MSG_ERROR,PawsManager::GetSingleton().Translate("Not a purified glyph"));
            error.FireEvent();
        }
    }
    // user clicked on one of the glyph slots
    else
    {
        if (floatingSlot == NULL)
        {
            if (clickedSlot->StackCount() > 0)
            {
                CreateFloatingSlot(clickedSlot);
                clickedSlot->StackCount(clickedSlot->StackCount() - 1);
            }
        }
        else
        {
            pawsGlyphSlot * targetSlot = GetGlyphTypeSlot(floatingSlot->GetStatID(), floatingSlot->GetPurifyStatus());
            if (targetSlot != NULL)
            {
                targetSlot->StackCount(targetSlot->StackCount() + 1);
                PawsManager::GetSingleton().SetDragDropWidget(NULL);
                SendAssembler();
            }
        }
    }

    return true;
}

void pawsGlyphWindow::CreateFloatingSlot(pawsGlyphSlot * sourceSlot)
{
    pawsGlyphSlot * floatingSlot = new pawsGlyphSlot();
    floatingSlot->PlaceItem( sourceSlot->ImageName(), 1 );
    floatingSlot->SetStatID(sourceSlot->GetStatID());
    floatingSlot->SetPurifyStatus(sourceSlot->GetPurifyStatus());
    floatingSlot->SetBackgroundAlpha(0);

    PawsManager::GetSingleton().SetDragDropWidget( floatingSlot );
}

void pawsGlyphWindow::SendAssembler()
{
    psGlyphAssembleMessage mesg( assembler[0]->GetStatID(),
                                 assembler[1]->GetStatID(), 
                                 assembler[2]->GetStatID(), 
                                 assembler[3]->GetStatID() );
                                 
    msgHandler->SendMessage( mesg.msg );                                 
}
