#include <iostream>
#include <ctime>
#include <fstream>
#include <vector>
#include <cstdlib>
using namespace std;

#include "merc.h"
#include "skill.h"
#include "skill_code.h"

//lookup table, etc
#include "skill_c.include"

vector<Skill> SkillTable;
//Skill SkillTable[MAX_SKILL];

/*fixme:

Program received signal SIGSEGV, Segmentation fault.
0x00000003 in ?? ()
(gdb) bt
#0  0x00000003 in ?? ()
#1  0x080b51a1 in event_update() () at stl_vector.h:501
#2  0x080b6e80 in update_handler() () at update.c:1144
#3  0x0806f4d6 in game_loop_unix(int) (control=6) at comm.c:860
#4  0x0806f023 in main (argc=2, argv=0xbffff754) at comm.c:464

*/

/*****************************************************************************/
/**********************************Class Methods******************************/
/*****************************************************************************/
Skill::Skill(void)
{
    cast_code = pulse_code = upkeep_code = expire_code = NULL;
    magic_type = magic_level = target = skill_flags = lag = hours = upkeep = gsn = 0;
    valid = false;
    for (int i = 0; i < MAX_MAGIC_TYPE; i++)
    {
        base_cost[i] = 0;
        upkeep_cost[i] = 0;
    }
}

Skill::Skill(const Skill &SkFoo)
{
        name = SkFoo.name;
        pulse_self = SkFoo.pulse_self;
        off_self = SkFoo.off_self;
        off_room = SkFoo.off_room;
        upkeep_self = SkFoo.upkeep_self;

        magic_type = SkFoo.magic_type;
        magic_level = SkFoo.magic_level;
        target = SkFoo.target;
        skill_flags = SkFoo.skill_flags;
        for (int i = 0; i < MAX_MAGIC_TYPE; i++)
        {
            base_cost[i] = SkFoo.base_cost[i];
            upkeep_cost[i] = SkFoo.upkeep_cost[i];
        }
        lag = SkFoo.lag;
        hours = SkFoo.hours;
        upkeep = SkFoo.upkeep;
        gsn = SkFoo.gsn;
        assign_code(gsn);

        valid = SkFoo.valid;
}

Skill &Skill::operator= (const Skill &SkFoo)
{
    name = SkFoo.name;
    pulse_self = SkFoo.pulse_self;
    off_self = SkFoo.off_self;
    off_room = SkFoo.off_room;
    upkeep_self = SkFoo.upkeep_self;

    magic_type = SkFoo.magic_type;
    magic_level = SkFoo.magic_level;
    target = SkFoo.target;
    skill_flags = SkFoo.skill_flags;
    for (int i = 0; i < MAX_MAGIC_TYPE; i++)
    {
        base_cost[i] = SkFoo.base_cost[i];
        upkeep_cost[i] = SkFoo.upkeep_cost[i];
    }
    lag = SkFoo.lag;
    hours = SkFoo.hours;
    upkeep = SkFoo.upkeep;
    gsn = SkFoo.gsn;
    assign_code(gsn);

    valid = SkFoo.valid;

    return *this;
}

//for STL sort of vector
//Less-than method
//input:    The RHS Skill
//output:   nothing
//returns:  True if LHS skill is valid and RHS is not
//false cases: V V, V I, I I
//only true case: I V (switch)
//fixme: newly validated skills are being put infront of already valid skills
//fixme fixme fixme fixme 6/26/2004 5:41AM
bool Skill::operator< (const Skill &rhsSkill) const
{
/*
    if (!valid && rhsSkill.valid)
        return false;
    else
        return true;
*/
    if (valid && !rhsSkill.valid)
        return true;
    else
        return false;

}

