/* 
 * Copyright (C) 2003 Tim Martin
 *
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>

#include "utils.h"
#include "player.h"

struct balance_item balance_sheet_items[] = {
    {INCOME_INTEREST, "INTEREST",      0},
    {INCOME_RENT,     "RENT",          0},
    {INCOME_REVENUE,  "REVENUE",       0},
    {INCOME_LAND,     "LAND_SALES",    1},
    {INCOME_TAX,      "TAX",           0},
    {BORROW,          "BORROW",        1},
    
    {COST_LAND,       "LAND_PURCHASE", 1},
    {COST_BUILD,      "BUILD",         1},
    {COST_UPKEEP,     "UPKEEP",        0},
    {COST_SALARY,     "SALARY",        0},
    {COST_DEBT,       "DEBT_PAYMENTS", 0},

    {INCOME_NUMTYPES, NULL,            0}
};

typedef struct loan_s {
    struct loan_s *next;

    int total;
    int term;   
    int principal;
    int monthly_payment;
    int payments_made;
    float rate;

} loan_t;

struct player_s
{
    int playernum;

    char name[100];
    
    double money;    

    double income[RANGE_NUMRANGES][INCOME_NUMTYPES];

    loan_t *loans;
};


static player_t *players[MAX_NUM_PLAYERS];

int player_new(char *name, player_t **player)
{
    player_t *ret;

    ret = calloc(sizeof(player_t), 1);
    if (!ret) return -1;

    strncpy(ret->name, name, sizeof(ret->name)-1);
    ret->playernum = -1;

    *player = ret;
    return 0;
}

int
player_setopennumber(player_t *player)
{
    int i;
    
    for (i = 1; i < MAX_NUM_PLAYERS; i++) {
	if (players[i] == NULL) {
	    players[i] = player;
	    player->playernum = i;
	    return 0;
	}
    }

    return -1;
}

#if 0
static char *
type_to_string(income_types type)
{
    switch(type)
	{
	case INCOME_INTEREST: return "Interest";
	case INCOME_RENT: return "Rent";
	case INCOME_REVENUE: return "Revenue";
	case INCOME_LAND: return "Land Sale";

	case BORROW: return "Borrow";

	case COST_LAND: return "Land Purchase";
	case COST_BUILD: return "Building";
	case COST_UPKEEP: return "Upkeep";
	case COST_SALARY: return "Salary";
	case COST_DEBT: return "Debt payments";

	case INCOME_NUMTYPES: return "Invalid Type";
	}

    return "Invalid Type";
}
#endif /* 0 */

int player_isactive(int number)
{
    if (players[number] == NULL) return 0;

    return 1;
}

player_t *player_get(int number)
{
    return players[number];
}

int player_getnum(player_t *player)
{
    return player->playernum;
}

void player_setnumber(player_t *player, int number)
{
    player->playernum = number;
    players[number] = player;
}

int player_getmoney(player_t *player)
{
    return (int)player->money;
}

int player_canafford(player_t *player, int amount)
{
    if (player->money < amount) {
	return 0;
    }

    return 1;
}

void player_reducemoney(player_t *player, income_types type, float amount)
{
    player_reducemoney_num(player->playernum, type, amount);
}

void player_reducemoney_num(int playernum, income_types type, float amount)
{
    if ((playernum <= 0) || (playernum > MAX_NUM_PLAYERS)) return;

    if (!players[playernum]) return;

    players[playernum]->income[RANGE_CURRENT_MONTH][type] -= amount;
    players[playernum]->income[RANGE_CURRENT_YEAR][type] -= amount;
    players[playernum]->income[RANGE_ALLTIME][type] -= amount;
    players[playernum]->money -= amount;

    /* xxx printf("%s spent $%.2f on %s\n", players[playernum]->name, amount, type_to_string(type));*/
}

void player_addmoney(player_t *player, income_types type, float amount)
{
    player_addmoney_num(player->playernum, type, amount);
}

void player_addmoney_num(int playernum, income_types type, float amount)
{
    if ((playernum <= 0) || (playernum > MAX_NUM_PLAYERS)) return;

    if (!players[playernum]) return;

    players[playernum]->income[RANGE_CURRENT_MONTH][type] += amount;
    players[playernum]->income[RANGE_CURRENT_YEAR][type] += amount;
    players[playernum]->income[RANGE_ALLTIME][type] += amount;
    players[playernum]->money += amount;

    /* xxx printf("%s received $%.2f from %s\n", players[playernum]->name, amount, type_to_string(type));*/
}

static void
player_clearout_month(int playernum)
{
    int i;
    player_t *player = players[playernum];

    if (!player) return;

    for (i = 0; i < INCOME_NUMTYPES; i++) {
	player->income[RANGE_LAST_MONTH][i] = player->income[RANGE_CURRENT_MONTH][i];
	player->income[RANGE_CURRENT_MONTH][i] = 0;
    }
}

