/*  items.c: (c) 2002 sibn

    This file is part of GWiz.

    GWiz 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.

    GWiz 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 GWiz; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_ttf.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <netinet/in.h>
#include <dirent.h>
#include "gwiz.h"
#include "uiloop.h"
#include "prefsdat.h"
#include "text.h"
#include "maploader.h"
#include "menus.h"
#include "playerpawn.h"
#include "castle.h"
#include "playergen.h"
#include "inspect.h"
#include "items.h"
#include "joystick.h"
#include "party.h"
#include "items.h"

extern GwizApp gwiz;

int dropcursorposition = 0;

void LoadItem (Inventory *item, int itemno)
{
    char filename[9];  /* itemXYZ where XYZ is itemno */
    char *freeme;
    FILE *itemfile;
    
    snprintf (filename, sizeof(char)*9, "/item%d", itemno);
    freeme = MakePath (gwiz.udata.items, filename);
    
    itemfile = fopen (freeme, "rb");
    if (itemfile == NULL)
	BailCall ("items.c: unable to load item: ", freeme);
    
    if (fread (item, sizeof(Inventory), 1, itemfile) != 1)
	BailCall ("items.c: unable to read item from file: ", freeme);
    
    if (ntohl(item->version) < 0)
	BailCall ("items.c: item file reports incorrect version ",
		  freeme);
    
    item->version = ntohl (item->version);
    item->acmod   = ntohl (item->acmod);
    item->swimmod = ntohl (item->swimmod);
    item->luckmod = ntohl (item->luckmod);
    item->agimod  = ntohl (item->agimod);
    item->vitmod  = ntohl (item->vitmod);
    item->devmod  = ntohl (item->devmod);
    item->iqmod   = ntohl (item->iqmod);
    item->strmod  = ntohl (item->strmod);
    item->buyfor  = ntohl (item->buyfor);
    item->sellfor = ntohl (item->sellfor);
    item->usage   = ntohl (item->usage);
    item->special = ntohl (item->special);
    item->itemno  = ntohl (item->itemno);
    
    /* FIXME: Connect item's function handler */
    
    fclose (itemfile);
    Sfree (freeme);
}

int CountGwizItems (void)
{
    struct dirent *pdd;
    DIR *path;
    int itemcount = 0;
    int itemsuffix;
    
    path = opendir (gwiz.udata.items);
    if (path == NULL)
	BailCall ("items.c: unable to open package data dir: ",
		  gwiz.udata.items);
    while ((pdd = readdir (path)) != NULL)
	{
	    /* make sure that the filename BEGINS with "item" */
	    if (strncmp (pdd->d_name, "item", 4) == 0)
		{
		    /* make sure the item file has a numerical suffix */
		    itemsuffix = atoi (&pdd->d_name[4]);
		    if ((itemsuffix >= 1) && (itemsuffix <= 256))
			itemcount++;
		}
	}
    
    /* make it easy to use in a loop.  Warning: loop counter begins at 1 */
    if (itemcount > 0)
	itemcount++;
    
    /* FIXME:  Do we need to free() pdd, ord does closedir() do that? */
    closedir (path);
    return (itemcount);
}

void CompactPawnItems (PlayerPawn *pawn)
{
    int i;
    int j;
    for (i = 0; i < 8; i++)
	{
	    if (pawn->item[i].version != ITEM_VERSION)
		{
		    for (j = i; j < 8; j++)
			if (pawn->item[j].version == ITEM_VERSION)
			    {
				memmove (&pawn->item[i], &pawn->item[j],
					 sizeof(Inventory));
				pawn->item[j].version = -1;
				pawn->item[j].usage = USAGE_NONE;
				break;
			    }
		}
	}
}

int FirstEmptyPawnItem (PlayerPawn *pawn)
{
    int i;
    for (i = 0; i < 8; i++)
	{
	    if (pawn->item[i].version != ITEM_VERSION)
		return (i);
	}
    return (-2);
}

int GetItemNoByName (char *name)
{
    /* FIXME: strcmp() is ugly.  especially when used it this many times. */
    if (strcmp ("Short Sword", name) == 0)
	return (1);
    if (strcmp ("Cloth", name) == 0)
	return (2);
    if (strcmp ("Dagger", name) == 0)
	return (3);
    if (strcmp ("Long Sword", name) == 0)
	return (4);
    if (strcmp ("Mace", name) == 0)
	return (5);
    if (strcmp ("Robes", name) == 0)
	return (6);
    GwizLogEvent (__FILE__, __LINE__, "Unknown item number: for item: %s.\n\
 Maybe add to items.c?", name);
    fprintf (stderr, "Unknown item number. Check %s for more info\n",
	     gwiz.udata.log);
    return (-1); /* failsafe */
}

