/*   Copyright 2005,2006 Paweł Niegowski
*
*    This file is part of Fenrir.
*
*    Fenrir 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.
*
*    Fenrir 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 Fenrir; if not, write to the Free Software
*    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "gui.h"
#include "main.h"
#include "player.h"
#include "network.h"
#include "palette.h"
#include "play.h"
#include "lookup.h"
#include "item.h"
#include "skill.h"
#include <math.h>
#include <list>
#define X_EXTEND 8
#define Y_EXTEND 2


using namespace std;

list<GUIElement*> gui_list;
BITMAP *corners = 0, *inv_tab = 0;
GUIBar *hpbar = 0, *mpbar = 0;
GUIElement *active = 0;

GUIMenu *main_menu = 0;
GUIItemList *menu_item = 0;
GUIUpButton *str_upbutton = 0, *vit_upbutton, *dex_upbutton, *agi_upbutton, *int_upbutton, *wil_upbutton;
int gui_recolor = 0;
bool gui_recolor_change = false;


GUIElement::GUIElement(int _x, int _y, int _w, int _h, int _dx, int _dy, int d)
:x(_x),y(_y),w(_w),h(_h),dialog(d),remove(false), remove_at_dest(false), x_speed(X_SPEED), y_speed(Y_SPEED)
{
 nx = dx = _dx;
 ny = dy = _dy;
 gui_list.push_back(this);
}

void GUIElement::think()
{
 if(ABS(dx-x) < x_speed) x = dx; else if(dx > x) x += x_speed; else x -= x_speed;
 if(ABS(dy-y) < y_speed) y = dy; else if(dy > y) y += y_speed; else y -= y_speed;
 if(remove_at_dest && x == dx && y == dy) remove = true;
}

GUIWindow::GUIWindow(int _x, int _y, int _w, int _h, int _dx, int _dy, int d, bool _fill)
: GUIElement(_x,_y,_w,_h,_dx,_dy,d), fill(_fill)
{
// current_x = _x+_w/2-4; current_y = _y+_h/2-4;
 dest_w = _w; dest_h = _h;
 current_w = 8; current_h = 8;
}

void GUIWindow::think()
{
 GUIElement::think();
 if(ABS(current_w - dest_w) < (w/8+1)) current_w = dest_w; else if(current_w < dest_w) current_w += (w/8+1); else  current_w -= (w/8+1);
 if(ABS(current_h - dest_h) < (h/8+1)) current_h = dest_h; else if(current_h < dest_h) current_h += (h/8+1); else  current_h -= (h/8+1);
// if(x < 0) x = 0; if(x + w > buf->w) x = buf->w - w;
// if(y < 0) y = 0; if(y + h > buf->h) y = buf->h - h;
}

void GUIWindow::draw()
{
 int scr_x,scr_y,scr_x2,scr_y2;
 scr_x = x+(w-current_w)/2; scr_y = y+(h-current_h)/2;
 scr_x2 = scr_x + current_w; scr_y2 = scr_y + current_h;
 int step = ((int)((scr_y2-scr_y-4)/16.0))+1;
 if(scr_x < 0) scr_x = 0; if(scr_y < 0) scr_y = 0;
 if(scr_x2 < 1 || scr_y2 < 1 || scr_x >= buf->w-1 || scr_y >= buf->h-1) return;
 if(scr_x2 >= buf->w) scr_x2 = buf->w - 1;
 if(scr_y2 >= buf->h) scr_y2 = buf->h - 1;
 masked_blit(corners,buf,0,0,scr_x,scr_y,4,4);
 masked_blit(corners,buf,4,0,scr_x2-2,scr_y,4,4);
 masked_blit(corners,buf,0,4,scr_x,scr_y2-2,4,4);
 masked_blit(corners,buf,4,4,scr_x2-2,scr_y2-2,4,4);

 for(int i=scr_x+2; i < scr_x2-2; i++)
 {
  buf->line[scr_y][i] = buf->line[scr_y2][i] = 252;
  buf->line[scr_y+1][i] = buf->line[scr_y2-1][i] = 253;
 }
 if(fill)
  for(int i=scr_y+2; i<scr_y2-1; i++)
  {
   buf->line[i][scr_x] = buf->line[i][scr_x2] = 252;
   buf->line[i][scr_x+1] = buf->line[i][scr_x2-1] = 253;
   hline(buf,scr_x+2,i,scr_x2-2,224+((i-2-(y+(h-current_h)/2))/step));
  }
 else
  for(int i=scr_y+2; i<scr_y2-1; i++)
  {
   buf->line[i][scr_x] = buf->line[i][scr_x2] = 252;
   buf->line[i][scr_x+1] = buf->line[i][scr_x2-1] = 253;
  }
}

GUIBar::GUIBar(int _x, int _y, int _w, int _dx, int _dy, int c, unsigned int *t, unsigned int m, int d)
: GUIElement(_x,_y,_w,2,_dx,_dy,d)
{
 color = c;
 target = t;
 max = m;
 current_pos = 0;
 dest_pos = w;
}

void GUIBar::think()
{
 GUIElement::think();
 if(current_pos < dest_pos) current_pos++; else if(current_pos > dest_pos) current_pos--;
 dest_pos = (int)((float)(*target)/max * w);
 if(dest_pos > w) dest_pos = 0;
}

void GUIBar::draw()
{
 hline(buf,x,y,w,254);
 hline(buf,x,y,x+current_pos,color);
 hline(buf,x,y+1,x+current_pos,color-1);
}

GUIText::GUIText(int _x, int _y, int _w, int _h, int _dx, int _dy, int c, char *t, int d)
: GUIElement(_x,_y,_w,_h,_dx,_dy,d)
{
 if(strlen(t) >= GUI_MAX_TEXT) allegro_message("ERROR: too long string passed to GUIText\n");
 strcpy(text,t);
 color = c;
}

void GUIText::think()
{
 GUIElement::think();
}

void GUIText::draw()
{
 textout_ex(buf,font,text,x+2,y+2,0,-1);
 textout_ex(buf,font,text,x,y,color,-1);
}

GUINumber::GUINumber(int _x, int _y, int _w, int _h, int _dx, int _dy, int c, unsigned int *t, int d)
: GUIElement(_x,_y,_w,_h,_dx,_dy,d)
{
 color = c;
 target_int = t;
 target_short = 0;
}

GUINumber::GUINumber(int _x, int _y, int _w, int _h, int _dx, int _dy, int c, unsigned short *t, int d)
: GUIElement(_x,_y,_w,_h,_dx,_dy,d)
{
 color = c;
 target_int = 0;
 target_short = t;
}

void GUINumber::think()
{
 GUIElement::think();
}

void GUINumber::draw()
{
 char text[10];
 if(target_int)
  sprintf(text,"%d",*target_int); else
 if(target_short)
  sprintf(text,"%d",*target_short);

 textout_ex(buf,font,text,x+2,y+2,0,-1);
 textout_ex(buf,font,text,x,y,color,-1);
}

GUIImage::GUIImage(int _x, int _y, int _dx, int _dy, BITMAP *img, int d)
: GUIElement(_x,_y,img->w,img->h,_dx,_dy,d)
{
 image = img;
}

void GUIImage::think()
{
 GUIElement::think();
}

void GUIImage::draw()
{
 draw_sprite(buf,image,x,y);
}

GUIMenu::GUIMenu(int _x, int _y, int _w,int _h, int _dx, int _dy, int d)
: GUIElement(_x,_y,_w,_h,_dx,_dy,d)
{
 selection_valid = false;
}

void GUIMenu::think()
{
 GUIElement::think();
}

void GUIMenu::draw()
{
 int num = elements.size();
 if(!num) return;
 int _y = y;
 for(list<_MenuElement>::iterator it = elements.begin(); it != elements.end(); it++)
 {
   textout_ex(buf,font,(*it).name,x+2,_y+2 - font_height/2,254,-1);
   if(selection_valid && selection == it && active == this)
    textout_ex(buf,font,(*it).name,x,_y - font_height/2,255,-1);
   else
    textout_ex(buf,font,(*it).name,x,_y - font_height/2,(*it).func?252:254,-1);
   _y += h / num;
 }
}

void GUIMenu::add_element(char *name, void (*func)())
{
 elements.push_front(_MenuElement());
 list<_MenuElement>::iterator it = elements.begin();
 strcpy((*it).name,name);
 (*it).func = func;
 if(func)
 {
  selection = it;
  selection_valid = true;
 }
}

void GUIMenu::handle_keypress(int key)
{
 if(!selection_valid) return;
 if(key == keymap[KEYMAP_X] || key == keymap[KEYMAP_A])
  (*selection).func();
 if(key == keymap[KEYMAP_DOWN])
  do
  {
   selection++;
   if(selection == elements.end()) selection = elements.begin();
  }
  while(!(*selection).func);
 if(key == keymap[KEYMAP_UP])
  do
  {
   if(selection == elements.begin()) selection = elements.end();
   selection--;
  }
  while(!(*selection).func);
}

GUIUpButton::GUIUpButton(GUINumber *t, bool (*cf)(GUINumber *t), void (*uf)(), GUIUpButton *p)
: GUIElement(t->x+t->w,t->y,48,t->h,t->dx+t->w,t->dy,t->dialog), check_func(cf), up_func(uf), target(t), prev(0),next(0), counter(0)
{
 if(p) { prev = p; p->next = this; }
}

void GUIUpButton::think()
{
 GUIElement::think();
 counter++;
}

void GUIUpButton::draw()
{
 if(active == this)
  draw_sprite(buf,(BITMAP*)maindata[DATA_ARROW].dat,x,y+(int)(sin(counter/10.0)*5.0));
 else
  draw_sprite(buf,(BITMAP*)maindata[DATA_ARROW].dat,x,y);
}

void GUIUpButton::handle_keypress(int key)
{
 if(key == keymap[KEYMAP_X] || key == keymap[KEYMAP_A])
 {
  if(check_func && !check_func(target)) return;
  if(target->target_int) (*target->target_int)++;
  if(target->target_short) (*target->target_short)++;
  if(up_func) up_func();
 }
 if(key == keymap[KEYMAP_Y])
  active = main_menu;
 if(key == keymap[KEYMAP_DOWN] && next)
  active = next;
 if(key == keymap[KEYMAP_UP] && prev)
  active = prev;

}

GUIAuraDisplay::GUIAuraDisplay(int _x, int _y, int _dx, int _dy, int d)
: GUIElement(_x,_y,6,12,_dx,_dy,d)
{

}


void GUIAuraDisplay::think()
{
 GUIElement::think();
 if(rand()%5000 < energy)
 {
  bubbles_r.push_back(_AuraBubble(rand()%10/100.0 + 0.02, rand()%2));
  bubbles_g.push_back(_AuraBubble(rand()%10/100.0 + 0.02, rand()%2+2));
  bubbles_b.push_back(_AuraBubble(rand()%10/100.0 + 0.02, rand()%2+4));
 }

 for(list<_AuraBubble>::iterator it = bubbles_r.begin(); it != bubbles_r.end(); it++)
  (*it).h += (*it).v;
 for(list<_AuraBubble>::iterator it = bubbles_r.begin(); it != bubbles_r.end(); it++)
  if((*it).h > h) { bubbles_r.erase(it); it = bubbles_r.begin(); }

 for(list<_AuraBubble>::iterator it = bubbles_g.begin(); it != bubbles_g.end(); it++)
  (*it).h += (*it).v;
 for(list<_AuraBubble>::iterator it = bubbles_g.begin(); it != bubbles_g.end(); it++)
  if((*it).h > h) { bubbles_g.erase(it); it = bubbles_g.begin(); }

 for(list<_AuraBubble>::iterator it = bubbles_b.begin(); it != bubbles_b.end(); it++)
  (*it).h += (*it).v;
 for(list<_AuraBubble>::iterator it = bubbles_b.begin(); it != bubbles_b.end(); it++)
  if((*it).h > h) { bubbles_b.erase(it); it = bubbles_b.begin(); }
}

void GUIAuraDisplay::draw()
{
//blue aura is reduced by 1 when displaying, so the 33/33/34 setting (default) is in one line
 rectfill(buf,x,y,x+w-1,y+h,254);
 rect(buf,x,y+(int)(h - h*aura_current[0]/100.0),x+1,y+h,247);
 rect(buf,x+2,y+(int)(h - h*aura_current[1]/100.0),x+3,y+h,241);
 rect(buf,x+4,y+(int)(h - h*(aura_current[2]-1)/100.0),x+5,y+h,245);
 int _y = 0;
 if(y < 0 || x < 0) return;

 _y = y + (int)(h - h*aura_target[0]/100.0);
 buf->line[_y][x] = buf->line[_y][x+1] = 248;
 for(list<_AuraBubble>::iterator it = bubbles_r.begin(); it != bubbles_r.end(); it++)
  if((*it).h < h*aura_target[0]/100.0)
  { buf->line[h - (int)(*it).h + y][x+(*it).offset] = 248; }

 _y = y + (int)(h - h*aura_target[1]/100.0);
 buf->line[_y][x+2] = buf->line[_y][x+3] = 242;
 for(list<_AuraBubble>::iterator it = bubbles_g.begin(); it != bubbles_g.end(); it++)
  if((*it).h < h*aura_target[1]/100.0)
  { buf->line[h - (int)(*it).h + y][x+(*it).offset] = 242; }

 _y = y + (int)(h - h*(aura_target[2]-1)/100.0);
 buf->line[_y][x+4] = buf->line[_y][x+5] = 246;
 for(list<_AuraBubble>::iterator it = bubbles_b.begin(); it != bubbles_b.end(); it++)
  if((*it).h < h*(aura_target[2]-1)/100.0)
  { buf->line[h - (int)(*it).h + y][x+(*it).offset] = 246; }


}

//a small macro to avoid multiple if's in actual code
#define INVENTORY (selected_inv==INV_USE?inventory_use:(selected_inv==INV_LOOT?inventory_loot:inventory_etc))

GUIItemList::GUIItemList(int _x, int _y, int _w, int _h, int _dx, int _dy, int d)
: GUIElement(_x,_y,_w,_h,_dx,_dy,d)
{
 selected_inv = 0;
 selected_item = 0;
}

void GUIItemList::think()
{
 GUIElement::think();
 if(selected_item > (int)INVENTORY.size()) selected_item = 0;
}
char _name[50];
void GUIItemList::draw()
{
 int i = 0, color = 252;
 for(i = 0; i < 4; i++)
  masked_blit(inv_tab,buf,i*32,0,x+i*32,y-8-(selected_inv==i?4:0),32,8);
 if(selected_inv == INV_EQUIP) return;
 if(!INVENTORY.size()) return;
 i = 0;
 for(list<Item>::iterator it = INVENTORY.begin(); it != INVENTORY.end(); it++,i++)
 {
  if(selected_item == i) color = 253; else color = 252;
  item_display(selected_inv,(*it).id,(*it).quantity,x,y+font_height*i,w,color);
 }
}

void GUIItemList::handle_keypress(int k)
{
 if(k == keymap[KEYMAP_Y])
 {
  gui_hide(DIALOG_MENU_ITEM_UP,DIR_UP,true,1,4);
  gui_hide(DIALOG_MENU_ITEM_DOWN,DIR_DOWN,true,1,4);
  gui_unhide(DIALOG_MENU_MENU);
  gui_unhide(DIALOG_MENU_STAT);
  active = main_menu;
 }
 if(k == keymap[KEYMAP_L])
 {
  if(selected_inv == 0) selected_inv = INV_LOOT;
  else
  selected_inv--;
 }
 if(k == keymap[KEYMAP_R])
 {
  if(selected_inv == INV_LOOT) selected_inv = 0;
  else
  selected_inv++;
 }
 if(!INVENTORY.size()) return;
 if(k == keymap[KEYMAP_DOWN])
 {
  selected_item++;
  if((unsigned)selected_item >= INVENTORY.size())
   selected_item = 0;
 }
 if(k == keymap[KEYMAP_UP])
 {
  selected_item--;
  if(selected_item < 0)
   selected_item = INVENTORY.size()-1;
 }
 if(k == keymap[KEYMAP_X])
 {
  if(selected_inv == INV_USE)
  {
   list<Item>::iterator itemit = inventory_use.begin();
   for(int j = 0; j < selected_item; j++)
    itemit++;
   item_use((*itemit).id);
  }
  if(selected_inv == INV_LOOT)
  {
   list<Item>::iterator itemit = inventory_loot.begin();
   for(int j = 0; j < selected_item; j++)
    itemit++;
   new GUIWindow(32,38,256,100,32,38,DIALOG_LOOT);
   new GUIWindow(32,138,256,25,32,138,DIALOG_LOOT);
   active = new GUILootDialog(35,40,35,40,DIALOG_LOOT,(*itemit).id,(*itemit).quantity);
  }
 }
 //hotkey binding
 if(k == keymap[KEYMAP_A] && selected_inv == INV_USE)
 {
  new GUIWindow(110,78,106,44,110,78,DIALOG_HOTKEY);
  unsigned char msg[5];
  list<Item>::iterator itemit = inventory_use.begin();
  for(int j = 0; j < selected_item; j++)
   itemit++;
  msg[0] = ID_HOTKEY_LIST;
  memcpy(msg+3,&((*itemit).id),2);
  msg[2] = HOTKEY_ITEM;
  active = new GUIHotkeySelection(114,82,114,82,DIALOG_HOTKEY,this,msg,5);
 }
}

GUIItemDescription::GUIItemDescription(int _x, int _y, int _w, int _h, int _dx, int _dy, int d)
: GUIElement(_x,_y,_w,_h,_dx,_dy,d)
{

}

void GUIItemDescription::think()
{
 GUIElement::think();
}

void GUIItemDescription::draw()
{
 if(!INVENTORY.size()) return;
 int i = 0;
 list<Item>::iterator itemit = INVENTORY.begin();
 for(int j = 0; j < selected_item; j++)
  itemit++;
 unsigned short index = (*itemit).id;
 if(selected_inv == INV_USE)
 {
  for(list<_DescriptionLine>::iterator it = item_use_db[index].description.begin(); it != item_use_db[index].description.end(); it++,i++)
  {
   textprintf_ex(buf,font,x+2,y+font_height*i+2,254,-1,(*it).d);
   textprintf_ex(buf,font,x,y+font_height*i,253,-1,(*it).d);
  }
 } else
 if(selected_inv == INV_ETC)
 {
  for(list<_DescriptionLine>::iterator it = item_etc_db[index].description.begin(); it != item_etc_db[index].description.end(); it++,i++)
  {
   textprintf_ex(buf,font,x+2,y+font_height*i+2,254,-1,(*it).d);
   textprintf_ex(buf,font,x,y+font_height*i,253,-1,(*it).d);
  }
 } else
 if(selected_inv == INV_LOOT)
 {
  textprintf_ex(buf,font,x+2,y+font_height*i+2,254,-1,"Loot dropped by %s.",monster_db[index].name);
  textprintf_ex(buf,font,x,y+font_height*i,253,-1,"Loot dropped by %s.",monster_db[index].name);
 }
}

GUIHotkeySelection::GUIHotkeySelection(int _x, int _y, int _dx, int _dy, int d, GUIElement *p, unsigned char *pack, int pl)
: GUIElement(_x,_y,96,36,_dx,_dy,d), parent(p), plength(pl)
{
 memcpy(packet,pack,plength);
 timer = selection = 0;
}

void GUIHotkeySelection::think()
{
 GUIElement::think();
 timer++;
}

void GUIHotkeySelection::draw()
{
 if(timer > 24)
 {
  rectfill(buf,x,y,x+w,y+24,254);
  rect(buf,x + selection%8 * 12,y + selection/8*12,x + selection%8 * 12 + 12,y + selection/8*12 + 12,208 + timer%16);
  for(int i = 0; i < 16; i++)
   if(hotkeys[i].type == HOTKEY_ITEM)
   {
    draw_sprite(buf,(BITMAP*)itemuse[item_use_db[hotkeys[i].id].datanum].dat,x + i%8 * 12,y + i/8*12);
   }
  masked_blit((BITMAP*)maindata[DATA_BUTTONS].dat,buf,0,0,x,y+24,96,12);
 }
 else
  rectfill(buf,x,y+12-timer/2,x+w,y+12+timer/2,254);
}

void GUIHotkeySelection::handle_keypress(int k)
{
 if(k == keymap[KEYMAP_Y])
 {
  gui_hide(dialog,DIR_UP,true,1,4);
  active = menu_item;
 }
 if(k == keymap[KEYMAP_X])
 {
  packet[1] = selection;
  network_send_packet(packet,plength);
 }
 if(k == keymap[KEYMAP_LEFT])
 { selection--;  if(selection < 0) selection = 15;}
 if(k == keymap[KEYMAP_RIGHT])
 { selection++;  if(selection > 15) selection = 0; }
 if(k == keymap[KEYMAP_UP])
 { selection -= 8; if(selection < 0) selection += 16; }
 if(k == keymap[KEYMAP_DOWN])
 { selection += 8; if(selection > 15) selection -= 16; }

}

GUIHotkeyDelays::GUIHotkeyDelays(int _x, int _y, int _dx, int _dy, int d)
: GUIElement(_x,_y,32,12,_dx,_dy,d)
{

}

void GUIHotkeyDelays::think()
{
 GUIElement::think();
}

void GUIHotkeyDelays::draw()
{
 rectfill(buf,x,y,x+32,y+12,254);
 for(int i = 0; i < 16; i++)
 if(hotkeys[i].type == HOTKEY_ITEM || hotkeys[i].type == HOTKEY_SKILL)
 {
  if(hotkeys[i].delay == 0)
   vline(buf,x + i % 8 * 4 + 2, y + i/8 * 6, y + i/8 * 6 + 5, hotkey_colors[i]+16);
   rect(buf,x + i%8 * 4 + 1,        y + i/8 * 6 + (int)(hotkeys[i].delay * 6.0/(hotkeys[i].orig_delay+1)),
            x + i%8 * 4 + 3,      y + i/8*6 + 6,
            hotkey_colors[i]);
 }

}


GUILootDialog::GUILootDialog(int _x, int _y, int _dx, int _dy, int d, unsigned short id, unsigned short quantity)
: GUIElement(_x,_y,250,120,_dx,_dy,d), i(id), q(quantity), num(1), timer(0)
{
}

void GUILootDialog::think()
{
 GUIElement::think();
 timer++;
 if(num > q) num = q;
}

void GUILootDialog::draw()
{
 if(timer < 16) return;
// rect(buf,x,y,x+w,y+h,255);
 item_display(INV_LOOT,i,q,x,y+h-font_height-5,w,253);
 int n = 0;
 for(list<_LootResult>::iterator it = results.begin(); it != results.end(); it++,n++)
  item_display((*it).inv,(*it).id,(*it).q,x,y + 5 + font_height * n,w,253);
 if(!q) return;
 if(num > 1) {
  textprintf_ex(buf,font,x+w+2 - 80 - (int)(sin(timer/8.0) * 2.0), y + h - font_height - 3, 254, -1, "<");
  textprintf_ex(buf,font,x+w - 80 - (int)(sin(timer/8.0) * 2.0), y + h - font_height - 5, 253, -1, "<");
 }
 if(num < q) {
  textprintf_ex(buf,font,x+w+2 - 55 + (int)(sin(timer/8.0) * 2.0), y + h - font_height - 3, 254, -1, ">");
  textprintf_ex(buf,font,x+w - 55 + (int)(sin(timer/8.0) * 2.0), y + h - font_height - 5, 253, -1, ">");
 }
 textprintf_centre_ex(buf,font,x+w+2 - 65, y + h - font_height - 3, 254, -1, "%d",num);
 textprintf_centre_ex(buf,font,x+w - 65, y + h - font_height - 5, 253, -1, "%d",num);
}

void GUILootDialog::handle_keypress(int k)
{
 if(k == keymap[KEYMAP_Y])
  { gui_hide(dialog,DIR_DOWN,1,4); active = menu_item; }
 if(!q) return;
 if(k == keymap[KEYMAP_X])
 {
  unsigned char msg[5];
  msg[0] = ID_ITEMLOOT;
  memcpy(msg + 1,&i,2);
  memcpy(msg + 3,&num,2);
  network_send_packet(msg,5);
 }
 if(k == keymap[KEYMAP_A])
 {
  unsigned char msg[5];
  msg[0] = ID_ITEMLOOT;
  memcpy(msg + 1,&i,2);
  memcpy(msg + 3,&q,2);
  network_send_packet(msg,5);
 }
 if(k == keymap[KEYMAP_LEFT])
  { num--; if(num < 1) num = 1; }
 if(k == keymap[KEYMAP_RIGHT])
  { num++; if(num > q) num = q; }
}

void GUILootDialog::handle_packet(unsigned char *msg, int l)
{
 unsigned short id,quantity;
 if(msg[0] == ID_ITEMDEL)
 {
  memcpy(&id,msg+2,2);
  memcpy(&quantity,msg+4,2);
  if(id == i) q -= quantity;
  return;
 }
 //check if additional loot items didn't arrive while the dialog was opened
 if(msg[0] == ID_ITEMLOOT)
 {
  memcpy(&id,msg+1,2);
  memcpy(&quantity,msg+3,2);
  if(id == i) q += quantity;
  return;
 }
 if(msg[0] == ID_ITEMUSE || msg[0] == ID_ITEMETC)
 {
  memcpy(&id,msg+1,2);
  memcpy(&quantity,msg+3,2);
  int inv = (msg[0] == ID_ITEMUSE) ? INV_USE : INV_ETC;
  for(list<_LootResult>::iterator it = results.begin(); it != results.end(); it++)
   if((*it).inv == inv && (*it).id == id)
     { (*it).q += quantity; return; }
  results.push_back(_LootResult(inv,id,quantity));
  return;
 }
}
/*
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */


