/*  Included Header Files
    ************************************************************************ */

#include "untitled.h"



/*  Command Table
    ************************************************************************ */

const struct Command Command_Table [] =
{
    { "quit",       COMMAND_QUIT      },
    { "inventory",  COMMAND_INVENTORY },
    { "equipment",  COMMAND_EQUIPMENT },
    { "wear",       COMMAND_WEAR      },
    { "remove",     COMMAND_REMOVE    },
    { "attack",     COMMAND_ATTACK    },
    { "swap",       COMMAND_SWAP      },
    { "sit",        COMMAND_SIT       },
    { "stand",      COMMAND_STAND     },
    { "help",       COMMAND_HELP      },
    { "look",       COMMAND_LOOK      },
    { "health",     COMMAND_HEALTH    },

    { "",           0                 }
};



/*  ************************************************************************
    * displays a string to the console and uses word-wrap                  *
    * [param] string  the string to print                                  *
    * [param] ...     additional arguments                                 *
    * [returns] (none)                                                     *
    ************************************************************************ */

void output ( HWND window, const char * string, ... )
{
    static size_t current_column = 0;
    va_list arguments;
    char complete_string[USER_INPUT_MAX_LENGTH];
    char * sub_string;
    size_t previous_column, string_length, n;
    char buffer[OUTPUT_MAX_LENGTH];
    static int num_lines = 0;

    n = 0;

    (void) GetWindowText(window, buffer, OUTPUT_MAX_LENGTH);

    /* get the complete string, with additional format parameters */
    va_start(arguments, string);
    (void) vsprintf(complete_string, string, arguments);
    va_end(arguments);

    sub_string = &complete_string[0];
    string_length = strlen(complete_string);

    /* set what the column is at before we add the string */
    previous_column = current_column;

    if (current_column == 0)
        sub_string[0] = toupper(sub_string[0]);

    /* readjust the column */
    while (n < string_length)
    {
        /* set the column to what it will be after adding this string */
        current_column++;

        /* check if there's a newline before word wrap */
        if (n < WORD_WRAP - previous_column)
        {   /* TODO: this isn't working properly? */
            /* TODO: add sub_string[n] == 0x0D somehow */
            if (sub_string[n] == 0x0A)  /* 0x0A = \n (ASCII-based, i.e. Windows) */
            {
                fprintf(stdout, "%.*s", n, sub_string);
                (void) strncat(buffer, sub_string, n + 1);
                SetWindowText(window, buffer);
                num_lines++;
                (void) SendMessage(window, EM_LINESCROLL, 0, num_lines);

                sub_string = &sub_string[n + 1];
                current_column = 0;
            };
        };

        n++;
    };

    /* TODO: disregard newline, carriage return, tab, null string, and space when considering
             the end of a string (for current_column (i.e. the length) */

    /* column goes past the word wrap */
    if (current_column > WORD_WRAP)
    {
        int i = 0, finished = FALSE;

        do
        {
            /* start in the string at the spot of the word wrap minus the previous column */
            i = WORD_WRAP - previous_column - 1;

            /* find the first space, tab or null string */
            while (i >= 0)
            {  
                if (sub_string[i] == 0x20   /* space */
                 || sub_string[i] == 0x09   /* tab */
                 || sub_string[i] == 0x0A   /* newline */
/*                 || sub_string[i] == 0x0D*/   /* carriage return */
                 || sub_string[i] == 0x00)  /* null string terminator */
                    break;
                i--;
            };

            /* word wrap success */
            if (i >= 0)
            {
                /* print up to the wrap */
                fprintf(stdout, "%.*s", i, sub_string);
                (void) strncat(buffer, sub_string, i);
                SetWindowText(window, buffer);

                /* TODO: this is a temporary Windows fix */
                if (sub_string[i] != 0x0D)
                {
                    /*fprintf(stdout, "\r");*/
                    (void) strcat(buffer, "\r");
                    SetWindowText(window, buffer);
                };
                if (sub_string[i + 1] != 0x0A)
                {
                    fprintf(stdout, "\n");
                    (void) strcat(buffer, "\n");
                    SetWindowText(window, buffer);
                };
                num_lines++;
                (void) SendMessage(window, EM_LINESCROLL, 0, num_lines);

                /* set the new substring to be point after the wrap */
                sub_string = &sub_string[i];
                current_column = strlen(sub_string) - 1; /* strlen - 1 ? */

                /* get rid of white space for the newline */
                while (sub_string[0] == 0x20 || sub_string[0] == 0x09 || sub_string[0] == 0x0A || sub_string[0] == 0x0D)
                    sub_string = &sub_string[1];

                /* we've reached the end of the output buffer */
                if (strlen(sub_string) < WORD_WRAP)
                {
                    fprintf(stdout, "%s", sub_string);
                    (void) strcat(buffer, sub_string);
                    SetWindowText(window, buffer);
                    (void) SendMessage(window, EM_LINESCROLL, 0, num_lines);

                    finished = TRUE;
                };
            }
            /* word wrap failure */
            else
            {
                    fprintf(stdout, "\r\n%s", sub_string);
                    (void) strcat(buffer, "\r\n");
                    (void) strcat(buffer, sub_string);
                    SetWindowText(window, buffer);
                    num_lines++;
                    (void) SendMessage(window, EM_LINESCROLL, 0, num_lines);

                if (strlen(sub_string) <= WORD_WRAP)
                    current_column = strlen(sub_string);

                finished = TRUE;
            };
        } while (finished == FALSE);
    }

    /* column is within the word wrap limit */
    else if (current_column > 0)
    {
        fprintf(stdout, "%s", sub_string);
        (void) strcat(buffer, sub_string);
        buffer[0] = toupper(buffer[0]);
        SetWindowText(window, buffer);
        (void) SendMessage(window, EM_LINESCROLL, 0, num_lines);
    };
}



