/*  levelup.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_ttf.h>
#include <stdlib.h>
#include "gwiz.h"
#include "text.h"
#include "inn.h"
#include "uiloop.h"
#include "levelup.h"
#include "playerpawn.h"

extern GwizApp gwiz;
extern GwizInn inn;

void HandleLevelUp (void)
{
    inn.pawn->level++;
    inn.str = FALSE;
    inn.iq = FALSE;
    inn.dev = FALSE;
    inn.vit = FALSE;
    inn.agi = FALSE;
    inn.luck = FALSE;
    
    if ((rand() % (100 - 1) + 1) < 20)
	inn.str = TRUE;
    if ((rand() % (100 - 1) + 1) < 20)
	inn.iq = TRUE;
    if ((rand() % (100 - 1) + 1) < 20)
	inn.dev = TRUE;
    if ((rand() % (100 - 1) + 1) < 20)
	inn.vit = TRUE;
    if ((rand() % (100 - 1) + 1) < 20)
	inn.agi = TRUE;
    if ((rand() % (100 - 1) + 1) < 20)
	inn.luck = TRUE;
    GetHpBonus();
    GetSpellBonus();
    ProcessLevelUp();
}

void GetHpBonus (void)
{
    int modifier = 0;
    switch (inn.pawn->class)
	{
	case FIGHTER:
	    modifier = 45 * inn.pawn->attr.vit;
	    break;
	case MAGE:
	    modifier = 13 * inn.pawn->attr.vit;
	    break;
	case CLERIC:
	    modifier = 14 * inn.pawn->attr.vit;
	    break;
	case THIEF:
	    modifier = 17 * inn.pawn->attr.vit;
	    break;
	case WIZARD:
	    modifier = 9 * inn.pawn->attr.vit;
	    break;
	case SAMURAI:
	    modifier = 34 * inn.pawn->attr.vit;
	    break;
	case LORD:
	    modifier = 37 * inn.pawn->attr.vit;
	    break;
	case NINJA:
	    modifier = 29 * inn.pawn->attr.vit;
	    break;
	}
    modifier = modifier / 10;

    if ((inn.pawn->class == MAGE) ||
	(inn.pawn->class == CLERIC) ||
	(inn.pawn->class == WIZARD))
	if (rand() < 0.7)
	    modifier -= 15;
    if (inn.pawn->counter.hp_max > 700)
	if ((rand() < 0.95) && (modifier > 0))
	    modifier -= 55;
    if (modifier < 1)
	modifier = 1;
    inn.hp = rand () % (modifier - 1) + 1;
}

void ProcessLevelUp (void)
{
    switch (inn.pawn->race)
	{
	case HUMAN:
	    if ((inn.str) && (inn.pawn->attr.str < HUMAN_STR))
		{
		    printf("%d %d\n", HUMAN_STR, inn.pawn->attr.str);
		    inn.pawn->attr.str++;
		    inn.improvements[0] = TRUE;
		} else 
		    inn.improvements[0] = FALSE;
	    if ((inn.agi) && (inn.pawn->attr.agi < HUMAN_AGI))
		{
		    inn.pawn->attr.agi++;
		    inn.improvements[1] = TRUE;
		} else 
		    inn.improvements[1] = FALSE;
	    if ((inn.iq) && (inn.pawn->attr.iq < HUMAN_IQ))
		{
		    inn.pawn->attr.iq++;
		    inn.improvements[2] = TRUE;
		} else 
		    inn.improvements[2] = FALSE;
	    if ((inn.dev) && (inn.pawn->attr.dev < HUMAN_DEV))
		{
		    inn.pawn->attr.dev++;
		    inn.improvements[3] = TRUE;
		} else 
		    inn.improvements[3] = FALSE;
	    if ((inn.vit) && (inn.pawn->attr.vit < HUMAN_VIT))
		{
		    inn.pawn->attr.vit++;
		    inn.improvements[4] = TRUE;
		} else 
		    inn.improvements[4] = FALSE;
	    if ((inn.luck) && (inn.pawn->attr.luck < HUMAN_LUCK))
		{
		    inn.pawn->attr.luck++;
		    inn.improvements[5] = TRUE;
		} else 
		    inn.improvements[5] = FALSE;
	    break;
	case ELF:
	    if ((inn.str) && (inn.pawn->attr.str < ELF_STR))
		{
		    inn.pawn->attr.str++;
		    inn.improvements[0] = TRUE;
		} else 
		    inn.improvements[0] = FALSE;
	    if ((inn.agi) && (inn.pawn->attr.agi < ELF_AGI))
		{
		    inn.pawn->attr.agi++;
		    inn.improvements[1] = TRUE;
		} else 
		    inn.improvements[1] = FALSE;
	    if ((inn.iq) && (inn.pawn->attr.iq < ELF_IQ))
		{
		    inn.pawn->attr.iq++;
		    inn.improvements[2] = TRUE;
		} else 
		    inn.improvements[2] = FALSE;
	    if ((inn.dev) && (inn.pawn->attr.dev < ELF_DEV))
		{
		    inn.pawn->attr.dev++;
		    inn.improvements[3] = TRUE;
		} else 
		    inn.improvements[3] = FALSE;
	    if ((inn.vit) && (inn.pawn->attr.vit < ELF_VIT))
		{
		    inn.pawn->attr.vit++;
		    inn.improvements[4] = TRUE;
		} else 
		    inn.improvements[4] = FALSE;
	    if ((inn.luck) && (inn.pawn->attr.luck < ELF_LUCK))
		{
		    inn.pawn->attr.luck++;
		    inn.improvements[5] = TRUE;
		} else 
		    inn.improvements[5] = FALSE;
	    break;
	case GNOME:
	    if ((inn.str) && (inn.pawn->attr.str < GNOME_STR))
		{
		    inn.pawn->attr.str++;
		    inn.improvements[0] = TRUE;
		} else 
		    inn.improvements[0] = FALSE;
	    if ((inn.agi) && (inn.pawn->attr.agi < GNOME_AGI))
		{
		    inn.pawn->attr.agi++;
		    inn.improvements[1] = TRUE;
		} else 
		    inn.improvements[1] = FALSE;
	    if ((inn.iq) && (inn.pawn->attr.iq < GNOME_IQ))
		{
		    inn.pawn->attr.iq++;
		    inn.improvements[2] = TRUE;
		} else 
		    inn.improvements[2] = FALSE;
	    if ((inn.dev) && (inn.pawn->attr.dev < GNOME_DEV))
		{
		    inn.pawn->attr.dev++;
		    inn.improvements[3] = TRUE;
		} else 
		    inn.improvements[3] = FALSE;
	    if ((inn.vit) && (inn.pawn->attr.vit < GNOME_VIT))
		{
		    inn.pawn->attr.vit++;
		    inn.improvements[4] = TRUE;
		} else 
		    inn.improvements[4] = FALSE;
	    if ((inn.luck) && (inn.pawn->attr.luck < GNOME_LUCK))
		{
		    inn.pawn->attr.luck++;
		    inn.improvements[5] = TRUE;
		} else 
		    inn.improvements[5] = FALSE;
	    break;
	case DWARF:
	    if ((inn.str) && (inn.pawn->attr.str < DWARF_STR))
		{
		    inn.pawn->attr.str++;
		    inn.improvements[0] = TRUE;
		} else 
		    inn.improvements[0] = FALSE;
	    if ((inn.agi) && (inn.pawn->attr.agi < DWARF_AGI))
		{
		    inn.pawn->attr.agi++;
		    inn.improvements[1] = TRUE;
		} else 
		    inn.improvements[1] = FALSE;
	    if ((inn.iq) && (inn.pawn->attr.iq < DWARF_IQ))
		{
		    inn.pawn->attr.iq++;
		    inn.improvements[2] = TRUE;
		} else 
		    inn.improvements[2] = FALSE;
	    if ((inn.dev) && (inn.pawn->attr.dev < DWARF_DEV))
		{
		    inn.pawn->attr.dev++;
		    inn.improvements[3] = TRUE;
		} else 
		    inn.improvements[3] = FALSE;
	    if ((inn.vit) && (inn.pawn->attr.vit < DWARF_VIT))
		{
		    inn.pawn->attr.vit++;
		    inn.improvements[4] = TRUE;
		} else 
		    inn.improvements[4] = FALSE;
	    if ((inn.luck) && (inn.pawn->attr.luck < DWARF_LUCK))
		{
		    inn.pawn->attr.luck++;
		    inn.improvements[5] = TRUE;
		} else 
		    inn.improvements[5] = FALSE;
	    break;
	case HOBBIT:
	    if ((inn.str) && (inn.pawn->attr.str < HOBBIT_STR))
		{
		    inn.pawn->attr.str++;
		    inn.improvements[0] = TRUE;
		} else 
		    inn.improvements[0] = FALSE;
	    if ((inn.agi) && (inn.pawn->attr.agi < HOBBIT_AGI))
		{
		    inn.pawn->attr.agi++;
		    inn.improvements[1] = TRUE;
		} else 
		    inn.improvements[1] = FALSE;
	    if ((inn.iq) && (inn.pawn->attr.iq < HOBBIT_IQ))
		{
		    inn.pawn->attr.iq++;
		    inn.improvements[2] = TRUE;
		} else 
		    inn.improvements[2] = FALSE;
	    if ((inn.dev) && (inn.pawn->attr.dev < HOBBIT_DEV))
		{
		    inn.pawn->attr.dev++;
		    inn.improvements[3] = TRUE;
		} else 
		    inn.improvements[3] = FALSE;
	    if ((inn.vit) && (inn.pawn->attr.vit < HOBBIT_VIT))
		{
		    inn.pawn->attr.vit++;
		    inn.improvements[4] = TRUE;
		} else 
		    inn.improvements[4] = FALSE;
	    if ((inn.luck) && (inn.pawn->attr.luck < HOBBIT_LUCK))
		{
		    inn.pawn->attr.luck++;
		    inn.improvements[5] = TRUE;
		} else 
		    inn.improvements[5] = FALSE;
	    break;
	}
    inn.pawn->counter.hp_max += inn.hp;
    inn.pawn->counter.hp += inn.hp;
    inn.str = FALSE;
    inn.agi = FALSE;
    inn.iq = FALSE;
    inn.dev = FALSE;
    inn.vit = FALSE;
    inn.luck = FALSE;

    SummarizeLevelImprovements();
    inn.hp = 0;
}

void SummarizeLevelImprovements (void)
{
    char *msgs[] = {
	"You have celebrated your birthday!",
	"You have gained Strength!",
	"You have gained Agility!",
	"You have gained I.Q.!",
	"You have gained Devotion!",
	"You have gained Vitality!",
	"You have gained Luck!",
	NULL
    };
    char hpmsg[31];
    SDL_Surface *list;
    SDL_Surface *box;
    SDL_Surface *displace;
    SDL_Surface *hpline;
    SDL_Rect drect;
    SDL_Rect srect;
    SDL_Rect trect;
    int i;

    snprintf (hpmsg, 31, "You have gained %2d Hit Points!", inn.hp);
    hpline = GwizRenderText (hpmsg);

    list = NewVertTextMsg (msgs, 0);
    box = NewTextBox (list->w, list->h);
    displace = NewGwizSurface (box->w, box->h);

    drect.x = CenterHoriz (gwiz.canvas, displace);
    drect.y = CenterVert (gwiz.canvas, displace);
    drect.h = displace->h;
    drect.w = displace->w;
    srect.x = 0;
    srect.y = 0;
    srect.h = gwiz.font.height;
    srect.w = list->w;
    trect.x = BORDERWIDTH;
    trect.y = BORDERHEIGHT;
    trect.h = srect.h;
    trect.w = srect.w;

    SDL_BlitSurface (gwiz.canvas, &drect, displace, NULL);
    if (inn.hadbirthday)
	{
	    SDL_BlitSurface (list, &srect, box, &trect);
	    trect.y += gwiz.font.height;
	}
    srect.y += gwiz.font.height;
    SDL_BlitSurface (hpline, NULL, box, &trect);
    SDL_FreeSurface (hpline);
    trect.y += gwiz.font.height;

    for (i = 1; i < 7; i++)
	{
	    if (inn.improvements[i-1])
		{
		    SDL_BlitSurface (list, &srect, box, &trect);
		    trect.y += gwiz.font.height;
		    inn.improvements[i-1] = FALSE;
		}
	    srect.y += gwiz.font.height;
	}

    SDL_FreeSurface (list);
    SDL_BlitSurface (box, NULL, gwiz.canvas, &drect);
    SDL_Flip (gwiz.canvas);
    SDL_FreeSurface (box);
    WaitForAnyKey();
    SDL_BlitSurface (displace, NULL, gwiz.canvas, &drect);
    SDL_Flip (gwiz.canvas);
    SDL_FreeSurface (displace);
    inn.hadbirthday = FALSE;
    inn.pawn->level++;
}

char *GetHitPointsBonusMsg (void)
{
    char *msg = Smalloc(31);

    snprintf (msg, 31, "You have gained %2d Hit Points!", inn.hp);

    return (msg);
}

void GetSpellBonus (void)
{
    char learnsnew;
    if ((!PawnIsCleric(inn.pawn)) || (!PawnIsMage(inn.pawn)) ||
	(!PawnIsWizard(inn.pawn)))
	return;
    DistributeNinePlusBonusPoints();
    learnsnew = CalculateOddsOfLearningSpell ();
    if (learnsnew < 60) /* Odds of learning new spell by default: 40% */
	return;
    if (learnsnew > 100)
	LearnLastLevelSpell();
    else
	LearnAnyLevelSpell();
}

