/******************************************************************************/
/*  This file contains the various non-OLC commands a wiz uses to edit things
 * such as skills.
 *
 * Note to self:  Interactive mode more flexible?  How you wanna do this?
 *
 * JH 5/25/2004 8:25PM
 *
 * $ring0
 *
 */

using namespace std;
#include <iostream>
#include <vector>

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

/*
Left off:
- need to do whatever to d->character->pcdata->menu_locked
- make exec_menu for interp--rethink how to do innermost core, the menu is executed,
not the function.  Know what I mean?  But there'll have to be some sort of set
function regardless--behind the scenes, let the menu provide the interface.
*/

/****************************Skill editing section****************************/
bool skill_createmenu(CHAR_DATA *, int);

void skill_edit (CHAR_DATA *ch, int foo, bool FirstExecute = false);

void do_skill_list(CHAR_DATA *ch, char *argument)
{
    ch_printf(ch, "Skl#   Skill Name                    Magic Type  Lvl  New?\n\r");
    ch_printf(ch, "{y-{Y--{y-  {g----------{G---------{g----------  {c---{C----{c---  {m-{M-{m-  {r-{R--{r-{x\n\r");
//                 1.  skull                         1 chi

    for (unsigned int i = 0; i < SkillTable.size(); i++)
    {
        ch_printf(ch, "{Y%3d.  {G%-30s {C%-10s  {M%3d  %s{x\n\r", i, SkillTable[i].name.c_str(),
            magic_table[SkillTable[i].magic_type].name, SkillTable[i].magic_level, 
            SkillTable[i].valid ? "{RNo" : "{GYes");
    }
    return;
}

//outermost core: pick a skill to edit
void do_skill_edit(CHAR_DATA *ch, char *argument)
{
    int skill;
    if (IS_NPC(ch)) return;

    MENU_DATA *menuold = NULL;

    if (!argument || !*argument)
    {
        send_to_char("No skill specified.  Pick by number.  Here's a list of skills.\n\r", ch);
        do_function(ch, &do_skill_list, "");
        send_to_char("Alternatively, you can: {r'{Rskedit save{r'{x, {r'{Rskedit add{r'{x,"
                     " {r'{Rskedit validate{r'{x.\n\r", ch);
        send_to_char("{YNOTE: Be _{RVERY{Y_ sure you truly wish to validate a new skill!\n\r", ch);
        return;
    }

    //fixme what the fuck is going on here?  None of the below is acting as it should.
    if (!is_number(argument)) //save or add or validate
    {
        char arg[MIL];
        argument = one_argument(argument, arg);

        if (!str_prefix(arg, "save"))
        {
            ch_printf(ch, "Saving skills...\n\r");
            if (save_skills())
            {
                ch_printf(ch, "{RError: skill save failed.{x\n\r");
                bugf("Skill save failed, called here: %s:%d", __FILE__, __LINE__);
                return;
            }
            ch_printf(ch, "Done.\n\r");
        }
        else if (!str_prefix(arg, "add"))
        {
            ch_printf(ch, "argument: %s\n\r", argument);
            if (argument[0] == '\0')
            {
                send_to_char ("Add what skill?\n\r", ch);
                return;
            }

            if (find_skill(argument) >= 0)
            {
                send_to_char ("Error: A skill already exists with that name.\n\r", ch);
                return;
            }

            Skill SkAdd;
            SkAdd.valid = false;
            SkAdd.name = argument;
            SkAdd.gsn = MAX_SKILL++; //push another Skill onto the vector
            ch_printf(ch, "Skill added.  Use skedit <number> to edit it.\n\r");
            SkillTable.push_back(SkAdd);
        }
        else if (!str_prefix(arg, "validate")) //fixme broken.
        {
            int which_skill;
            ch_printf(ch, "argument: %s\n\r", argument);

            if (argument[0] == '\0')
            {
                send_to_char ("Validate what skill?\n\r", ch);
                return;
            }

            which_skill = atoi(argument);

            if (which_skill < 0 || which_skill > SkillTable.size() - 1)
            {
                ch_printf(ch, "blahblah seg fault blah\n\r");
                return;
            }

            if (SkillTable[which_skill].valid)
            {
                ch_printf(ch, "The skill '%s' is already valid.\n\r",
                    SkillTable[which_skill].name.c_str());
            }
            else
            {
                //set valid.
                //add new function parameters to skill_code.h, skill_code.c,
                //if appropriate. 
                if (SkillTable[which_skill].validate())
                {
                    ch_printf(ch, "Skill validated.  The MUD will have to be"
                    " recompiled for the skill to be linked to its functions.\n\r\n\r");
                    do_function(ch, &do_skill_edit, "save");
                }
                
            }
        }
        else
        {
            send_to_char("You can: {r'{Rskedit save{r'{x, {r'{Rskedit add{r'{x,"
                     " {r'{Rskedit validate{r'{x.\n\r", ch);
        }
        return;
    }


    skill = atoi (argument);

    if (skill < 0 || skill >= SkillTable.size())
    {
        send_to_char("Skill choice out of range.  Use sklist to pick a skill.\n\r", ch);
        return;
    }

    //valid skill choice entered.  Let them know what they're editing. :)
    //ch_printf(ch, "---Skill %d, '%s'---\n\r", skill, SkillTable[skill].name.c_str());

    //hang on...is the person already editing a skill?  If so, we have to free.
    //JH: technically this is impossible, exec_menu requires them to leave a menu
    //however, if they disconnect, the old menu will be there yet. One would think
    //that the event handler would take care of it in time, but this is for safety
    //check: is it necessary? fixme?
    if ((menuold = find_menu_char (ch, "Skillbuilder")) != NULL)
    {
        menu_from_char(ch, menuold);
        free_menu(menuold);
    }

    if (skill_createmenu(ch, skill))
        show_menu(ch, ch->pcdata->menu_locked, TRUE);

    ch_printf(ch, "Hit a number plus its corresponding arguments to change a field.\n\r");
    ch_printf(ch, "Example: \n\r> 2 chi\n\r");
    ch_printf(ch, "Hit ? to view this skill again, or enter Q or X to quit.\n\r");

}