/* Function:    process_input
   Description: determines what command the user is trying to use
   Parameters:  NOTE modifies string
   Returns:     [void]
changes string to be the parameters that follow the user's command
   -------------------------------------------------------------------------- */

Command_Type process_input(char * string)
{
    size_t length;
    char * command;
    char * sub_string;
    int i;

    if (string == NULL)
        return NO_COMMAND;

    /* get rid of initial white space */
    command = &string[0];
    while (command[0] == 0x20 || command[0] == 0x09)
        command++;  /* command = &command[1];*/

    /* the user just pressed Enter */
    if (command[0] == 0x0A || command[0] == 0x0D)
        return NO_COMMAND;

    /* get the length of the command that the user input */
    length = 0;
    sub_string = &command[0];
    while (sub_string[0] != 0x20 && sub_string[0] != 0x09 && sub_string[0] != 0x0A && sub_string[0] != 0x0D && sub_string[0] != 0x00)
    {
        sub_string = &sub_string[1];
        length++;
    };

    /* get rid of subsequent white space */
    while (sub_string[0] == 0x20 || sub_string[0] == 0x09)
        sub_string = &sub_string[1];

    /* parse through the commands */
    for (i = 0; Command_Table[i].name != "" /* OR i < sizeof(Command_Table) / sizeof(Command) */; i++)
        if (strncmp(command, Command_Table[i].name, length) == 0)
        {
            /* get rid of newline/carriage return at end of string */
            if (sub_string[strlen(sub_string) - 1] == 0x0A)
                sub_string[strlen(sub_string) - 1] = 0x00;

            /* set the string to be the parameters of the command */
            (void) strcpy(string, sub_string);

            return Command_Table[i].command;
        };

    return NO_COMMAND;
}



/* Function:    command_quit
   exits the game
   Parameters:   [void]
   Return Value: [void]
   -------------------------------------------------------------------------- */

void command_quit ( HWND window, Creature * creature )
{
    window = window;
    creature = creature;
/*    shut_down(creature);

    exit(EXIT_SUCCESS);*/
}



void command_inventory ( HWND window, const Creature * creature )
{
    Item * gear;

    gear = creature->inventory;

    if (creature == NULL)
        return;

    output(window, "You are wearing ");
    if (gear == NULL)
    {
        output(window, "nothing.\r\n");
    }
    else
    {
        if (gear->next == NULL)
            output(window, "%s.", gear->description);
        else if (gear->next->next == NULL)
            output(window, "%s and %s.\r\n", gear->name, gear->next->name);
        else
        {
            while (gear->next != NULL)
            {
                output(window, "%s, ", gear->description);
                gear = gear->next;
            };
            output(window, "and %s.\r\n", gear->description);
        };
    };

    output(window, "You are holding ");
    if (creature->right_held == NULL)
        output(window, "nothing ");
    else
        output(window, "%s ", creature->right_held->description);
    output(window, "in your right hand and ");
    if (creature->left_held == NULL)
        output(window, "nothing ");
    else
        output(window, "%s ", creature->left_held->description);
    output(window, "in your left.");
}