int gui_fade = 0;

void gui_init()
{
 corners = (BITMAP*)maindata[DATA_CORNERS].dat;
 inv_tab = (BITMAP*)maindata[DATA_TAB].dat;
 gui_fade = 0;
}

void gui_think()
{
 if(combat_mode && gui_recolor < 60) { gui_recolor++; gui_recolor_change = true; }
 if(!combat_mode && gui_recolor > 0) { gui_recolor--; gui_recolor_change = true; }
 if(menu_active && gui_fade < 40) { gui_fade++; }
 if(!menu_active && gui_fade > 0) { gui_fade--; }

 for(list<GUIElement*>::iterator it = gui_list.begin(); it != gui_list.end(); it++)
  (*it)->think();

 for(list<GUIElement*>::iterator it = gui_list.begin(); it != gui_list.end(); it++)
  if((*it)->remove)
  {
   delete (*it);
   gui_list.erase(it);
   it = gui_list.begin();
  }
}


void gui_draw()
{
 if(gui_fade)
 {
  drawing_mode(DRAW_MODE_TRANS,NULL,0,0);
  color_map = trans_map+TRANS_LEVELS;
  rectfill(buf,0,buf->h/2-gui_fade*5/2,buf->w,buf->h/2+gui_fade*5/2,156);
  solid_mode();
 }
 for(list<GUIElement*>::iterator it = gui_list.begin(); it != gui_list.end(); it++)
  (*it)->draw();
 if(gui_recolor_change)
 { gui_recolor_change = false; gui_loadpalette();  set_palette_range(current_palette,224,240,0); }
}