bool Skill::assign_code(const int foo)
{
    bool ret_val = false;

    //if foo >= COMPILED_MAX_SKILL then it is a new skill that
    //does not have any functions yet compiled into the game.
    //You must recompile for newly-added skills to be linked
    //to their functions.
    if (foo >= 0 && foo < COMPILED_MAX_SKILL)
    {
        //gsn = skill_code_table[foo].gsn;
        //*gsn = foo;
        cast_code = skill_code_table[foo].cast_code;
        pulse_code = skill_code_table[foo].pulse_code;
        upkeep_code = skill_code_table[foo].upkeep_code;
        expire_code = skill_code_table[foo].expire_code;
        ret_val = true;
    }

    return ret_val;
}

bool Skill::validate(void)
{
    ofstream fout_h, fout_c;

    //have to have this instead of returning just member 'valid' because
    //after the resort *this skill's gsn changes.
    bool Validated = false;

    fout_h.open(SKILLCODE_H_FILE, ofstream::out | ofstream::app);
    fout_c.open(SKILLCODE_C_FILE, ofstream::out | ofstream::app);
    if (!valid && fout_h && fout_c)
    {
        if (cast_code)
        {
            fout_h << "DECLARE_SKILL_FUN( " << name << "_cast_code );" << endl;
            fout_c << "SKILL_RET " << name
                   << "_cast_code (CHAR_DATA *ch, const char *argument)"
                   << endl << "{" << endl
                   << "    return DEFAULT_RETURN;" << endl << "}"
                   << endl << endl;
        }
        if (pulse_code)
        {
            fout_h << "DECLARE_SKILL_FUN( " << name << "_pulse_code );" << endl;
            fout_c << "SKILL_RET " << name
                   << "_pulse_code (CHAR_DATA *ch, const char *argument)"
                   << endl << "{" << endl
                   << "    return DEFAULT_RETURN;" << endl << "}"
                   << endl << endl;
        }
        if (upkeep_code)
        {
            fout_h << "DECLARE_SKILL_FUN( " << name << "_upkeep_code );" << endl;
            fout_c << "SKILL_RET " << name
                   << "_upkeep_code (CHAR_DATA *ch, const char *argument)"
                   << endl << "{" << endl
                   << "    return DEFAULT_RETURN;" << endl << "}"
                   << endl << endl;
        }
        if (expire_code)
        {
            fout_h << "DECLARE_SKILL_FUN( " << name << "_expire_code );" << endl;
            fout_c << "SKILL_RET " << name
                   << "_expire_code (CHAR_DATA *ch, const char *argument)"
                   << endl << "{" << endl
                   << "    return DEFAULT_RETURN;" << endl << "}"
                   << endl << endl;
        }
        valid = true;
        Validated = true;
    }

    fout_h << endl;

    fout_h.close();
    fout_c.close();

    //required!  Pushes newly-validated skill to the top.
    //if we don't so this, the function lookup table save will fuck up. (sucks!)
    //
    //if you wanted to fix this, self, you could use std::map, key = skill's name,
    //value = functions. fixme! (or 4 maps, one for each func)
    sort(SkillTable.begin(), SkillTable.end());
    //have to re-assign GSNs, sadly.  This won't affect previous skills,
    //just shakes up the invalid skills and newly-validated skill a bit.
    for (unsigned int indx = 0; indx < SkillTable.size(); indx++)
    {
        SkillTable[indx].gsn = indx;
    }

    return Validated;
}

