#include "arguments.h"

#include <iostream>
#include <utility>
#include <fstream>
#include <string>
#include <sstream>

#include "constants.h"
#include "file_input.h"
#include "file_output.h"
#include "console_input.h"
#include "console_output.h"
#include "curses_input.h"
#include "curses_output.h"
#include "rng.h"

feature proc_args(int argc, char **argv,
                  std::unique_ptr<cursor>  &curse,
                  std::unique_ptr<input>   &in,
                  std::unique_ptr<output> &out,
                  std::unique_ptr<RNG>     &rng,
                  std::ifstream &data) {
        feature result = 0;
        std::string str;
        std::string fn_fin;
        std::string fn_fout;
        std::string fn_lout;
        std::string fn_save;
        std::string seed;

        for (int i = 1; i < argc; ++i) {
                str = argv[i];

                if (str == "-n") {
                        if (result & (FEATURE_IN_FILE | FEATURE_OUT_FILE))
                                return FEATURE_CONFLICT | i;

                        result |= FEATURE_NCURSES;
                } else if (str == "-r") {
                        if (result & FEATURE_READ_MAP)
                                return FEATURE_CONFLICT | i;

                        result |= FEATURE_RAND_MAP;
                } else if (str == "-c") {
                        result |= FEATURE_ENEMIES_CHASE;
                } else if (str == "-d") {
                        result |= FEATURE_DOORS;
                } else if (str == "-i") {
                        result |= FEATURE_INVENTORY | FEATURE_WALK_OVER;
                } else if (str == "-t") {
                        result |= FEATURE_THROW |
                                  FEATURE_INVENTORY |
                                  FEATURE_WALK_OVER;
                } else if (str == "-R") {
                        if (result & FEATURE_READ_MAP)
                                return FEATURE_CONFLICT | i;

                        result |= FEATURE_REVISIT;
                } else if (str == "-e") {
                        result |= FEATURE_EXTRA_STUFF;
                } else if (str == "-E") {
                        if (result & FEATURE_READ_MAP)
                                return FEATURE_CONFLICT | i;

                        result |= FEATURE_EXTRA_LEVELS;
                } else if (str == "-o") {
                        result |= FEATURE_WALK_OVER;
                } else if (str == "-m") {
                        if ((result & FEATURE_RAND_MAP) ||
                            (result & FEATURE_REVISIT) ||
                            (result & FEATURE_EXTRA_LEVELS) ||
                            (result & FEATURE_READ_MAP))
                                return FEATURE_CONFLICT | i;

                        result |= FEATURE_READ_MAP;
                        ++i;
                        str = argv[i];
                        data.open(str);
                } else if (str == "-s") {
                        ++i;
                        str = argv[i];

                        if (!seed.empty() && seed != str)
                                return FEATURE_CONFLICT | i;

                        seed = str;
                        result |= FEATURE_SEED;
                } else if (str == "-I") {
                        ++i;
                        str = argv[i];

                        if (!fn_fin.empty() && fn_fin != str)
                                return FEATURE_CONFLICT | i;

                        fn_fin = str;
                        result |= FEATURE_IN_FILE;
                } else if (str == "-O") {
                        ++i;
                        str = argv[i];

                        if (!fn_fout.empty() && fn_fout != str)
                                return FEATURE_CONFLICT | i;

                        fn_fout = str;
                        result |= FEATURE_OUT_FILE;
                } else if (str == "-h" || str == "--help") {
                        return FEATURE_LIST_ARGS;
                } else if (str == "--races") {
                        return FEATURE_LIST_RACES;
                } else if (str == "--enemies") {
                        return FEATURE_LIST_ENEMIES;
                } else if (str == "--potions") {
                        return FEATURE_LIST_POTIONS;
                } else if (str == "--commands") {
                        return FEATURE_LIST_COMMANDS;
                } else {
                        return FEATURE_PANIC | i;
                }
        }

        if (result & FEATURE_IN_FILE) {
                std::ifstream fin(fn_fin);

                if (!fin.good())
                        return FEATURE_PANIC_FILE | FEATURE_IN_FILE;

                in = std::make_unique<file_input>(std::move(fin));
        } else if (!(result & FEATURE_NCURSES))
                in = std::make_unique<console_input>(std::cin);

        if (result & FEATURE_OUT_FILE) {
                std::ofstream fout(fn_fout);

                if (!fout.good())
                        return FEATURE_PANIC_FILE | FEATURE_OUT_FILE;

                out = std::make_unique<file_output>(std::move(fout));
        } else if (!(result & FEATURE_NCURSES))
                out = std::make_unique<console_output>(std::cout);

        if (result & FEATURE_NCURSES) {
                curse = std::make_unique<cursor>();
                in = std::make_unique<curses_input>(curse.get());
                out = std::make_unique<curses_output>(curse.get());
        }

        if (result & FEATURE_SEED) {
                std::istringstream iss {seed};
                unsigned int tmp;

                if (!(iss >> tmp))
                        return FEATURE_PANIC_SEED;

                rng = std::make_unique<RNG>(tmp);
        } else {
                rng = std::make_unique<RNG>();
        }

        return result;
}