void DistributeNinePlusBonusPoints (void)
{
    int i = 0;
    int j = 7;
    char bonus;
    for (i = 0; i < 7; i++)
	{
	    if (inn.pawn->mm.maxpoints[i] < j*9)
		if (inn.pawn->mm.maxpoints[i] > 8)
		    {
			bonus = (rand() < 0.5) ? 1 : 2;
			inn.pawn->mm.maxpoints[i] += bonus;
			inn.pawn->mm.spellpoints[i] = inn.pawn->mm.maxpoints[i];
		    }
	    if (inn.pawn->cm.maxpoints[i] < j*9)
		if (inn.pawn->cm.maxpoints[i] > 8)
		    {
			bonus = (rand() < 0.5) ? 1 : 2;
			inn.pawn->cm.maxpoints[i] += bonus;
			inn.pawn->cm.spellpoints[i] = inn.pawn->cm.maxpoints[i];
		    }
	    j--;
	}
}

void DistributeNineMinusBonusPoints (void)
{
    char discrepancy = (rand() < 0.5) ? 2 : 3;
    int level;
    int i;

    if (PawnIsMage(inn.pawn) || PawnIsWizard(inn.pawn))
	{
	    for (i = 6; i > -1; i--)
		if (inn.pawn->mm.maxpoints[i-1] >= (inn.pawn->mm.maxpoints[i] +
						    discrepancy))
		    break;
	    level = i;
	    inn.pawn->mm.maxpoints[i] += discrepancy;
	}
    if (PawnIsCleric(inn.pawn) || PawnIsWizard(inn.pawn))
	{
	    for (i = 6; i > -1; i--)
		if (inn.pawn->cm.maxpoints[i-1] >= (inn.pawn->cm.maxpoints[i] +
						    discrepancy))
		    break;
	    level = i;
	    inn.pawn->cm.maxpoints[i] += discrepancy;
	}
}