/* quick check to determine whether the item is a weapon */
int ItemIsWeapon (Inventory *item)
{
    int weapons = USAGE_SWORD |
	USAGE_MACE    |
	USAGE_DAGGER  |
	USAGE_BOW     |
	USAGE_STAFF   |
	USAGE_2X_SWORD;
    
    if ((item->usage & weapons) != 0)
	return (1);
    
    return (0);
}

int ItemIsArmor (Inventory *item)
{
    int armors = USAGE_ARMOR |
	USAGE_ROBES  |
	USAGE_LEATHER;
    if (item->usage & armors)
	return TRUE;
    return FALSE;
}

int ItemIsShield (Inventory *item)
{
    if (item->usage & USAGE_SHIELD)
	return TRUE;
    return FALSE;
}

int ItemIsHelmet (Inventory *item)
{
    if (item->usage & USAGE_HELMET)
	return TRUE;
    return FALSE;
}

int ItemIsGauntlet (Inventory *item)
{
    if (item->usage & USAGE_GAUNTLET)
	return TRUE;
    return FALSE;
}

int ItemIsMisc (Inventory *item)
{
    int miscs = USAGE_BOOT |
	USAGE_RING     |
	USAGE_AMULET   |
	USAGE_MULTIPLE |
	USAGE_SINGLE;

    if ((item->usage == USAGE_NONE) || (item->usage & miscs))
	return TRUE;
    return FALSE;
}

int ItemIsScroll (Inventory *item)
{
    int scrolls = USAGE_SCROLL | USAGE_POTION;
    if (item->usage & scrolls)
	return TRUE;
    return FALSE;
}

/* Creates a linked list out of gwiz.shop.wpn containing various weapons */
void MakeWeaponList (void)
{
    Inventory item;
    int itemcount;
    int i;
    int total = 1;
    
    itemcount = CountGwizItems();
    gwiz.shop.wpncount = 0;
    DestroyShopNodeList (&gwiz.shop.wpn);
    
    /* count begins at 1, because that's the first item. */
    for (i = 1; i < itemcount; i++)
        {
	    LoadItem (&item, i);
	    if (ItemIsWeapon(&item))
	        {
		    GwizShopNode *gsn;

		    gsn = NewGwizShopNode (strdup(item.name), item.buyfor,
					   gwiz.shopstocks[i], total);

		    AppendGwizShopNode (&gwiz.shop.wpn, gsn);

		    gwiz.shop.wpncount++;
		}
	    total++;
	}
    GwizLogEvent (__FILE__, __LINE__, "Registered num. weapons for sale: %d",
		  gwiz.shop.wpncount);
}

void MakeArmorList (void)
{
    Inventory item;
    int itemcount;
    int i;
    int total = 1;

    itemcount = CountGwizItems();
    gwiz.shop.armcount = 0;
    DestroyShopNodeList (&gwiz.shop.arm);

    for (i = 1; i < itemcount; i++)
	{
	    LoadItem (&item, i);
	    if (ItemIsArmor (&item))
		{
		    GwizShopNode *gsn;

		    gsn = NewGwizShopNode (strdup(item.name), item.buyfor,
					   gwiz.shopstocks[i], total);

		    AppendGwizShopNode (&gwiz.shop.arm, gsn);

		    gwiz.shop.armcount++;
		}
	    total++;
	}
    GwizLogEvent (__FILE__, __LINE__, "Registered num. armors for sale: %d",
		  gwiz.shop.armcount);
}

void MakeShieldList (void)
{
    Inventory item;
    int itemcount;
    int i;
    int total = 1;

    itemcount = CountGwizItems();
    gwiz.shop.shldcount = 0;
    DestroyShopNodeList (&gwiz.shop.shld);

    for (i = 1; i < itemcount; i++)
	{
	    LoadItem (&item, i);
	    if (ItemIsShield (&item))
		{
		    GwizShopNode *gsn;

		    gsn = NewGwizShopNode (strdup(item.name), item.buyfor,
					   gwiz.shopstocks[i], total);

		    AppendGwizShopNode (&gwiz.shop.shld, gsn);

		    gwiz.shop.shldcount++;
		}
	    total++;
	}
    GwizLogEvent (__FILE__, __LINE__, "Registered num. shields for sale: %d",
		  gwiz.shop.shldcount);
}

