/*
 * client/CharacterScreen.cc:
 *
 * Copyright (C) 2000 John Watson, jwatson@tempusmud.com
 * 
 * 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; either version 2
 * of the License, or (at your option) any later version.
 * 
 * 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 "CharacterScreen.h"
#include "../util/TextureManager.h"
#include "../widgets/Draggable.h"
#include "../widgets/Widget.h"
#include "../widgets/LabelWidget.h"
#include "../widgets/ScrollpaneWidget.h"
#include "../widgets/ButtonWidget.h"
#include "../widgets/TableWidget.h"
#include "../widgets/DefaultTableModel.h"
#include "../widgets/TextPaneWidget.h"

#include "../server/Player.h"
#include "../server/Character.h"
#include "../server/Item.h"

#include <SDL/SDL_mixer.h>

Mix_Chunk * pickup_wav = 0;
Mix_Chunk * drop_wav = 0;

CharacterScreen::CharacterScreen( int x, int y )
        : GenericScreen( x, y, Client::CLIENT_MENU_MAIN, Client::CLIENT_MENU_MAIN ) {

        if ( drop_wav == 0 )
                drop_wav = Mix_LoadWAV( "lib/sfx/drop.wav" );
        if ( pickup_wav == 0 )
                pickup_wav = Mix_LoadWAV( "lib/sfx/pickup.wav" );

        _titleBar = new LabelWidget( x, 32, "" );

        add( _titleBar, 0, y - _titleBar->getSize()[1] );

        _inventory = new InventoryWidget( x / 3, y / 6, 
                                          3, 8,
                                          4, 4 );
        add( _inventory, x / 3, 0 );

        _paperDoll = new PaperDollWidget( x / 3, y - y / 6 - _titleBar->getSize()[1] );

        add( _paperDoll, x / 3, y / 6 );

        _statsWidget = 0;
        _pointsWidget = 0;
        _closeButton = 0;
        _skillsPanel = 0;
        _infoPanel = 0;

        _statsWidget = new StatsWidget( x / 3, y / 3 );

        add( _statsWidget, 0, _titleBar->getPos()[1] - y / 3 );

        _pointsWidget = new PointsWidget( x / 3, _statsWidget->getPos()[1] );

        add( _pointsWidget, 0, 0 );

        _closeButton = new ButtonWidget( x / 6, 30, "Close" );
        add( _closeButton, x - _closeButton->getSize()[0], 0 );

        _skillsPanel = new SkillsPanel( x / 3, y / 3 );

        add( _skillsPanel, x * 2 / 3, _titleBar->getPos()[1] - _skillsPanel->getSize()[1] );

        _infoPanel = new InfoPanel( x / 3, _skillsPanel->getPos()[1] - _closeButton->getSize()[1] );

        add( _infoPanel, x * 2 / 3, _closeButton->getSize()[1] );

        setOpaque( true );

        updateWidget();
}

CharacterScreen::~CharacterScreen() {
        if ( _paperDoll )    delete _paperDoll;
        if ( _inventory )    delete _inventory;
        if ( _titleBar )     delete _titleBar;
        if ( _statsWidget )  delete _statsWidget;
        if ( _pointsWidget ) delete _pointsWidget;
        if ( _infoPanel )    delete _infoPanel;
        if ( _closeButton )  delete _closeButton;
        if ( _skillsPanel )  delete _skillsPanel;       
}

void CharacterScreen::updateWidget() {

        class Player *player = Client::getPlayer();
        
        if ( !player ) {
                return;
        }

        char buf[ 128 ];
        sprintf( buf, "%.64s the Brave, Level %.3d", player->getName(), player->getLevel() );
        
        _titleBar->setText( buf );

        //
        //
        //

        _paperDoll->updateData( player );
        _inventory->updateData( player );
        _statsWidget->updateData( player );
        _pointsWidget->updateData( player );
}

StatsWidget::StatsWidget( int x, int y ) 
        : Widget( x, y ) {

        int statsize = y / ( NUM_STATS + 1 );

        _label.resize( x, statsize );
        _label.setText( "Stats" );
        add( &_label, 0, y - statsize );

        int cur_y = y - statsize;

        for ( int i = 0; i < NUM_STATS; ++i ) {
                cur_y -= statsize;
                _statLabelLabels[ i ].resize( x / 2, statsize );
                _statLabelLabels[ i ].setText( Character::STAT_NAMES[ i ] );
                _statLabelLabels[ i ].setDrawBorder( false );
                
                add( &_statLabelLabels[i], 0, cur_y );
                
                _statLabels[i].resize( min( x / 2, 40 ),
                                        min( statsize, 20 ) );
                _statLabels[i].setFGColor4fv( getLookAndFeel()->getBlackColor() );
                _statLabels[i].setOpaque( true );

                add ( &_statLabels[i], 
                      x / 2 + ( x / 2 - _statLabels[i].getSize()[0] ) / 2,
                      cur_y + ( statsize - _statLabels[i].getSize()[1] ) / 2 );
        }
}

StatsWidget::~StatsWidget() {

}

void StatsWidget::updateData( const class Player * p ) {
        char buf[16];

        for ( int i = 0; i < NUM_STATS; ++i ) {
                sprintf( buf, "%d", p->getStat( (CharacterStat) i ) );
                _statLabels[i].setText( buf );
        }
}

PointsWidget::PointsWidget( int x, int y ) 
        : Widget( x, y ) {
        
        int statsize = y / ( NUM_POINTS + 1 );

        _label.resize( x, statsize );
        _label.setText( "Points" );
        add( &_label, 0, y - statsize );

        static char * point_names[ NUM_POINTS ] = { 
                "HP",
                "AC",
                "Gold",
                "Exp",
                "(needed)",
                "Skillpoints"
        };
   
        int cur_y = y - statsize;

        for ( int i = 0; i < NUM_POINTS; ++i ) {
                cur_y -= statsize;

                _pointLabelLabels[ i ].resize( x / 2, statsize );
                _pointLabelLabels[ i ].setText( point_names[ i ] );
                _pointLabelLabels[ i ].setDrawBorder( false );
                
                add( &_pointLabelLabels[i], 0, cur_y );

                _pointLabels[i].resize( min( x / 2, 40 ),
                                        min( statsize, 20 ) );
                _pointLabels[i].setFGColor4fv( getLookAndFeel()->getBlackColor() );
                _pointLabels[i].setOpaque( true );

                add ( &_pointLabels[i], 
                      x / 2 + ( x / 2 - _pointLabels[i].getSize()[0] ) / 2,
                      cur_y + ( statsize - _pointLabels[i].getSize()[1] ) / 2 );   
        }
}

PointsWidget::~PointsWidget() {
}

void PointsWidget::updateData( const class Player * p ) {
        int points[ NUM_POINTS ] = {
                p->getCurHPs(),
                p->getAC(),
                p->getExp(),
                0,
                p->getSkillPoints()
        };

        char buf[16];
        for ( int i = 0; i < NUM_POINTS; ++i ) {
                sprintf( buf, "%d", points[i] );
                _pointLabels[i].setText( buf );
        }
}

PaperDollWidget::PaperDollWidget( int x, int y ) 
        : Widget( x, y ) {

        setBGTexture( "icons/man", GL_RGB );
        setOpaque( true );

        _numItems = (int) NUM_WORN_POSITIONS;

        _items = new ItemWidget * [ _numItems ];

        static float positions[ NUM_WORN_POSITIONS ][2] = { 
                {  61,   256 - 28 },
                {  17,   256 - 126 },
                { 113,   256 - 126 },
                {  41,   256 - 231 },
                {  81,   256 - 231 },
                {  20,   256 - 155 },
                { 111,   256 - 155 }
        };
        
        for ( int i = 0; i < _numItems; ++i ) {
                _items[i] = new ItemWidget( 32, 32, i, ItemWidget::EQUIPMENT );
                
                if ( positions[i][0] != 0 || positions[i][1] != 0 )
                        add(_items[i], positions[i][0] * x / 128 - 16, positions[i][1] * y / 256 - 16);
        }
}

PaperDollWidget::~PaperDollWidget() {
        for ( int i = 0; i < _numItems; ++i ) {
                delete _items[i];
        }

        delete[] _items;
}

void PaperDollWidget::updateData( const class Player * p ) {
        for ( int i = 0; i < _numItems; ++i ) {
                _items[i]->setItem( p->getEquippedItem( (WornPosition) i ) );
        }
}

InventoryWidget::InventoryWidget( int x, int y, int rows, int cols, int padx, int pady )
        : Widget( x, y ) {

        _rows = rows;
        _cols = cols;
        _numItems = rows * cols;

        _items = new ItemWidget * [ _numItems ];

        int cellsize[2] = { ( x - ( ( cols + 1 ) * padx ) )  / cols,
                            ( y - ( ( rows + 1 ) * pady ) )  / rows };
        
        for ( int i = 0; i < _numItems; ++i ) {
                _items[i] = new ItemWidget( cellsize[0], cellsize[1], i, ItemWidget::INVENTORY );

                int pos[2] = { ( i % _cols ) * ( cellsize[0] + padx ) + padx,
                               ( i / _cols ) * ( cellsize[1] + pady ) + pady };
                
                add( _items[i], pos[0], pos[1] );
        }
}

InventoryWidget::~InventoryWidget() {
        for ( int i = 0; i < _numItems; ++i ) {
                delete _items[i];
        }
        
        delete[] _items;
}    

void InventoryWidget::updateData( const class Player * p ) {

        class Item * inventory[ Character::NUM_INVENTORY_SLOTS ];
        
        p->getInventorySlots( inventory );
        
        for ( int i = 0; i < _numItems; ++i ) {
                _items[i]->setItem( inventory[i] );
        }
}


ItemWidget::ItemWidget( int x, int y, int index, ItemWidgetType type ) 
        :  Widget( x, y ),
           _item( 0 ),
           _index( index ),
           _type( type )
{
        setOpaque( true );
        setBGColor4fv( getLookAndFeel()->getWhiteColor() );
}

class Item * ItemWidget::setItem( class Item * item ) {
        class Item * oldItem = _item;
        _item = item;
        setBGTexture( _item ? _item->getIconName() : 0 , GL_RGBA );
        return oldItem;
}

class Draggable * ItemWidget::performDNDPickup( DNDGestureEvent * evt ) {

        if ( _item == 0 ) {
                return 0;
        }
        
        class Draggable * d = new Draggable( "icon", getBGTexture(), _item );
        setItem( 0 );
        
        Mix_PlayChannel( -1, pickup_wav, 0 );
        return d;
}

bool ItemWidget::performDNDDrop(   DNDGestureEvent * evt, class Draggable ** pickup ) {

        if ( _item ) {
                Mix_PlayChannel( -1, pickup_wav, 0 );
                *pickup = new Draggable( "icon", getBGTexture(), _item );
        }
                
        class Item * dropItem = (class Item*) evt->draggable->getObject();
        
        class Player * player = Client::getPlayer();

        class Item * inItem = 0;
        class Character * ch = 0;
        
        int success = false;
        
        switch ( _type ) {
        case INVENTORY:
                
                //
                // item is in a container
                //

                if ( ( inItem = dropItem->getContainerItem() ) ) {
                        cout << " containers not supported" << endl;
                }

                //
                // item is carried
                //

                else if ( ( ch = dropItem->getCarriedBy() ) ) {
                        // item is carried by this player
                        if ( ch == player ) {
                                success = player->slideInventoryItem( dropItem, getIndex() );

                        }
                        // item is carried by another player (a 'give' action)
                        else {
                                cout << " give not supported" << endl;
                        }
                }

                //
                // item is equipped/worn
                //

                else if ( ( ch = dropItem->getWornBy() ) ) {
                        if ( ch != player ) {
                                cout << "ERROR cannot give items in equipment" << endl;
                        }

                        //
                        // trying to drop the equipped item into inventory where item already
                        // resides, so try to equip the inventory item
                        //

                        else if ( player->getInventoryItemAt( getIndex() ) ) {
                                success = player->reEquipItem( player->getInventoryItemAt( getIndex() ), 
                                                               dropItem->getWornPos() );
                        }
                        
                        //
                        // inventory slot is open, simply unequip
                        //
                        
                        else {
                                success = player->unequipItem( dropItem, getIndex() );
                        }
                        
                }
                break;
        case EQUIPMENT:
                
                //
                // item is carried
                //

                if ( ( ch = dropItem->getCarriedBy() ) ) {
                        if ( ch != player ) {
                                cout << "ERROR cannot equip items from give" << endl;
                        } else {
                                success = player->reEquipItem( dropItem, (WornPosition)getIndex() );
                        }
                }
                
                //
                // item is worn
                //

                else if ( ( ch = dropItem->getWornBy() ) ) {
                        if ( ch != player ) {
                                cout << "ERROR cannot equip items from give from worn!!" << endl;
                        } else {
                                success = player->reEquipItem( dropItem, (WornPosition)getIndex() );
                        }
                }
                break;
        }
        
        if ( ! success ) {
                if ( *pickup ) {
                        delete *pickup;
                        *pickup = 0;
                }
                return false;
        }

        setItem( dropItem );
        delete evt->draggable;
        evt->draggable = 0;
        Mix_PlayChannel( -1, drop_wav, 0 );
        return true;
}

ItemWidget::~ItemWidget() {
}

InfoPanel::InfoPanel( int x, int y )
        : Widget( x, y ) {

        _info = 0;
        
        _panelLabel = new LabelWidget( x, 25, "Info" );
        
        add( _panelLabel, 0, y - _panelLabel->getSize()[1] );

        _infoLabel = new LabelWidget( x, 25, "Item name" );

        int imsize[2];
        
        TextureObject * to = TextureManager::getTexture( "icons/man", GL_RGB );
        
        imsize[0] = min( x, to->getSize()[0] );
        imsize[1] = imsize[0] * to->getSize()[1] / to->getSize()[0];
        
        _imageWidget = new Widget( imsize[0], imsize[1] );
        
        _imageWidget->setBGColor4fv( getLookAndFeel()->getWhiteColor() );
        _imageWidget->setOpaque( true );
        _imageWidget->setBGTexture( to );

        _textPane = new TextPaneWidget( x, 50 );
        _textPane->appendText( " dummy text\nline 2\nline 3\n\n\nline 4\n" );

        _info = new Widget( x, 
                            _infoLabel->getSize()[1] + _imageWidget->getSize()[1] + _textPane->getSize()[1] );
        _info->setOpaque( true );

        _info->add( _infoLabel, 0, _info->getSize()[1] - _infoLabel->getSize()[1] );
        _info->add( _imageWidget, 
                    ( x - _imageWidget->getSize()[0] ) / 2,
                    _infoLabel->getPos()[1] - _imageWidget->getSize()[1] );

        _info->add( _textPane, 0, 0 );


        _scroll = new ScrollpaneWidget( x, _panelLabel->getPos()[1],
                                        _info );
        
        _scroll->scrollTo( 0, _info->getSize()[1] );

        add( _scroll, 0, 0 );
}

InfoPanel::~InfoPanel() {
        delete _info;
        delete _infoLabel;
        delete _imageWidget;
        delete _textPane;
        delete _scroll;
        delete _panelLabel;
}

SkillsPanel::SkillsPanel( int x, int y )
        : Widget( x, y ) {

        _label = new LabelWidget( x, 25, "Skills" );

        add( _label, 0, y - _label->getSize()[1] );

        static char * names[] = { "Skill Name", "Level" };
        enum TableCellType types[] = { TABLE_CELL_CHAR_PTR, TABLE_CELL_CHAR_PTR };
        static const void * values[12][2] = {
                { "00 Melee",      "50" },
                { "01 Ranged",     "80" },
                { "02 Parry",      "20" },
                { "03 Shield",     "30" },
                { "04 Flatulence", "99" },
                { "05 Fantastic",  "100" },
                { "06 Dummy 1",    "5" },
                { "07 Dummy 2",    "15" },
                { "08 Dummy 3",    "25" },
                { "09 Dummy 4",    "35" },
                { "10 Dummy 5",    "45" },
                { "11 Dummy 6",    "55" }
        };
        
        _model = new DefaultTableModel( 12, 2,
                                        (const char **) names, 
                                        types, 
                                        (void **) values  );
        
        _table = new TableWidget( x - 16, 12 * 20, _model );

        int widths[2] = { _table->getSize()[0] * 3 / 4,
                          _table->getSize()[0] / 4 };

        _table->setColumnWidths( widths );
        
        class TableHeader * header = _table->getHeader();

        add( header, 0, _label->getPos()[1] - header->getSize()[1] );
        
        _scroll = new ScrollpaneWidget( x, header->getPos()[1], _table );
        
        _scroll->scrollTo( 0, _table->getSize()[1] );

        add( _scroll, 0, 0 );
}

SkillsPanel::~SkillsPanel() {
        delete _scroll;
        delete _label;
        delete _table;
        delete _model;
}