//Ofstream insertion operator
//input:    Ofstream reference, Skill to insert into the stream
//output:   Skill to insert into stream
//returns:  reference to the stream (for chaining: fout << skill << endl)
ofstream &operator<<( ofstream &ofs, const Skill &skillin )
{
    //jh new
    ofs << "***BEGIN SKILL***" << endl;
    ofs << "Skill " << skillin.gsn << endl;

    ofs << skillin.name << endl;
    ofs << skillin.pulse_self << endl;
    ofs << skillin.off_self << endl;
    ofs << skillin.off_room << endl;
    ofs << skillin.upkeep_self << endl;

    ofs << skillin.magic_type << endl;
    ofs << skillin.magic_level << endl;
    ofs << skillin.target << endl;
    ofs << skillin.skill_flags << endl;

    for (int i = 0; i < MAX_MAGIC_TYPE; i++)
    {
        ofs << skillin.base_cost[i] << " ";
    }
    ofs << endl;

    for (int i = 0; i < MAX_MAGIC_TYPE; i++)
    {
        ofs << skillin.upkeep_cost[i] << " ";
    }
    ofs << endl;

    ofs << skillin.lag << endl;
    ofs << skillin.hours << endl;
    ofs << skillin.upkeep << endl;

    return ofs;
}

//Ifstream extraction operator
//input:    Ifstream reference, Skill to extract out of the stream
//output:   nothing
//returns:  reference to the stream (for chaining: fin >> skill >> whatever)
ifstream &operator>>( ifstream &ifs, Skill &skillin )
{
    string parse = "";

    //cleanup (JH new 6/26/2004 8:25PM)
    while (parse != "***BEGIN SKILL***")
        getline(ifs, parse);

    //"Skill 6" <-- 6 = gsn
    getline(ifs, parse);
    parse.erase(0, 6);
    skillin.gsn = atoi (parse.c_str());

    getline(ifs, skillin.name);

    //hack
    getline(ifs, skillin.pulse_self);
    if (skillin.pulse_self.length() < 3)
        skillin.pulse_self = "";

    //hack
    getline(ifs, skillin.off_self);
    if (skillin.off_self.length() < 3)
        skillin.off_self = "";
    //hack
    getline(ifs, skillin.off_room);
    if (skillin.off_room.length() < 3)
        skillin.off_room = "";

    //hack
    getline(ifs, skillin.upkeep_self);
    if (skillin.upkeep_self.length() < 3)
        skillin.upkeep_self = "";

    ifs >> skillin.magic_type;
    ifs >> skillin.magic_level;
    ifs >> skillin.target;
    ifs >> skillin.skill_flags;

    for (int i = 0; i < MAX_MAGIC_TYPE; i++)
    {
        ifs >> skillin.base_cost[i];
    }

    for (int i = 0; i < MAX_MAGIC_TYPE; i++)
    {
        ifs >> skillin.upkeep_cost[i];
    }

    ifs >> skillin.lag;
    ifs >> skillin.hours;
    ifs >> skillin.upkeep;

    //assign gsn and other misc.
    skillin.assign_code(skillin.gsn);
    skillin.valid = true;

    return ifs;
}


/*****************************************************************************/
/**********************************Utility functions**************************/
/*****************************************************************************/

int save_skills (void)
{
    ofstream fout(SKILL_FILE);
    //how many skills will we end up having?  We don't save invalid skills.
    int skill_count = 0;
    unsigned int indx;
    if (!fout)
    {
        bugf("Cannot open %s to write skills.", SKILL_FILE);
        return 1;
    }

    fout << "SkullMUD Skills Data V1.0" << endl;
    fout << "*************************" << endl;


    for (indx = 0; indx < SkillTable.size(); indx++)
    {
        if (SkillTable[indx].valid)
        {
            fout << SkillTable[indx];
            skill_count++;
        }
    }

    fout.close();

    save_include_files(skill_count);

    return 0;
}

int load_skills (void)
{
    char buf[MSL];
    Skill SkillLoad;

    ifstream fin(SKILL_FILE);
    if (!fin)
    {
        bugf("Cannot open %s to load skills.", SKILL_FILE);
        return 0;
    }

    //snatch first couple of lines.
    fin.getline(buf, MSL);
    fin.getline(buf, MSL);

    //Now let the skill's methods do the rest.
    for (int sk_ref = 0; sk_ref < MAX_SKILL; sk_ref++)
    {
        writelogf("Loading skill %d...", sk_ref);
        fin >> SkillLoad;
        if (!fin.good())
        {
            bugf("ERROR: %s is corrupt.", SKILL_FILE);
            bugf("Skill %d will be discarded.", sk_ref);
            break;
        }
        SkillTable.push_back(SkillLoad);
        writelogf("[test] SkillTable[%d].name = %s", sk_ref, SkillTable[sk_ref].name.c_str());
    }

    fin.close();

    return SkillTable.size();
};