/* command_equipment()
   prints out a list of the player's gear
   Parameters:   void
   Return Value: void
   -------------------------------------------------------------------------- */

void command_equipment ( HWND window, const Creature * creature )
{
    if (creature == NULL)
        return;

    output(window, "You are wearing ");
    if (creature->equipment == NULL)
        output(window, "nothing.");
    else
    {
        if (creature->equipment->next == NULL)
            output(window, "%s.", creature->equipment->description);
        else if (creature->equipment->next->next == NULL)
            output(window, "%s and %s.", creature->equipment->description, creature->equipment->next->description);
        else
        {
            Item * gear = creature->equipment;
            while (gear->next != NULL)
            {
                output(window, "%s, ", gear->description);
                gear = gear->next;
            };
            output(window, "and %s.", gear->description);
        };
    };
}



/* TODO: investigate the determinal_ordinal usage */
void command_wear ( HWND window, Creature * creature, char * string )
{
    int ordinal = 1;
    char * item_name = NULL;

    if (string == NULL)
        return;
    else if (strcmp(string, "") == 0)
    {
        output(window, "What do you want to wear?");
        return;
    };

    /* if user typed "first", "second", "other", etc. */
    ordinal = determine_ordinal(&string);

    /* get the item name */
    item_name = &string[0];
    while (item_name[0] != 0x20)
    {
        item_name++;
    };
    item_name++;

    if (creature != NULL)
    {
        /* check the right hand */
        if (creature->right_held != NULL)
        {
            if (strncmp(item_name, creature->right_held->name, strlen(item_name)) == 0)
            {
                if (ordinal == 1)
                {
                    creature->right_held->next = creature->equipment;
                    creature->equipment = creature->right_held;
                    creature->right_held = NULL;
                    /* TODO: recalculate modifiers */
                    /* TODO: output variety of equipping messages, depending on armor */
                    output(window, "You equip %s.", creature->equipment->description);
                    return;
                };
            };
        };

        /* check the left hand */
        if (creature->left_held != NULL)
        {
            if (strncmp(item_name, creature->left_held->name, strlen(item_name)) == 0)
            {
                if (ordinal == 2)
                {
                    creature->left_held->next = creature->equipment;
                    creature->equipment = creature->left_held;
                    creature->left_held = NULL;
                    /* TODO: recalculate modifiers */
                    /* TODO: output variety of equipping messages, depending on armor */
                    output(window, "You equip %s.", creature->equipment->description);
                    return;
                };
            };
        };

        output(window, "You are not holding that.");
    };
}



void command_remove ( HWND window, Creature * creature, char * string )
{
    short ordinal = 0, i = 0;
    char * item_name = NULL;

    if (string == NULL)
        return;
    else if (strcmp(string, "") == 0)
    {
        output(window, "What do you want to remove?");
        return;
    };

    /* if user typed "first", "second", "other", etc. */
    ordinal = determine_ordinal(&string);

    /* get the item name */
    item_name = &string[0];
    while (item_name[i] != 0x20)
    {
        item_name++;
    };
    item_name++;

    /* TODO */
    if (creature != NULL)
    {
        Item * item = NULL;

        /* check the hands, if it's in them then say "You're already holding that" */

        /* check all equipment */

        /* find_item_in_equipment */
        /* if (item != NULL)
        {
            item_ordinal needs to be decremented by amount of counts in find_item_in_equipment 
        }; */

        /* check the inventory */
        item = find_item_in_inventory(item_name, creature->inventory, &ordinal);
        if (item != NULL)
        {
            /* TODO: remove item */
        };

        output(window, "You are not wearing that.");
    };
}