/* Function will determine whether a spell is to be learned via random selection
   UNLESS the last level in the pawn's spell array has no known spells.
   Return value is < 60 for no new spells learned, >60 && <100 for new spell
   learned, and >100 for spell learned in the last level only. */
char CalculateOddsOfLearningSpell (void)
{
    char probability = rand() % (100 - 1) + 1;
    int i;

    for (i = 6; i > -1; i--)
	{
	    if (PawnIsCleric(inn.pawn) || PawnIsWizard(inn.pawn))
		if (LastLevelSpellUnknown(FALSE))
		    {
			probability = 101;
			break;
		    }
	    if (PawnIsMage (inn.pawn) || PawnIsWizard (inn.pawn))
		if (LastLevelSpellUnknown(TRUE))
		    {
			probability = 101;
			break;
		    }
	}
    return (probability);
}

char LastLevelSpellUnknown (short mage)
{
    PlayerMagic *magic = (mage ? &inn.pawn->mm : &inn.pawn->cm);
    int cklevel;
    int i;
    int level[7] = { MM_FIRST_LEVEL, MM_SECOND_LEVEL, MM_THIRD_LEVEL,
		     MM_FOURTH_LEVEL, MM_FIFTH_LEVEL, MM_SIXTH_LEVEL,
		     MM_SEVENTH_LEVEL };
    if (!mage)
	{
	    level[0] = CM_FIRST_LEVEL;
	    level[1] = CM_SECOND_LEVEL;
	    level[2] = CM_THIRD_LEVEL;
	    level[3] = CM_FOURTH_LEVEL;
	    level[4] = CM_FIFTH_LEVEL;
	    level[5] = CM_SIXTH_LEVEL;
	    level[6] = CM_SEVENTH_LEVEL;
	}

    for (i = 6; i > -1; i++)
	if (magic->maxpoints[i])
	    break;
    cklevel = i;
    if (!cklevel)
	return (FALSE);

    for (i = cklevel; i > level[cklevel-1]; i--)
	if ((inn.pawn->spellsknown[i] > -1) && 
	    (inn.pawn->spellsknown[i] < NO_SPELL))
	    return (TRUE);

    return (FALSE);
}