void MakeHelmetList (void)
{
    Inventory item;
    int itemcount;
    int i;
    int total = 1;

    itemcount = CountGwizItems();
    gwiz.shop.hlmcount = 0;
    DestroyShopNodeList (&gwiz.shop.hlm);

    for (i = 1; i < itemcount; i++)
	{
	    LoadItem (&item, i);
	    if (ItemIsHelmet (&item))
		{
		    GwizShopNode *gsn;

		    gsn = NewGwizShopNode (strdup(item.name), item.buyfor,
					   gwiz.shopstocks[i], total);

		    AppendGwizShopNode (&gwiz.shop.hlm, gsn);

		    gwiz.shop.hlmcount++;
		}
	    total++;
	}
    GwizLogEvent (__FILE__, __LINE__, "Registered num. helmets for sale: %d",
		  gwiz.shop.hlmcount);
}

void MakeGauntletList (void)
{
    Inventory item;
    int itemcount;
    int i;
    int total = 1;

    itemcount = CountGwizItems();
    gwiz.shop.gntcount = 0;
    DestroyShopNodeList (&gwiz.shop.gnt);

    for (i = 1; i < itemcount; i++)
	{
	    LoadItem (&item, i);
	    if (ItemIsGauntlet (&item))
		{
		    GwizShopNode *gsn;

		    gsn = NewGwizShopNode (strdup(item.name), item.buyfor,
					   gwiz.shopstocks[i], total);

		    AppendGwizShopNode (&gwiz.shop.gnt, gsn);

		    gwiz.shop.gntcount++;
		}
	    total++;
	}
    GwizLogEvent (__FILE__, __LINE__, "Registered num. gauntlet for sale: %d",
		  gwiz.shop.gntcount);
}
	    
void MakeMiscList (void)
{
    Inventory item;
    int itemcount;
    int i;
    int total = 1;

    itemcount = CountGwizItems();
    gwiz.shop.misccount = 0;
    DestroyShopNodeList (&gwiz.shop.misc);

    for (i = 1; i < itemcount; i++)
	{
	    LoadItem (&item, i);
	    if (ItemIsMisc (&item))
		{
		    GwizShopNode *gsn;

		    gsn = NewGwizShopNode (strdup(item.name), item.buyfor,
					   gwiz.shopstocks[i], total);

		    AppendGwizShopNode (&gwiz.shop.misc, gsn);

		    gwiz.shop.misccount++;
		}
	    total++;
	}
    GwizLogEvent (__FILE__, __LINE__, "Registered num. miscs. for sale: %d",
		  gwiz.shop.misccount);
}

void MakeScrollList (void)
{
    Inventory item;
    int itemcount;
    int i;
    int total = 1;

    itemcount = CountGwizItems();
    gwiz.shop.scrlcount = 0;
    DestroyShopNodeList (&gwiz.shop.scrl);

    for (i = 1; i < itemcount; i++)
	{
	    LoadItem (&item, i);
	    if (ItemIsScroll (&item))
		{
		    GwizShopNode *gsn;

		    gsn = NewGwizShopNode (strdup (item.name), item.buyfor,
					   gwiz.shopstocks[i], total);

		    AppendGwizShopNode (&gwiz.shop.scrl, gsn);

		    gwiz.shop.scrlcount++;
		}
	    total++;
	}
    GwizLogEvent (__FILE__, __LINE__, "Registered num. scrolls for sale: %d",
		  gwiz.shop.scrlcount);
}

void DropItem (GwizInspector *gi, PlayerPawn *pawn)
{
    CompactPawnItems (pawn);

    if (pawn->item[0].usage == USAGE_NONE)
	return;

    DropMenuLoop (gi, pawn);
}