void gui_cleanup()
{
 for(list<GUIElement*>::iterator it = gui_list.begin(); it != gui_list.end(); it++)
  delete (*it);
 gui_list.clear();
}

void gui_handle_keypress(int key)
{
 if(active)
  active->handle_keypress(key);
}

void gui_handle_packet(unsigned char *msg, int l)
{
 if(active)
  active->handle_packet(msg,l);
}

int gui_get_help_page()
{
 if(active)
  return active->get_help_page();
 return 0;
}

void gui_test_dialog()
{
 new GUIWindow(0,0,320,64,0,0,0);
}

void gui_hp_display()
{
 new GUIWindow(0,0,90,16,0,0,DIALOG_HP_DISPLAY);
 hpbar = new GUIBar(2,4,80,2,4,248,&(player->hp),player->mhp,DIALOG_HP_DISPLAY);
 mpbar = new GUIBar(2,8,80,2,8,246,&(player->mp),player->mmp,DIALOG_HP_DISPLAY);
 new GUIBar(2,12,80,2,12,244,&(player->st),100,DIALOG_HP_DISPLAY);
 new GUIAuraDisplay(82,2,82,2,DIALOG_HP_DISPLAY);

 new GUIWindow(90,0,36,16,90,0,DIALOG_SKILL_DISPLAY);
 new GUIHotkeyDelays(92,2,92,2,DIALOG_SKILL_DISPLAY);
}