void mf_skedit_name (CHAR_DATA *ch, char *argument)
{
    //MENU_DATA *menu = ch->desc->menu_locked;
    ch_printf(ch, "The name of a skill is locked and uneditable.\n\r");
}

void mf_skedit_mag (CHAR_DATA *ch, char *argument)
{
    int i;
    MENU_DATA *menu = ch->pcdata->menu_locked;

    if (menu && argument && *argument)
    {
        for (i = 0; i < MAX_MAGIC_TYPE; i++)
        {
            if (!str_prefix(magic_table[i].name, argument))
            {
                SkillTable[menu->argi].magic_type = magic_table[i].type;
                ch_printf(ch, "Skill type of magic changed to %s.\n\r",
                    magic_table[i].name);
                return;
            }
        }
    }

    ch_printf(ch, "Unrecognized skill type.\n\r");
}


void mf_skedit_lvl (CHAR_DATA *ch, char *argument)
{
    int i;
    MENU_DATA *menu = ch->pcdata->menu_locked;

    if (menu && argument && *argument && ((i = atoi(argument)) > 0))
    {
        SkillTable[menu->argi].magic_level = i;
        ch_printf(ch, "Skill level changed to %d.\n\r", i);
    }
}


void mf_skedit_targ (CHAR_DATA *ch, char *argument)
{
    int i;
    MENU_DATA *menu = ch->pcdata->menu_locked;

    if (menu && argument && *argument && ((i = atoi(argument)) >= 0))
    {
        SkillTable[menu->argi].target = i;
        ch_printf(ch, "Skill target changed to %d.\n\r", i);
    }
}


void mf_skedit_fla (CHAR_DATA *ch, char *argument)
{
    int i;
    MENU_DATA *menu = ch->pcdata->menu_locked;

    if (menu && argument && *argument && ((i = atoi(argument)) >= 0))
    {
        SkillTable[menu->argi].skill_flags = i;
        ch_printf(ch, "Skill flags changed to %d.\n\r", i);
    }
}


void mf_skedit_cos (CHAR_DATA *ch, char *argument)
{
    char arg[MSL];
    MENU_DATA *menu = ch->pcdata->menu_locked;

    if (menu && argument && *argument)
    {
        ch_printf(ch, "Skill base costs changed to: ");
        for (int j = 0; j < MAX_MAGIC_TYPE; j++)
        {
            argument = one_argument(argument, arg);
            SkillTable[menu->argi].base_cost[j] = UMAX(0, atoi(arg));
            ch_printf(ch, "{R%d {x%s ", SkillTable[menu->argi].base_cost[j],
                magic_table[j].name);
        }
    }
    ch_printf(ch, "\n\r");
}


void mf_skedit_ukc (CHAR_DATA *ch, char *argument)
{
    char arg[MSL];
    MENU_DATA *menu = ch->pcdata->menu_locked;

    if (menu && argument && *argument)
    {
        ch_printf(ch, "Skill upkeep costs changed to: ");
        for (int j = 0; j < MAX_MAGIC_TYPE; j++)
        {
            argument = one_argument(argument, arg);
            SkillTable[menu->argi].upkeep_cost[j] = UMAX(0, atoi(arg));
            ch_printf(ch, "{R%d {x%s ", SkillTable[menu->argi].upkeep_cost[j],
                magic_table[j].name);
        }
    }
    ch_printf(ch, "\n\r");
}