void DropMenuLoop (GwizInspector *gi, PlayerPawn *pawn)
{
    SDL_Surface *inven;
    SDL_Event event;
    SDL_Rect dest;
    SDL_Rect crect;
    int breakout = FALSE;
    int yoffset = gi->area->h - gwiz.tbord[0]->h - gwiz.font.height*8;

    dest.x = CenterHoriz (gwiz.canvas, gi->area);
    dest.y = CenterVert (gwiz.canvas, gi->area);
    dest.h = gi->area->h;
    dest.w = gi->area->w;
    crect.x = gwiz.tbord[0]->w;
    crect.y = yoffset;
    crect.h = gwiz.cursor->h;
    crect.w = gwiz.cursor->w;
    gi->inven.x += gwiz.cursor->w; /* Set it to use the position after the * */

    SDL_BlitSurface (gwiz.cursor, NULL, gi->area, &crect);
    SDL_BlitSurface (gi->area, NULL, gwiz.canvas, &dest);
    SDL_Flip (gwiz.canvas);

    while (SDL_WaitEvent (&event) != 0)
	{
	    SDL_Event *e = &event;
	    int pos = dropcursorposition;
	    int max = CountPawnItems (pawn);
	    if (EventIsDown (e))
		MoveDropCursor (pawn, FALSE);
	    if (EventIsUp (e))
		MoveDropCursor (pawn, TRUE);
	    if (EventIsCancel (e))
		breakout = TRUE;
	    if (EventIsOk (e))
		RequestDrop (pawn, &pawn->item[pos]);

	    /* FIXME: FUGLY.  this should be broken up into smaller funcs */
	    SDL_FillRect (gi->area, &crect, 0);
	    inven = RenderPawnInventory (pawn);
	    if (dropcursorposition > max - 1)
		dropcursorposition = max - 1;
	    crect.y = yoffset + gwiz.font.height * dropcursorposition;
	    SDL_BlitSurface (inven, NULL, gi->area, &gi->inven);
	    SDL_FreeSurface (inven);
	    if ((breakout) || (max == 0))
		break;
	    SDL_BlitSurface (gwiz.cursor, NULL, gi->area, &crect);
	    SDL_BlitSurface (gi->area, NULL, gwiz.canvas, &dest);
	    SDL_Flip (gwiz.canvas);
	}
    gi->inven.x -= gwiz.cursor->w;
}

void MoveDropCursor (PlayerPawn *pawn, int goingup)
{
    int maxpos = CountPawnItems (pawn);
    int pos = dropcursorposition;

    if (goingup)
	{
	    pos--;
	    if (pos < 0)
		pos = maxpos - 1;
	} else {
	    pos++;
	    if (pos > maxpos - 1)
		pos = 0;
	}
    dropcursorposition = pos;
}

void RequestDrop (PlayerPawn *pawn, Inventory *item)
{
    if (item->usage & USAGE_EQUIPPED)
	{
	    MsgBox ("Cannot drop items in use");
	    return;
	}
    item->usage = USAGE_NONE;
    item->version = -1;
    CompactPawnItems (pawn);
}

int CountPawnItems (PlayerPawn *pawn)
{
    int count = 0;
    int i;
    for (i = 0; i < 8; i++)
	if (pawn->item[i].usage != USAGE_NONE)
	    count++;
    return (count);
}

void TradeItem (GwizInspector *gi, PlayerPawn *pawn)
{
    SDL_Surface *refreshlist;
    PlayerPawn *destpawn;
    int givewhich;
    int acceptwhich;

    if (CountPawnItems (pawn) == 0)
	{
	    MsgBox ("Nothing to trade");
	    return;
	}
    if (CountPartyMembers() < 1)
	{
	    MsgBox ("Nobody to receive object");
	    return;
	}

    gi->maxinvenpos = CountPawnItems(pawn) - 1;
    gi->invenpos = 0;

    destpawn = &gwiz.pawn[NewMiniPawnList("Give to whom?")];
    acceptwhich = FirstEmptyPawnItem (destpawn);

    if (destpawn == pawn)
	{
	    MsgBox ("Cannot trade with yourself");
	    return;
	}

    if (CountPawnItems (destpawn) == 8)
	{
	    MsgBox ("That character\'s pack is full");
	    return;
	}

    gi->inven.x += gwiz.cursor->w;
    givewhich = GetPawnItemPosition (gi, pawn);

    if (givewhich < 0)
	{
	    gi->inven.x -= gwiz.font.width;
	    return;
	}

    if ((pawn->item[givewhich].usage & USAGE_EQUIPPED) == USAGE_EQUIPPED)
	{
	    gi->inven.x += gwiz.cursor->w;
	    MsgBox ("Cannot trade equipped items");
	    return;
	}

    memmove (&destpawn->item[acceptwhich], &pawn->item[givewhich],
	     sizeof(Inventory));
    RequestDrop (pawn, &pawn->item[givewhich]);

    refreshlist = RenderPawnInventory (pawn);
    SDL_BlitSurface (refreshlist, NULL, gi->area, &gi->inven);
    SDL_BlitSurface (gi->area, NULL, gwiz.canvas, &gi->dest);

    gi->inven.x -= gwiz.font.width;    
}