static void
player_clearout_year(int playernum)
{
    int i;
    player_t *player = players[playernum];

    if (!player) return;

    for (i = 0; i < INCOME_NUMTYPES; i++) {
	player->income[RANGE_LAST_YEAR][i] = player->income[RANGE_CURRENT_YEAR][i];
	player->income[RANGE_CURRENT_YEAR][i] = 0;
    }
}

void
players_clearout_month(void)
{
    int i;
    
    for (i = 1; i < MAX_NUM_PLAYERS; i++) {
	player_clearout_month(i);
    }    
}

void
players_clearout_year(void)
{
    int i;
    
    for (i = 1; i < MAX_NUM_PLAYERS; i++) {
	player_clearout_year(i);
    }    
}

void player_setmoney(player_t *player, int number)
{
    player->money = number;
}

void player_delete(player_t *player)
{

    /*
     * Remove from used player list
     */
    if (player->playernum > 0) {
	players[ player->playernum ] = NULL;
    }
    
    free(player);
}

int
player_getincometype(player_t *player, income_range_t range, income_types type)
{
    return player->income[range][type];
}

static int
loan_monthly_payment(float total, float rate, int term)
{
    float top;
    float bot;

    /*
     * R = (P*I/12)/(1 - 1/((1+I/12)^N))
     */
    top = total * rate/12.0;
    bot = 1.0 - 1.0/(pow(1+rate/12.0,term));

    return top/bot;    
}

void
player_addloan(player_t *player, int amount, int term, float rate)
{
    loan_t *newloan;

    newloan = calloc(1, sizeof(loan_t));
    if (!newloan) {
	printf("Error allocating loan\n");
	return;
    }
    
    newloan->total = amount;
    newloan->principal = amount;
    newloan->term = term;
    newloan->rate = rate;
    newloan->monthly_payment = loan_monthly_payment(amount, rate, term);

    newloan->next = player->loans;
    player->loans = newloan;

    player_addmoney(player, BORROW, amount);
}

int
player_total_borrowed(player_t *player)
{
    int amount = 0;
    loan_t *loan = player->loans;

    while (loan) {
	amount += loan->principal;

	loan = loan->next;
    }

    return amount;
}

extern int
player_payoff_loan(player_t *player, int num)
{
    loan_t *loan = player->loans;
    loan_t *prev = NULL;
    int i;
    int cost;

    for (i = 0; i < num; i++) {
	prev = loan;
	loan = loan->next;	
    }
    
    cost = loan->principal;
    
    if (!player_canafford(player, cost)) {
	return -1;
    }
   
    player_reducemoney(player, COST_DEBT, cost);

    /*
     * remove loan
     */
    if (prev) {
	prev->next = loan->next;
    } else {
	player->loans = loan->next;
    }
    free(loan);    
    
    return 0;
}

void player_servicedebt(player_t *player)
{
    loan_t *loan = player->loans;
    loan_t *prev = NULL;

    while (loan) {
	int payment = loan->monthly_payment;
	int interest = loan->principal * (loan->rate/12.0);
	loan_t *next = loan->next;

	player_reducemoney(player, COST_DEBT, payment);

	loan->principal -= (payment - interest);
	loan->payments_made ++;

	if (loan->principal <= 0) {
	    /*
	     * remove loan
	     */
	    if (prev) {
		prev->next = next;
	    } else {
		player->loans = next;
	    }
	    free(loan);
	} else {
	    prev = loan;
	}
	
	loan = next;
    }
}

int
is_one_time(char *str)
{
    int i;

    for (i = 0; i < INCOME_NUMTYPES; i++) {
	struct balance_item *item = &balance_sheet_items[i];

	if (strcasecmp(str, item->string) == 0) {
	    return item->is_one_time;
	}
    }

    return 0;
}

void
player_loans_enumerate(player_t *player, player_loan_iterate_callback *cb, void *rock)
{
    loan_t *loan = player->loans;
    int num = 0;

    while (loan) {

	cb(player, num, loan->total, loan->term, loan->payments_made, loan->rate, rock);

	loan = loan->next;
	num++;
    }
}

char *
player_getname(int num)
{
    player_t *p;

    if (num == 0) return _("General population");

    p = players[num];

    if (p) {
	return p->name;
    } else {
	return _("Unknown player");
    }
}

int
player_lastmonth_profit(player_t *player)
{
    int i;
    int profit = 0;

    for (i = 0; i < INCOME_NUMTYPES; i++) {
	struct balance_item *item = &balance_sheet_items[i];

	if (!is_one_time(item->string)) {
	    profit += player->income[RANGE_LAST_MONTH][i];
	}

    }

    return profit;
}