void mf_skedit_lag (CHAR_DATA *ch, char *argument)
{
    MENU_DATA *menu = ch->pcdata->menu_locked;
    int i;
    if (menu && argument && *argument && ((i = atoi(argument)) >= 0))
    {
        SkillTable[menu->argi].lag = i;
        ch_printf(ch, "Skill lag changed to %d.\n\r", i);
    }
}


void mf_skedit_las (CHAR_DATA *ch, char *argument)
{
    MENU_DATA *menu = ch->pcdata->menu_locked;
    int i;
    if (menu && argument && *argument && ((i = atoi(argument)) > 0))
    {
        SkillTable[menu->argi].hours = i;
        ch_printf(ch, "Skill longevity ('hours') changed to %d.\n\r", i);
    }
}


void mf_skedit_upk (CHAR_DATA *ch, char *argument)
{
    MENU_DATA *menu = ch->pcdata->menu_locked;
    int i;
    if (menu && argument && *argument && ((i = atoi(argument)) >= 0))
    {
        SkillTable[menu->argi].upkeep = i;
        ch_printf(ch, "Skill upkeep timer changed to %d.\n\r", i);
    }
}


void mf_skedit_mself (CHAR_DATA *ch, char *argument)
{
    MENU_DATA *menu = ch->pcdata->menu_locked;
    if (menu && argument[0] == '\0')
    {
        ch_printf(ch, "Skill wearoff (self) cleared.\n\r");
        SkillTable[menu->argi].off_self = "";
    }
    else if (menu && argument && *argument)
    {
        SkillTable[menu->argi].off_self = argument;
        ch_printf(ch, "Skill wearoff (self) message changed to '%s'.\n\r", argument);
    }
}


void mf_skedit_mroom (CHAR_DATA *ch, char *argument)
{
    MENU_DATA *menu = ch->pcdata->menu_locked;
    if (menu && argument[0] == '\0')
    {
        ch_printf(ch, "Skill wearoff (room) cleared.\n\r");
        SkillTable[menu->argi].off_room = "";
    }
    if (menu && argument && *argument)
    {
        SkillTable[menu->argi].off_room = argument;
        ch_printf(ch, "Skill wearoff (room) message changed to '%s'.\n\r", argument);
    }
}


void mf_skedit_mupk (CHAR_DATA *ch, char *argument)
{
    MENU_DATA *menu = ch->pcdata->menu_locked;
    if (menu && argument[0] == '\0')
    {
        ch_printf(ch, "Skill upkeep (self) cleared.\n\r");
        SkillTable[menu->argi].upkeep_self = "";
    }
    if (menu && argument && *argument)
    {
        SkillTable[menu->argi].upkeep_self = argument;
        ch_printf(ch, "Skill upkeep (self) message changed to '%s'.\n\r", argument);
    }
}

void mf_skedit_mpuls (CHAR_DATA *ch, char *argument)
{
    MENU_DATA *menu = ch->pcdata->menu_locked;
    if (menu && argument[0] == '\0')
    {
        ch_printf(ch, "Skill pulse (self) cleared.\n\r");
        SkillTable[menu->argi].pulse_self = "";
    }
    if (menu && argument && *argument)
    {
        SkillTable[menu->argi].pulse_self = argument;
        ch_printf(ch, "Skill pulse (self) message changed to '%s'.\n\r", argument);
    }
}

void mf_skedit_code (CHAR_DATA *ch, char *argument)
{
    MENU_DATA *menu = ch->pcdata->menu_locked;
    char arg1[MIL];

    if (menu && argument && *argument)
    {
        argument = one_argument(argument, arg1);
        if (LOWER(arg1[0]) == 'y')
            SkillTable[menu->argi].cast_code = dummy_code;
        else
            SkillTable[menu->argi].cast_code = NULL;

        argument = one_argument(argument, arg1);
        if (LOWER(arg1[0]) == 'y')
            SkillTable[menu->argi].pulse_code = dummy_code;
        else
            SkillTable[menu->argi].pulse_code = NULL;

        argument = one_argument(argument, arg1);
        if (LOWER(arg1[0]) == 'y')
            SkillTable[menu->argi].upkeep_code = dummy_code;
        else
            SkillTable[menu->argi].upkeep_code = NULL;

        argument = one_argument(argument, arg1);
        if (LOWER(arg1[0]) == 'y')
            SkillTable[menu->argi].expire_code = dummy_code;
        else
            SkillTable[menu->argi].expire_code = NULL;
    }
}