void _menu_ITEM()
{
 gui_hide(DIALOG_MENU_MENU,DIR_RIGHT,false,5,1);
 gui_hide(DIALOG_MENU_STAT,DIR_LEFT,false,5,1);
 new GUIWindow(25,10,270,120,25,10,DIALOG_MENU_ITEM_UP,true);
 new GUIWindow(25,130,270,60,25,130,DIALOG_MENU_ITEM_DOWN,true);
 new GUIItemDescription(30,135,260,50,30,135,DIALOG_MENU_ITEM_DOWN);
 active = menu_item = new GUIItemList(30,15,260,110,30,15,DIALOG_MENU_ITEM_UP);
}

void _menu_STATUS()
{
 active = str_upbutton;
}

bool _check_stat(GUINumber *t)
{
 int val = 0;
 if(t->target_int) val = *t->target_int;
 if(t->target_short) val = *t->target_short;
 unsigned int cost = 1+val/10;
 if(free_statpoints >= cost)
 {
  free_statpoints -= cost;
  return true;
 }
 return false;

}

void _up_stat()
{
 unsigned char msg[2];
 msg[0] = ID_STATUP;
 if(active == str_upbutton) msg[1] = 0;
 if(active == vit_upbutton) msg[1] = 1;
 if(active == dex_upbutton) msg[1] = 2;
 if(active == agi_upbutton) msg[1] = 3;
 if(active == int_upbutton) msg[1] = 4;
 if(active == wil_upbutton) msg[1] = 5;
 network_send_packet(msg,2);
}