// IMPORTANT: This is meant for already panicking
void panic_args(feature panic) {
        using namespace std;

        if (panic & FEATURE_PANIC)
                cerr << "Invalid argument No. " << (panic ^ FEATURE_PANIC)
                     << "!" << endl;
        else if (panic & FEATURE_CONFLICT)
                cerr << "Argument No. " << (panic ^ FEATURE_CONFLICT)
                     << " conflicts with a previous argument!" << endl;
        else if (panic & FEATURE_PANIC_FILE) {
                switch (panic ^ FEATURE_PANIC_FILE) {
                case FEATURE_IN_FILE:
                        cerr << "Cannot open specified input file!" << endl;
                        break;

                case FEATURE_OUT_FILE:
                        cerr << "Cannot open specified output file!" << endl;
                        break;

                default:
                        cerr << "Something must have went really, really, wrong..."
                             << endl;
                        break;
                }
        } else if (panic & FEATURE_PANIC_SEED) {
                cerr << "Invalid seed!" << endl;
        } else
                cerr << "Something must have went really, really, wrong..."
                     << endl;
}

const char *ARGS_LIST = "\
-n         : Use ncurses for I/O\n\
-r         : Randomly generate maps. CANNOT BE USED WITH -m.\n\
-c         : Enemies chase the player (CAUTION: THEY CAN REALLY CHASE!!!)\n\
-d         : Enemies can go through doors (CAUTION: DO NOT ENABLE WITH CHASING!)\n\
-i         : Enable inventory (player can pick up potions, will turn on -o)\n\
-t         : Enable throw (will turn on -i)\n\
-R         : Enable revisiting levels. CANNOT BE USED WITH -m.\n\
-e         : Enable extra potions and races\n\
-E         : Enable extra levels. CANNOT BE USED WITH -m.\n\
-o         : Allows characters to go over gold and potions\n\
-m [file]  : Reads map data from file. CANNOT BE USED WITH -r or -R or -E.\n\
-s [seed]  : Sets initial seed to seed\n\
-I [file]  : Reads commands from file. CANNOT BE USED WITH -n.\n\
-O [file]  : Outputs to file. CANNOT BE USED WITH -n.\n\
-h/--help  : Displays options list (doesn't start a game)\n\
--races    : Displays playable characters list\n\
--enemies  : Displays enemies list\n\
--potions  : Displays potions list\n\
--commands : Displays available commands\n";