//Save include files
int save_include_files(const int skill_count)
{

    //Save skill.c include first.
    /**********************************************************************/
    ofstream fout(SKILLINCLUDE_C_FILE);

    if (!fout)
    {
        bugf("Cannot open %s to write skills", SKILLINCLUDE_C_FILE);
        return 1;
    }

    fout << endl;

    //write table
    fout << "const struct skill_code_type skill_code_table[] = "
         << endl << "{" << endl;

    for (unsigned int i = 0; i < SkillTable.size(); i++)
    {
        if (!SkillTable[i].valid)
            continue;

        fout << "    {\"" << SkillTable[i].name << "\", ";
        //validity check: new code and some other skills won't actually
        //have all 4 functions.  I check for that here.
        if (SkillTable[i].cast_code)
            fout << SkillTable[i].name << "_cast_code, ";
        else
            fout << "NULL, ";

        if (SkillTable[i].pulse_code)
            fout << SkillTable[i].name << "_pulse_code, ";
        else
            fout << "NULL, ";

        if (SkillTable[i].upkeep_code)
            fout << SkillTable[i].name << "_upkeep_code, ";
        else
            fout << "NULL, ";

        if (SkillTable[i].expire_code)
             fout << SkillTable[i].name << "_expire_code";
        else
            fout << "NULL";

         /*
         if (i == SkillTable.size() - 1)
            fout << "}" << endl;
         else
            fout << "}," << endl;
         */
        //JH rewrote above
         if (i == SkillTable.size() - 1 || !SkillTable[i + 1].valid)
            fout << "}" << endl;
         else
            fout << "}," << endl;

    }

    fout << "};" << endl << endl;

    //write MAX_SKILL
    //MAX_SKILL is allowed to be actively changed in-game.
    fout << "int MAX_SKILL = " << skill_count << ";" << endl << endl;
    //Write compiled, this is used to ensure the copy constructor doesn't
    //call assign_code for a non-existent entry in the function table.
    //Doing so would result in a seg fault.
    //Sadly, you must recompile to add in said functions.
    fout << "const int COMPILED_MAX_SKILL = " << skill_count << ";" << endl << endl;

    fout.close();

    //hack: had to do this because, well, fuck.
    system( "touch ../src/skill*" );

    return 0;
}

int find_skill(const char *argument)
{
    int ret_skill = -1;
    unsigned int indx;

    for (indx = 0; indx < SkillTable.size(); indx++)
    {
        if (SkillTable[indx].name == argument)
            ret_skill = SkillTable[indx].gsn;
    }

    return ret_skill;
}

/*****************************************************************************/
/****************************Skill-related functions**************************/
/*****************************************************************************/
bool fail_upkeep_cost(CHAR_DATA *caster, int iskill)
{
    bool bFail = false;

    for (int i = 0; i < MAX_MAGIC_TYPE; i++)
    {
        //if not enough mana, the affect/spell will fail
        if ((caster->magic_points[i] - SkillTable[iskill].upkeep_cost[i]) < 0)
            bFail = true;

        caster->magic_points[i] =
            UMAX(0, (caster->magic_points[i] - SkillTable[iskill].upkeep_cost[i]));
    }

    return bFail;
}

void do_cast (CHAR_DATA *ch, char *argument)
{
    char arg_spell[MIL], arg_targ[MIL];

    argument = one_argument( argument, arg_spell);
    argument = one_argument( argument, arg_targ );

    //lookup spell by name, get its || start here 6/14/2004 11:50PM


}