void command_attack ( HWND window, Creature * attacker, char * target_name )
{
    Item * attacking_object = NULL;
    Creature * defender = NULL;
    short ordinal = 1;
    double attack_speed = 0.0, to_hit_chance = 0.0, evasion_chance = 0.0;

    if (attacker == NULL)
        return;

    if (target_name == NULL)
        return;
    else if (strcmp(target_name, "") == 0)
    {
        output(window, "What do you want to attack?");
        return;
    };

    /* if user typed "first", "second", "other", etc. */
    ordinal = determine_ordinal(&target_name);

    defender = find_creature_in_room(target_name, attacker->location, &ordinal);
    if (defender == NULL)
    {
        output(window, "What do you want to attack?");
        return;
    };

    attacker->is_attacking = TRUE;

    /* 1. check the weapon */
    if (attacker->right_held == NULL && attacker->left_held == NULL)
        attacking_object = NULL;
    else if (attacker->right_held != NULL && attacker->left_held == NULL)
        attacking_object = attacker->right_held;
    else if (attacker->left_held != NULL && attacker->right_held == NULL)
        attacking_object = attacker->left_held;
    else if (attacker->right_held != NULL && attacker->left_held != NULL && attacker->left_held->subtype == BOW)
    {
        output(window, "You cannot fire a bow with something in your right hand.");
        return;
    }
    else if (attacker->right_held != NULL && attacker->left_held != NULL && attacker->right_held->subtype == BOW)
    {
        output(window, "You cannot fire a bow with something in your left hand.");
        return;
    }
    else
        attacking_object = attacker->right_held;


    /* 2. check the range */
    /* attacker's attack speed */
    attack_speed = calculate_attack_speed(window, attacker, attacking_object, TRUE);  /* TODO: remove TRUE, this is temporary */

    /* attacker's to hit */
    to_hit_chance = calculate_chance_to_hit(window, attacker, TRUE);  /* TODO: remove TRUE, this is temporary */

    /* defender's chance to evade */
    evasion_chance = calculate_chance_to_evade(window, defender);

    /* get defender's evasion allotment */
    /* determine if the evade was full evade, or took part of the blow */

    /* same thing for other defenses */

    /* attacker's damage */

    /* defender's damage mitigation */

    /* RECOVERY */

    /* actions greatly drain endurance, especially attacking or defending
     * the performance of an action is greatly reduced, especially attacking or defending
     * recovery period duration can be reduced
          o weight of the weapon vs. user's strength
          o constitution
          o skill in the weapon
          o dexterity 
    */
}



void command_swap ( HWND window, Creature * creature )
{
    if (creature == NULL)
        return;

    if (creature->right_held != NULL && creature->left_held != NULL)
    {
        Item * temp = NULL;

        output(window, "You move %s to your left hand and %s to your right.", creature->right_held, creature->left_held);
        creature->right_held = temp;
        creature->right_held = creature->left_held;
        creature->left_held = temp;
    }
    else if (creature->right_held != NULL && creature->left_held == NULL)
    {
        output(window, "You move %s to your left hand.", creature->right_held);
        creature->left_held = creature->right_held;
        creature->right_held = NULL;
    }
    else if (creature->left_held != NULL && creature->right_held == NULL)
    {
        output(window, "You move %s to your right hand.", creature->left_held);
        creature->right_held = creature->left_held;
        creature->left_held = NULL;
    }
    else
        output(window, "You are not holding anything.");
}