void gui_menu()
{
 new GUIWindow(240,0,80,168,240,0,DIALOG_MENU_MENU);
 main_menu = new GUIMenu(250,10,80,158,250,10,DIALOG_MENU_MENU);
 active = main_menu;
 main_menu->add_element("Config",0);
 main_menu->add_element("Guild",0);
 main_menu->add_element("Party",0);
 main_menu->add_element("Quest",0);
 main_menu->add_element("Monster",0);
 main_menu->add_element("Status",_menu_STATUS);
 main_menu->add_element("Job",0);
 main_menu->add_element("Sigil",0);
 main_menu->add_element("Skill",0);
 main_menu->add_element("Item",_menu_ITEM);

 new GUIText(-50,20,100,15,50,20,253,player->name,DIALOG_MENU_STAT);
 new GUIImage(-50,10,10,10,player->get_front_sprite(),DIALOG_MENU_STAT);

 new GUIText(-50,50,100,15,20,50,253,"Level",DIALOG_MENU_STAT);
 new GUINumber(-50,50,100,15,90,50,253,&player->level,DIALOG_MENU_STAT);

 new GUIText(-50,80,100,15,20,80,253,"Strength",DIALOG_MENU_STAT);
 new GUIText(-50,90,100,15,20,90,253,"Vitality",DIALOG_MENU_STAT);
 new GUIText(-50,100,100,15,20,100,253,"Dexterity",DIALOG_MENU_STAT);
 new GUIText(-50,110,100,15,20,110,253,"Agility",DIALOG_MENU_STAT);
 new GUIText(-50,120,100,15,20,120,253,"Intelligence",DIALOG_MENU_STAT);
 new GUIText(-50,130,100,15,20,130,253,"Will",DIALOG_MENU_STAT);

 new GUIText(-50,130,100,15,20,150,253,"Free points",DIALOG_MENU_STAT);
 new GUINumber(-50,130,20,15,90,150,253,&free_statpoints,DIALOG_MENU_STAT);

 GUINumber *n = 0;
 n = new GUINumber(-50,80,20,15,90,80,253,player->stat+ST_STR,DIALOG_MENU_STAT);
 str_upbutton = new GUIUpButton(n,_check_stat,_up_stat,0);
 n = new GUINumber(-50,90,20,15,90,90,253,player->stat+ST_VIT,DIALOG_MENU_STAT);
 vit_upbutton = new GUIUpButton(n,_check_stat,_up_stat,str_upbutton);
 n = new GUINumber(-50,100,20,15,90,100,253,player->stat+ST_DEX,DIALOG_MENU_STAT);
 dex_upbutton = new GUIUpButton(n,_check_stat,_up_stat,vit_upbutton);
 n = new GUINumber(-50,110,20,15,90,110,253,player->stat+ST_AGI,DIALOG_MENU_STAT);
 agi_upbutton = new GUIUpButton(n,_check_stat,_up_stat,dex_upbutton);
 n = new GUINumber(-50,120,20,15,90,120,253,player->stat+ST_INT,DIALOG_MENU_STAT);
 int_upbutton = new GUIUpButton(n,_check_stat,_up_stat,agi_upbutton);
 n = new GUINumber(-50,130,20,15,90,130,253,player->stat+ST_WIL,DIALOG_MENU_STAT);
 wil_upbutton = new GUIUpButton(n,_check_stat,_up_stat,int_upbutton);
 str_upbutton->prev = wil_upbutton;
 wil_upbutton->next = str_upbutton;
}