const char *COMMANDS_LIST = "\
Text-based commands:\n\
    Basic commands:\n\
        q   : quits game (in inventory, close inventory)\n\
        r   : restarts a new game\n\
        yes : confirms (in main menu) or use (in inventory)\n\
    In-menu commands:\n\
        s   : starts the game as a shade\n\
        d   : starts the game as a drow\n\
        v   : starts the game as a vampire\n\
        g   : starts the game as a goblin\n\
        t   : starts the game as a troll\n\
    Directional commands:\n\
        no  : north\n\
        so  : south\n\
        ea  : east\n\
        we  : west\n\
        ne  : northeast\n\
        nw  : northwest\n\
        se  : southeast\n\
        sw  : southwest\n\
    In-game commands:\n\
        f             : toggles whether or not enemies stop moving (will turn off 'd')\n\
        d             : toggles whether or not enemies stop moving and attacking (will turn off 'f')\n\
        u [direction] : try to use the potion indicated by direction\n\
        a [direction] : try to attack the enemy indicated by direction\n\
        i             : toggles inventory on and off\n\
        T [direction] : (in inventory) throw the selected potion towards direction\n\n\
ncurses-based commands:\n\
    Basic commands:\n\
        q   : quits game (in inventory, close inventory)\n\
        r   : restarts a new game\n\
        e   : confirms (in main menu) or use (in inventory)\n\
    Directional commands:\n\
        k   : north\n\
        j   : south\n\
        l   : east\n\
        h   : west\n\
        u   : northeast\n\
        y   : northwest\n\
        n   : southeast\n\
        b   : southwest\n\
    In-game commands:\n\
        f             : toggles whether or not enemies stop moving (will turn off 'd')\n\
        d             : toggles whether or not enemies stop moving and attacking (will turn off 'f')\n\
        u [direction] : try to use the potion indicated by direction\n\
        [direction]   : also tries to attack in the indicated direction\n\
        i             : toggles inventory on and off\n\
        t [direction] : (in inventory) throw the selected potion towards direction\n\
        >             : go down the stairs\n\
        <             : go up the stairs\n";

const char *RACES_LIST = "\
- Shade (125 HP, 25 Atk, 25 Def, 1/1 Hit Rate):\n\
      The most basic race.\n\n\
- Drow  (150 HP, 25 Atk, 15 Def, 1/1 Hit Rate):\n\
      All potions have their effect magnified by 1.5.\n\n\
- Vampire  (50 HP, 25 Atk, 25 Def, 1/1 Hit Rate):\n\
      Gains 5 HP on every successful attack;\n\
      Has no maximum HP.\n\n\
- Troll  (120 HP, 25 Atk, 15 Def, 1/1 Hit Rate):\n\
      Gains 5 HP every turn.\n\n\
- Goblin  (110 HP, 15 Atk, 20 Def, 1/1 Hit Rate):\n\
      Gains an additional 5 gold from every slain enemy.\n\n\
- T-800  (600 HP, 40 Atk, 50 Def, 2/3 Hit Rate):\n\
      All potions will give it rusty joints.\n\n\
- Mr. Goose  (130 HP, 25 Atk, 20 Def, never misses):\n\
      All potions are known at the beginning of the game;\n\n\
- Monk  (125 HP (starts with 100 HP), 70 Atk, 0 Def, 1/1 Hit Rate):\n\
      Gains 2 HP every turn.\n\n\
- Tavern Brawler  (120 HP, 20 Atk, 15 Def, 3/4 Hit Rate):\n\
      Has a 1/2 chance of attacking twice and 1/2 change of attacking three times.\n\n\
- Assassin  (100 HP, 30 Atk, 10 Def, 1/1 Hit Rate):\n\
      Upon a successful hit, has a 1/10 chance of assassinating the target.\n";