void command_look ( HWND window, Room * room, Creature * player, char * string )
{
    Creature * creature = NULL;
    Item * item = NULL;

    if (room == NULL)
        return;

    /* user simply typed "look" and is observing the room */
    if (strcmp(string, "") == 0)
    {
        output(window, "[%s]\r\n%s", room->name, room->description);

        /* if there are creatures in the room */
        if (room->creatures != NULL)
        {
            output(window, "\r\nYou also see");
            creature = room->creatures;

            if (creature->next == NULL)
                output(window, " %s.", creature->description);
            else
            {
                if (creature->next->next == NULL)
                    output(window, " %s and %s.", creature->description, creature->next->description);
                else
                {
                    while (creature != NULL)
                    {
                        if (creature->next != NULL)
                            output(window, " %s,", creature->description);
                        else
                            output(window, " and %s.", creature->description);

                        creature = creature->next;
                    };
                };
            };
        };
    }
    else
    {
        short ordinal = 1;

        /* if user typed "look at " */
        if (strlen(string) > 4 && string[0] == 'a' && string[1] == 't' && string[2] == ' ')
            string = string + 3;

        /* if user typed 'look in " */
        if (strlen(string) > 4 && string[0] == 'i' && string[1] == 'n' && string[2] == ' ')
        {
            string = string +3;

            /* if user typed "first", "second", "other", etc. */
            ordinal = determine_ordinal(&string);

            /* check containers */
            /* CONTAINER CHECK: Room */
            item = find_item_in_room(string, room, &ordinal);
            if (item != NULL && item->subtype == CONTAINER)
            {
                if (item->next == NULL)
                {
                    output(window, "Your %s is empty.", item->description);
                    return;
                }
                else if (item->next->next == NULL)
                {
                    output(window, "You see %s in your %s.", item->next->description, item->description);
                    return;
                }
                else if (item->next->next->next == NULL)
                {
                    output(window, "You see %s and %s in your %s.", item->next->next->description, item->next->description, item->description);
                    return;
                }
                else
                {
                    output(window, "You see %s, ");
                    while (item->next != NULL)
                    {
                        if (item->next->next->next == NULL)
                            output(window, "%s and %s in your %s.", item->next->next->description, item->next->description, item->description);
                        else
                            output(window, "%s, ", item->description);
                    };
                };
            }
            else
            {
                /* CONTAINER CHECK: Hands */
                item = find_item_in_hands(string, player, &ordinal);
                if (item != NULL && item->subtype == CONTAINER)
                {
                    if (item->next == NULL)
                    {
                        output(window, "Your %s is empty.", item->description);
                        return;
                    }
                    else if (item->next->next == NULL)
                    {
                        output(window, "You see %s in your %s.", item->next->description, item->description);
                        return;
                    }
                    else if (item->next->next->next == NULL)
                    {
                        output(window, "You see %s and %s in your %s.", item->next->next->description, item->next->description, item->description);
                        return;
                    }
                    else
                    {
                        output(window, "You see %s, ");
                        while (item->next != NULL)
                        {
                            if (item->next->next->next == NULL)
                                output(window, "%s and %s in your %s.", item->next->next->description, item->next->description, item->description);
                            else
                                output(window, "%s, ", item->description);
                        };
                    };
                }
                else
                {
                    /* CONTAINER CHECK: Inventory */
                    item = find_item_in_inventory(string, player->inventory, &ordinal);
                    if (item != NULL && item->subtype == CONTAINER)
                    {
                        if (item->next == NULL)
                        {
                            output(window, "Your %s is empty.", item->description);
                            return;
                        }
                        else if (item->next->next == NULL)
                        {
                            output(window, "You see %s in your %s.", item->next->description, item->description);
                            return;
                        }
                        else if (item->next->next->next == NULL)
                        {
                            output(window, "You see %s and %s in your %s.", item->next->next->description, item->next->description, item->description);
                            return;
                        }
                        else
                        {
                            output(window, "You see %s, ");
                            while (item->next != NULL)
                            {
                                if (item->next->next->next == NULL)
                                    output(window, "%s and %s in your %s.", item->next->next->description, item->next->description, item->description);
                                else
                                    output(window, "%s, ", item->description);
                            };
                        };
                    }
                    else
                        output(window, "What are you trying to look at?");
                };
            };
            return;
        };

        /* if user typed "first", "second", "other", etc. */
        ordinal = determine_ordinal(&string);

        /* check if the user is looking at a creature */
        creature = find_creature_in_room(string, room, &ordinal);
        if (creature != NULL && creature == player)
        {
            /* TODO: describe the player */
            output(window, "You see yourself, %s, <<description>>", creature->name);
        }
        else if (creature != NULL && creature != player)
        {
            output(window, "You see %s.", creature->description);
            display_health(window, creature);
        }
        else
        {
            /* check if the user is looking at an item in the room */
            item = find_item_in_room(string, room, &ordinal);
            if (item != NULL)
                output(window, "You see %s.", item->description);
            else
            {
                /* check if the user is looking at an item in their hands */
                item = find_item_in_hands(string, player, &ordinal);
                if (item != NULL)
                    output(window, "You see %s.", item->description);
                else
                {
                    /* check if the user is looking at an item in their inventory */
                    item = find_item_in_inventory(string, player->inventory, &ordinal);
                    if (item != NULL)
                        output(window, "You see %s.", item->description);
                    else
                    {
                        /* check if the user is looking at an item that they're wearing */
                        item = find_item_in_inventory(string, player->equipment, &ordinal);
                        if (item != NULL)
                            output(window, "You see %s.", item->description);
                        else
                            output(window, "What are you trying to look at?");
                    };
                };
            };
        };
    };
}



void command_health ( HWND window, Creature * player )
{
    if (player == NULL)
        return;

    display_health(window, player);
}