bool skill_createmenu(CHAR_DATA *ch, int skill)
{
    char buf[MSL];
    char sbuf[MSL];
    MENU_DATA *menu = new_menu("Skillbuilder");
    OPTION_DATA *option;

    //for exec_menu
    menu->argi = skill;
    SET_BIT(menu->menu_flags, MENU_PHOENIX); //use the fun_from to rebuild the menu
    menu->fun_from = do_skill_edit;
    sprintf(buf, "{C[{Gnam{C] {cName of skill {r[{Rlocked{r]{C: %s{x", SkillTable[skill].name.c_str());
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_name;
    append_option(menu, option);

    sprintf(buf, "{C[{Gmag{C] {cType of magic{C: %s{x", magic_table[SkillTable[skill].magic_type].name);
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_mag;
    append_option(menu, option);

    sprintf(buf, "{C[{Glvl{C] {cLevel of magic required{C: %d{x", SkillTable[skill].magic_level);
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_lvl;
    append_option(menu, option);

    sprintf(buf, "{C[{Gtar{C] {cValid skill targets{C: %d{x", SkillTable[skill].target);
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_targ;
    append_option(menu, option);

    sprintf(buf, "{C[{Gfla{C] {cSkill flags{C: %d{x", SkillTable[skill].skill_flags);
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_fla;
    append_option(menu, option);

    sprintf(buf, "{C[{Gcos{C] {cBase cast costs[%d]{C: {x", MAX_MAGIC_TYPE);
    for (int i = 0; i < MAX_MAGIC_TYPE; i++)
    {
        sprintf(sbuf, "{C%d {x", SkillTable[skill].base_cost[i]);
        strcat(buf, sbuf);
    }
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_cos;
    append_option(menu, option);

    sprintf(buf, "{C[{Gukc{C] {cUpkeep costs per upkeep round[%d]{C: {x", MAX_MAGIC_TYPE);
    for (int i = 0; i < MAX_MAGIC_TYPE; i++)
    {
        sprintf(sbuf, "{C%d {x", SkillTable[skill].upkeep_cost[i]);
        strcat(buf, sbuf);
    }
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_ukc;
    append_option(menu, option);

    sprintf(buf, "{C[{Glag{C] {cReadiness lag (RT seconds){C: %d{x", SkillTable[skill].lag);
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_lag;
    append_option(menu, option);

    sprintf(buf, "{C[{Glas{C] {cBase last seconds{C: %d{x", SkillTable[skill].hours);
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_las;
    append_option(menu, option);

    sprintf(buf, "{C[{Gupk{C] {cUpkeep time{C: %d{x", SkillTable[skill].upkeep);
    strcat(buf, "\n\r{C--------------------------------------------------------------------------------{x");
    strcat(buf, "\n\r{G---{yMessages{G---");
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_upk;
    append_option(menu, option);

    sprintf(buf, "{C[{Gmself{C] {cWear-off message (self){C: %s{x", SkillTable[skill].off_self.c_str());
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_mself;
    append_option(menu, option);

    sprintf(buf, "{C[{Gmroom{C] {cWear-off message (room){C: %s{x", SkillTable[skill].off_room.c_str());
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_mroom;
    append_option(menu, option);

    sprintf(buf, "{C[{Gmupk{C] {cUpkeep message (self){C: %s{x", SkillTable[skill].upkeep_self.c_str());
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_mupk;
    append_option(menu, option);

    sprintf(buf, "{C[{Gmpuls{C] {cPulse message (self){C: %s{x", SkillTable[skill].pulse_self.c_str());
    strcat(buf, "\n\r{C--------------------------------------------------------------------------------{x");
    strcat(buf, "\n\r{G---{ySkill Prerequisites{G---");
    strcat(buf, "\n\r{G<list of other named skills added here, e.g. 'haste'>{x");
    strcat(buf, "\n\r{C--------------------------------------------------------------------------------{x");
    option = new_option();
    option->args[0] = str_dup(buf);
    option->fun_call = mf_skedit_mpuls;
    append_option(menu, option);

    //set code options for freshly-created skills.
    if (!SkillTable[skill].valid)
    {
        const char *nostr = "{RNo{x";
        const char *yesstr = "{GYes{x";
        sprintf(buf, "{y[{Ycode{y] {cCustom code for:"
            " {CCast? %s {CPulse? %s {CUpkeep? %s {CExpire? %s{x",
            SkillTable[skill].cast_code ? yesstr : nostr,
            SkillTable[skill].pulse_code ? yesstr : nostr,
            SkillTable[skill].upkeep_code ? yesstr : nostr,
            SkillTable[skill].expire_code ? yesstr : nostr);
    
        option = new_option();
        option->args[0] = str_dup(buf);
        option->fun_call = mf_skedit_code;
        append_option(menu, option);
    }
    //let the event attach the menu to the ch
    //menu_to_char(ch, menu);

    menu_event(ch, menu);

    //locking will be independent of events.  6/10/2004 11:26PM JH: NOT ANYMORE!
    //return menu_lock(ch, menu);
    return true;
}