const char *ENEMIES_LIST = "\
- Human 'H' (140 HP, 20 Atk, 20 Def, 1/2 Hit Rate):\n\
      Upon death, drops 2 normal piles of gold.\n\n\
- Dwarf 'W' (100 HP, 20 Atk, 30 Def, 1/2 Hit Rate):\n\
      Vampires are allergic to them.\n\n\
- Elf 'E' (140 HP, 30 Atk, 10 Def, 1/2 Hit Rate):\n\
      Gets two attacks against every race except drow.\n\n\
- Orc 'O' (180 HP, 30 Atk, 25 Def, 1/2 Hit Rate):\n\
      Does 50% more damage to goblins.\n\n\
- Merchant 'M' (30 HP, 70 Atk, 5 Def, 1/2 Hit Rate):\n\
      Starts out neutral to all characters, can be angered by PC.\n\n\
- Dragon 'D' (150 HP, 20 Atk, 20 Def, 1/2 Hit Rate):\n\
      Always guards a treasure hoard.\n\n\
- Halfling 'L' (100 HP, 15 Atk, 20 Def, 1/2 Hit Rate):\n\
      Has an additional 50% chance to dodge PC's attack.\n\n\
- Viking 'V' (150 HP, 30 Atk, 25 Def, 1/3 Hit Rate):\n\
      Gets two attacks against every race.\n\n\
- Swordsman 'S' (100 HP, 25 Atk, 15 Def, 1/1 Hit Rate):\n\
      Attacks with finess.\n\n\
- Leprechaun 'l' (80 HP, 10 Atk, 15 Def, 1/2 Hit Rate):\n\
      Steals 3 pieces of gold from PC with every successful attack;\n\
      If PC doesn't have enough gold, hit instead with a strong attack of fixed Atk;\n\
      Upon death, drops all stolen gold plus 5 extra.\n\n\
- Witch 'Z' (100 HP, 20 Atk, 15 Def, 1/2 Hit Rate):\n\
      Upon a successful hit, has a 1/5 chance of applying a random potion onto PC (starts to take effect the next turn).\n\n\
- Hacker 'h' (90 HP, 15 Atk, 30 Def, 1/2 Hit Rate):\n\
      He has been in grinding in MC for too long.\n\n\
- Baby Dragon 'B' (140 HP, 20 Atk, 40 Def, 1/3 Hit Rate):\n\
      Not fully grown, can move;\n\
      Immune to all potions.\n";

const char *POTIONS_LIST = "\
- Restore Health (RH):\n\
      Instantly restore 10 HP.\n\n\
- Boost Atk (BA):\n\
      Increase ATK by 5 until exiting the level.\n\n\
- Boost Def (BD):\n\
      Increase DEF by 5 until exiting the level.\n\n\
- Poison Health (PH):\n\
      Instantly lose 10 HP.\n\n\
- Wound Atk (WA):\n\
      Decrease ATK by 5 until exiting the level.\n\n\
- Wound Def (BD):\n\
      Decrease DEF by 5 until exiting the level.\n\n\
- Continuous Restoration (CR):\n\
      Restores 3 HP every turn for 5 turns.\n\n\
- Savage Strike (SS):\n\
      Gets 1.25x basic ATK multiplier;\n\
      Gets 0.8x basic hit rate multiplier;\n\
      Lasts 20 turns.\n\n\
- Echoing Resilience (ER):\n\
      Restores 7 HP every turn;\n\
      Decrease basic ATK by 10;\n\
      Decrease basic DEF by 10;\n\
      Lasts 20 turns.\n\n\
- Tempest Tantrum (TT):\n\
      Instantly loses 25% of current HP;\n\
      Gets 3x final ATK multiplier;\n\
      Gets 0.5x final DEF multiplier;\n\
      Lasts 12 turns.\n\n\
- Berzerk Brew (BB):\n\
      Gets 2x basic ATK multiplier;\n\
      Gets 0.5x basic DEF multiplier;\n\
      Lasts 15 turns.\n\n\
- Borrow Life (BL):\n\
      Gains 50 HP upon use (can go over racial limit)\n\
      Loses 55 HP after 24 turns.\n\n\
- Fine Booze (FB):\n\
      Restores 2 HP every turn;\n\
      Gets 0.7x final hit rate multiplier;\n\
      Gives tavern brawlers the ability to never miss;\n\
      Lasts 12 turns.\n\n\
- Ironclad Ward (IW):\n\
      Gets 0.5x final ATK multiplier;\n\
      Gets 3x final DEF multiplier;\n\
      Gets 0.75x final hit rate multiplier;\n\
      Lasts 12 turns.\n";

void print_list(feature which) {
        if (which & FEATURE_LIST_ARGS)
                std::cout << ARGS_LIST;
        else if (which & FEATURE_LIST_COMMANDS)
                std::cout << COMMANDS_LIST;
        else if (which & FEATURE_LIST_ENEMIES)
                std::cout << ENEMIES_LIST;
        else if (which & FEATURE_LIST_POTIONS)
                std::cout << POTIONS_LIST;
        else if (which & FEATURE_LIST_RACES)
                std::cout << RACES_LIST;
}