int GetPawnItemPosition (GwizInspector *gi, PlayerPawn *pawn)
{
    SDL_Surface *inven = RenderPawnInventory (pawn);
    SDL_Event event;
    SDL_Event *e = &event;
    SDL_Rect dest;
    SDL_Rect crect;
    int breakout = FALSE;

    dest.x = gwiz.canvas->w/2 - gi->area->w/2;
    dest.y = gwiz.canvas->h/2 - gi->area->h/2;
    dest.h = gi->area->h;
    dest.w = gi->area->w;
    crect.x = BORDERWIDTH;
    crect.y = gi->area->h - BORDERHEIGHT - gwiz.font.height*8 +
	      gwiz.font.height*gi->invenpos + (gwiz.font.height/2 -
					       gwiz.cursor->h/2);
    crect.h = gwiz.cursor->h;
    crect.w = gwiz.cursor->w;

    SDL_BlitSurface (gwiz.cursor, NULL, gi->area, &crect);

    while (SDL_WaitEvent (&event) != 0)
	{
	    if (EventIsUp (e))
		gi->invenpos = MovePawnItemPositionCursor (gi, pawn, TRUE);
	    if (EventIsDown (e))
		gi->invenpos = MovePawnItemPositionCursor (gi, pawn, FALSE);
	    if (EventIsOk (e))
		{
		    SDL_FreeSurface (inven);
		    CleanPawnItemPositionCursor (gi);
		    break;
		}
	    if (EventIsCancel (e))
		breakout = TRUE;

	    SDL_FreeSurface (inven);
	    if (breakout)
		{
		    CleanPawnItemPositionCursor (gi);
		    return (-1);
		}
	    inven = RenderPawnInventory (pawn);
	    SDL_BlitSurface (inven, NULL, gi->area, &gi->inven);
	    SDL_BlitSurface (gi->area, NULL, gwiz.canvas, &dest);
	    SDL_Flip (gwiz.canvas);
	}

    return (gi->invenpos);
}

int MovePawnItemPositionCursor (GwizInspector *gi, PlayerPawn *pawn, 
				int goingup)
{
    int basey = gi->area->h - BORDERHEIGHT - gwiz.font.height*8 +
	        (gwiz.font.height/2 - gwiz.cursor->h/2);
    SDL_Rect crect;

    CleanPawnItemPositionCursor (gi);

    if (goingup)
	{
	    gi->invenpos--;
	    if (gi->invenpos < 0)
		gi->invenpos = gi->maxinvenpos;
	} else {
	    gi->invenpos++;
	    if (gi->invenpos > gi->maxinvenpos)
		gi->invenpos = 0;
	}
 
    crect.x = BORDERWIDTH;
    crect.y = basey + gi->invenpos*gwiz.font.height;
    crect.h = gwiz.cursor->h;
    crect.w = gwiz.cursor->w;

    SDL_BlitSurface (gwiz.cursor, NULL, gi->area, &crect);
    return (gi->invenpos);
}

void CleanPawnItemPositionCursor (GwizInspector *gi)
{
    SDL_Rect dest;

    dest.x = BORDERWIDTH;
    dest.y = gi->area->h - BORDERHEIGHT - gwiz.font.height*8;
    dest.w = gwiz.cursor->w;
    dest.h = gwiz.font.height*8;

    SDL_FillRect (gi->area, &dest, gwiz.bgc);
}

void UseItem (Inventory *item, void *origin, void *target)
{
    if ((item->usage & USAGE_SINGLE) != USAGE_SINGLE)
	if ((item->usage & USAGE_MULTIPLE) != USAGE_MULTIPLE)
	    return;
    if (gwiz.battle == FALSE)
	{
	    UseItemCamp (item, (PlayerPawn*)origin, target);
	}
}

void DestroyItemOnUse (Inventory *item)
{
    if ((item->usage & USAGE_SINGLE) == USAGE_SINGLE)
	{
	    item->version = -1;
	    item->usage = USAGE_NONE;
	}
}