void gui_hide(int dialog_num, int dir, bool rem, int xspd, int yspd)
{
 for(list<GUIElement*>::iterator it = gui_list.begin(); it != gui_list.end(); it++)
  if((*it)->dialog == dialog_num)
  {
   if(rem) (*it)->remove_at_dest = true;
   (*it)->x_speed = xspd;
   (*it)->y_speed = yspd;
   switch(dir)
   {
    case DIR_UP: (*it)->dy = -((*it)->h)-1; break;
    case DIR_DOWN: (*it)->dy = buf->h+1; break;
    case DIR_LEFT: (*it)->dx = -((*it)->w)-1; break;
    case DIR_RIGHT: (*it)->dx = buf->w+1; break;
   }
  }
}

void gui_unhide(int dialog_num)
{
 for(list<GUIElement*>::iterator it = gui_list.begin(); it != gui_list.end(); it++)
  if((*it)->dialog == dialog_num)
  { (*it)->dx = (*it)->nx; (*it)->dy = (*it)->ny; }
}

void gui_loadpalette()
{
 for(int i=224; i<240; i++)
 {
  base_palette[i].r = current_palette[i].r = (int)((239-i) * 3.0 * gui_recolor/61.0);
  base_palette[i].g = current_palette[i].g = (int)((239-i) * 2.0 * (60-gui_recolor)/61.0);;
  base_palette[i].b = current_palette[i].b = (int)((239-i) * 3.0 * (60-gui_recolor)/61.0);
 }
}