void LearnLastLevelSpell (void)
{
	short mlevel;
 	short clevel;
	short soonest = 0;
 	short latest = 0;
  	short actualspell;
  	int i = 0;

   	if ((PawnIsMage(inn.pawn) || PawnIsWizard(inn.pawn)) && LastLevelSpellUnknown (TRUE))
	   	for (i = 6; i > -1; i--)
     		if (inn.pawn->mm.maxpoints[i] > 0)
       			break;
    mlevel = i;
    if ((PawnIsCleric(inn.pawn) || PawnIsWizard(inn.pawn)) && LastLevelSpellUnknown (FALSE))
    	for (i = 6; i > -1; i--)
     		if (inn.pawn->cm.maxpoints[i] > 0)
       			break;
    clevel = i;

    if ((PawnIsMage(inn.pawn) || PawnIsWizard(inn.pawn)) && mlevel > 0)
    	{
		    switch (mlevel)
			    {
		   		case 0:
		    		soonest = 0;
		     		latest = MM_FIRST_LEVEL;
		      		break;
		    	case 1:
		    		soonest = MM_FIRST_LEVEL;
		     		latest = MM_SECOND_LEVEL;
		      		break;
		    	case 2:
		    		soonest = MM_SECOND_LEVEL;
		     		latest = MM_THIRD_LEVEL;
		      		break;
		    	case 3:
		    		soonest = MM_THIRD_LEVEL;
		     		latest = MM_FOURTH_LEVEL;
		      		break;
		    	case 4:
		    		soonest = MM_FOURTH_LEVEL;
		     		latest = MM_FIFTH_LEVEL;
		      		break;
		    	case 5:
		    		soonest = MM_FIFTH_LEVEL;
		     		latest = MM_SIXTH_LEVEL;
		      		break;
		   	 	case 6:
		    		soonest = MM_SIXTH_LEVEL;
		     		latest = MM_SEVENTH_LEVEL;
		      		break;
		    	}
			actualspell = rand() % (latest - soonest) + soonest;
			inn.pawn->spellsknown[actualspell] = TRUE;
   		}

 	if ((PawnIsCleric(inn.pawn) || PawnIsWizard(inn.pawn)) && clevel > 0)
  		{
	 		switch (clevel)
   				{
        		case 0:
        	 		soonest = 0;
        	   		latest = CM_FIRST_LEVEL;
        	     	break;
        	    case 1:
           		 	soonest = CM_FIRST_LEVEL;
           		  	latest = CM_SECOND_LEVEL;
              		break;
            	case 2:
            		soonest = CM_SECOND_LEVEL;
             		latest = CM_THIRD_LEVEL;
               		break;
            	case 3:
            		soonest = CM_THIRD_LEVEL;
            	 	latest = CM_FOURTH_LEVEL;
            	  	break;
            	case 4:
            		soonest = CM_FOURTH_LEVEL;
            	 	latest = CM_FIFTH_LEVEL;
            	  	break;
            	case 5:
            		soonest = CM_FIFTH_LEVEL;
            	 	latest = CM_SIXTH_LEVEL;
            	  	break;
            	case 6:
            		soonest = CM_SIXTH_LEVEL;
            	 	latest = CM_SEVENTH_LEVEL;
            	  	break;
        		}
			actualspell = rand() % (latest - soonest) + soonest;
		 	inn.pawn->spellsknown[actualspell] = TRUE;
   		}
}

void LearnAnyLevelSpell (void)
{
    int candidates[4];
    int lastspell = GetLastEligibleSpellToLearn();
    int firstspell = (PawnIsCleric(inn.pawn) ? MEDERI : 0);
    int i = firstspell;
    int j = 0;

    /* FIXME: what if the pawn is a wizard, and there's a gap in the
       list of spells eligible to learn? */
    while (TRUE)
	{
	    if (!inn.pawn->spellsknown[i])
		if (rand() < 0.1)
		    {
			candidates[j] = i;
			j++;
			if (j == 4)
			    break;
		    }
	    i++;
	    if (i > lastspell)
		i = firstspell;
	}
}

int GetLastEligibleSpellToLearn(void)
{
    int i = 0;

    return (i);
}
