#include <irrlicht.h>
#include <IGUIButton.h>
#include "driverChoice.h"
#include <IGUIEnvironment.h>
#include <list>
#include <random>
#include <algorithm>
#include <iostream>
#include<tuple>
#include <string>

using namespace irr;
using namespace std;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#include <Windows.h>
#else
#include <unistd.h>
#endif

#define _IRR_MATERIAL_MAX_TEXTURES_   4

// Define some values that we'll use to identify individual GUI controls.
enum
{
    GUI_ID_NEW_GAME_BUTTON = 101,
    GUI_ID_LOAD_GAME_BUTTON,
    GUI_ID_LOAD_SCENARIO_BUTTON,
    GUI_ID_NET_GAME_BUTTON,
    GUI_ID_SETTINGS_BUTTON,
    GUI_ID_QUIT_BUTTON,
    GUI_ID_LAUNCH_BUTTON,
    GUI_ID_MENU_BUTTON,
    GUI_ID_SETTINGS_SAVE_BUTTON,
    GUI_ID_DRIVER_BOX
};
int view_window = 0;
//set up settings

struct SAppContext
{
    IrrlichtDevice* device;
    s32             counter;
    IGUIListBox* listbox;
    IGUIListBox* ListboxDriver;
};

/*uncomment for random map
std::list<tuple <int, int, int>> map_gen()
{
    int x;
    int y;
    std::list<tuple <int, int, int>> map;
    x = 256;
    y = 128;
    random_device rd;     // only used once to initialise (seed) engine
    srand(rd());    // random-number engine used
    uniform_int_distribution<int> uni(1,5); // guaranteed unbiased
    for (int tile_y = 1; tile_y <= y; ++tile_y)
    {
        //std::cout << "new Y row" << std::endl;
        for (int tile_x = 1; tile_x <= x; ++tile_x)
        {
            uncomment for random map
            //std::cout << "new X row" << std::endl;
            tuple<int, int, int> tile;
            int max;
            int tile_type;
            max = 5; //set the upper bound to generate the random number
            tile_type = rand()%max;
            tile = make_tuple(tile_x, tile_y, tile_type);
            map.push_back(tile);

        }     
    }
    return map;
}
*/

std::list<tuple <int, int, int>> map_gen()
{
    int x;
    int y;
    std::list<tuple <int, int, int>> map;
    std::list<std::string> premap;
    premap =
    {
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "  aaaa      aa    aaaaaa    aa    aa        aaaaaaaa        aaaa      aaaaaa      aa      aa  aa      aa    aa        aa            aa    aaaa              aa  ",
        "               ::        ::    ::    ::::::          ::::::      ::::        ::::    ::::        ::::    ::    ::::::    ::::::::::    ::      ::::::::::::     ",
        "             ::::::::::::::::::::::::::  :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ",
        "                   :::::::::::::::::::    :::::::::::::   ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  ",
        "                    ::::::::::::::::    t    :::::::::: t  :::::::::::::    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::         ",
        " aaaaaaaaaaaaaa      :::::::::::::::   ttt     :::::::       :::::::::    t   ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::       aaaa   ",
        "aaaaaaaaaaaaaaaaa     :::::::::::::::  t t        ::::          :::::      t       :::::::::        :::::::::::::::::::::::::::::::::::              aa aaaa   a",
        "aaaaaaaaaaaaaaaaaa     :::::::::::::::            ::     tta      :::                 ::::    tt       ::::::::::::::::::::::::::::             t    tataaa   aa",
        "aaaaaaaaaaaaaaaaaa     :::::::::::::::                  tp                tttttt       ::              :::::::::::::::::::::::::        t  t          t at    ta",
        "aaaaaaaaaaaaaaaaat     :::::::::::::::::               tp               ttpptttttt      :               :::::::::::::::::::::::     tt                        tt",
        "tttaaaaaaaaaaaaaat    :::::::::::::::      tttt        pp     tt     tttttppttttt                              ::::::::::::::      tt    t     p  t   ttt      t",
        " tttaaaaaaaaaaaatt  :::::::::::::       pptttttttt           ttt tttttttttfffftt   ttt        ttttt              :::      ::           tttt                     ",
        "  tttaaaaaaaaaatt  ::::::              gppptpppttttt      ff ppt ttttfttftffffttffttttttt   tttftftttt           :::                   tppt           t a       ",
        "   tttaaaaaaaatt :::  ::   h          pphgffggpppttt   pttffffpggpptfftttffffffffffgggpfffffffffffpppttttttt     ::  ttt        tttt                  tttta     ",
        ":   tttaaaaaatt  :t    :       h     gphhgffggppppt   tttffffffggggpffffffffffffffpgggpfffpffffffffpppttpttttt    : ttttttttttttttttt           tttt    ttaa    ",
        "::   tttaaaatt  :: tft       h      gghpff ggg+fpp  ppffffhhfffgppggfffffffpffffffppppppffffggffffffffffgpptpptttt:  ttptttpttpttttpttttttttpttttttp p    ptaa  ",
        ":::   ttaaatt  ::: gg      hhh     hgmhpf   ggffppppppffffhfffggggpgffffpffffffffffpppppffffpgfffffpffffggppppttt :: tttttptttttttppttptttttttttttpp       paaa ",
        "::::  ttaatt  ::::          pg    ffhhfg    g+f+ffpffffffhfffgggppggpfffffgppffpffffpppfffffffppffffffffffffpp    : ttttttttttppffffffppttpttttppp   p    pptp  ",
        "::::  ttttt  :::::::::  gg  gp    ffggg    gggffffffpfffhmffggggppgpppffffgfffffffffpppffffffffpffffffffffff   :::: ttpptpttttppfffffffptpppttttp            pt ",
        ":::::  ttt  :::::::::: fpg  ff     gggg      gffppggppffhmffggggssffppgfffffffffpffpppppffpffgffffgggg  ff   :::::   tppptttppfffffgpfffffffppttpt              ",
        "::::::  tt ::::::::::  hg  hff       g    ggggffggpgggffhmfffggggpgfppggffffffffpppppfppffffffffffgg    ff :::::      tpppttpfffffffffffffffppttppp        tpp  ",
        " ::::::    ::::::::::     gpggg    g      ggffffgpgggfpfhmffffggggpgpppppffpffgggpgpffffffffgggggg     fhh ::::::  pppp p   ppggppffppffffffffffppppttp  t tp   ",
        " :::::::::::::::::::::   gsgggg    gg   ggggfffggggggffffhfffgggggggpfggppffffggg+ggffffffffggg        phh ::::::              gppffgpfffgfffpfffpppttp  pttppp ",
        "  ::::::::::::::::::::          ggggpppgggffffggffggggppffhfggffffggffffgffffffffgpppppppppgg          hpp  ::::::       :::::    ggffffffffffgfffppptttttppfffg",
        "p  :::::::::::::::::::   ggggggggggggpppggffffggffggggppfffhggffffggffffggffffffggppppppppgggg    p     p   :::::::::::::::::::::  ggffffffffffffffpppttttpfffff",
        "pp  :::::::::::::::::::    ggpggggpgggggggggffgpggppggggpppppggggpppppppggffpppgggppdpppppppppgg  p        :::::::::::::::::::::::  pphhfffggfffffffffppggffffff",
        "p     :::::::::::::::::    gpgggghghhgggggggffgggpppggppppppppggpppppppppgfgppppgpppddppppppppgg  p      :::::::::::::::::::::::::  pphhffggggfffgffffppggfffffg",
        "  pp   :::::::::::::::     ggggghmmmhhggpppppfggppppppp ggpppddddpgpppgpppgppppdddddppggppppggg        :::::::::::::::::::::::::::  ggffhhggggppgpgpp+pfffpppf  ",
        "  pp    :::::::::::::     ghggppgghghhggppgppg  ppgppp  ggpp pddpppgppppppppppdddpddppggppppgg    pp  :::::::::::::::::::::::::::: gggffhhppgppppppp+++f++ggggg ",
        "       :::::::::::::: ggppppmg    hg   gppppgg  gp pgg pppppddppppppppgpppgpppddddppggppggggg    ghg  :::::::::::::::::::::::::::  fgpgghhggggppggppggg+ggggggff",
        "f   ::::::::::::::::: ggppppph    gh    pppggg      gg  pppppdpgppppppgppppppppddpppggppgggg         :::::::::::::::::::::::::::: ffggghmhgggggggggpgpg+gggpgggf",
        "   :::::::::::::::::: ppphppg      gh   ggpgp       gg   ppppppppppgppppppppgpppppg   gggg     pm  :::::::::::::::::::::::::::::: fgpghhmmhgppggggpgggggffggggf ",
        "  :::::::::::::   :::  ppppp     h  gh  ggp  gppggppgg   pppgppppppppppppppppgppppgg   gg      pm  :::::::::::::::::::::::::::::: ffgphhmmhgppgggggggpggpfgggg  ",
        " :::::::::::::  g :::: hppp      g  g g gg  pgffppppppppppppppgphhhhppppgggpppppppgg   gg   gpmhp ::::::::::::::::::::::::::::::: fggghhmhpppggggggggggghhgg    ",
        " ::::::::::::::   ::::             gp    g  ggffppppppppppgpppgghhhhhpppggggppppppgg       gppmg  ::::::::::::::::::::::::::::::: pfpghhhpppppgggggggpghhggg    ",
        ":::::::::::::::::::::  hgggggggg              pppppppppppphhphhhhhhhhhppggggpppppgggg      g     ::::::::::::::::::::::::::::::::  fggppppppggggppgggghhgg     :",
        ":::::::::::::::::::   gggppppgggg             pppppppppppppphhhhmmmhhmhhggggpppggpggg        :::::::::::::::::::::::::::::::::::::  gggppphppgggppggghhgg    :::",
        "::::::::::::::::::  ppgppphhhhppgggg   ggggpppppppddpp   ppphhmmmmmmmmhhpgggggggpppggg       ::::::::::::::::::::::::::::::::::::::  pggpppddpppgggggggg   :::::",
        ":::::::::::::::::  ppppppdddddphhppgggggggpgppppppdddp    pphhhhhmmmmmhhppggggggpgppgg      :::::::::::::::::::::::::::::::::::::::: gpggppdddppgggggggg  :::   ",
        "::::::::::::::::: pddppdddddddddddpppppppppp  pddddddddp    ggghhhhhmmhhhggggppggggggg     ::::::::::::::::::::::::::::::::::::::::: pgggpddddppg     gg :::: g ",
        "::::::::::::::::: pppdddddddpddddddpppdpppppp   ddpdddddd    gggghhhhhhhgggpgpppggggg      :::::::::::::::::::::::::::::::::::::::::  gg ppdddpg      jg  :::   ",
        "::::::::::::::::: ppdddpddddddddddddddddpppdpp   dpddddddpdp  pggppgghhggggpppggggg       :::::::::::::::::::::::::   ::::::::::::::: pp  gpdppg       gs  :::::",
        "::::::::::::::::: ppddddddpdppdddpdddddpppddppp   pddddddddd  ppggggggggggggpgggg    g    ::::::::::::::::::::::::  h   ::::::::::::: pp  gpppgg  fj         :::",
        "::::::::::::::::: ppddddppppppppddppdddddppddddd   pdddddp     ghgffgg    ggggg   ::     ::::::::::::::::::::::::::   g  ::::::::::::  pp  ggggg  ff   ppp     :",
        "   :::::::::::::: ppppppppppppppppdpdddddpppddddp   ppdd   ::  gphffgg    ggg  :::: gg  ::::::::::::::::::::::::::::     :::::::::::::      gggggggj ::  gpp    ",
        "gp :::::::::::::: ggggppppppppppppppddpdppppppddpp  p    ::::   phggg     jggp :::: pg  :::::::::::::::::::::::::::::::::::::::::::::::::::   ggjpgg ::   pggp  ",
        "    ::::::::::::: ggggpppppgpgppppppgpdpppppppddpdp   d  :::::  phfg      jjpp :::: j    :::::::::::::::::::::::::::::::::::::::::::::::::::  ggppgg  :::       ",
        ": g :::::::::::::  pppggppgpgpggggggppppggppppdddpppddd  :::::  phgg      g ss ::::    j ::::::::::::::::::::::::::::::::::::::::::::::::::::    gjpg   ::::::::",
        ":   ::::::::::::::  ppggppggggggggggppgpggghgppddddpdd  :::::::  pg       g     ::::      :::::::::::::   ::::::::::::::::::::::::::::::::::::::  ppgg   ::    :",
        "   ::::::::::::::::   ppffjjffffffgggfggfffhhhppddpdd  :::::::::    p     gg           jp ::::::::::::: g :::::::::::::::::::::::::::::::::::::::   ggpp    gg  ",
        "g   :::::::::::::::::   ffjjff  ffggffggpfhhmmppddpp  :::::::::::: pg   p  g      gg   pg :::::::::::::   ::::::::::::::::::::::::::::::::::::::::: g  g  gggg g",
        "ggg   :::::::::::::::::         ggffjjjjffhhmhgppp   :::::::::::::    : jg       ppf      :::::::::::::::    ::::::::::::::::::::::::::::::::::::::    gpgggfffg",
        "ggggg  :::::::::::::::::::::::: ggjjfgfjffhhmhgppp ::::::::::::::::::::  gp    pjjpf      :::::::::::::::: j :::::::::::::::::::::::::::::::::::::::::  ggggfgjj",
        "jfffgg    ::::::::::::::::::::: gggfffjffghhp+gggg :::::::::::::::::::::  gpf   jgg         ::::::::::::::   ::::::::::::::::::::::::::::::::::::::::: ghhgpgggg",
        "jjffggggg    :::::::::::::::::: gggggg+jjhhhpggpgg ::::::::::::::::::::::  jf        gj   g       ::::::::::::::::::::::::::::::::::::::::::::::::::: gghhhggggg",
        "jgjfggppffgg  :::::::::::::::::  gggggjjjjjjgppgf  :::::::::::::::::::::::           g g  gggjpfg :::::::::::::::::::::::::::::::::::::::::::::::::: ggghhpgjjjj",
        "jjgjpjjpffggp  ::::::::::::::::: gggggjjjjjjggggg :::::::::::::::::::::::::: gg         ::   gfjgg   ::::::::::::::::::::::::::::::::::::::::::::::: ggghhpgjjjj",
        "jjggjjffffgggg  ::::::::::::::::  gggpfffgggffgg   :::::::::::::::::::::::::  ggg  g  g ::::  fg gpp   ::::::::::::::::::::::::::::::::::::::::::::: ggghhfffffj",
        "jjgjjjjfffgggpp ::::::::::::::::: ggppffpgggffff     ::::::::::::::::::::::::           :::::               ::::::::::::::::::::::::::::::::::::::::  gghhfffjjf",
        "jgjjjjffgggggpp ::::::::::::::::  ggppppgggghhff   g ::::::::::::::::::::::::::::::         ::  g   ::::  g  ::::::::::::::::::::::::::::::::::::::::  ggghhfffj",
        "fggffffpggggpp  :::::::::::::::: pgpppppgggghhff  gg :::::::::::::::::::::::::::::      gggg    gg   ::::  g :::::::::::::::::::::::::::::::::::::::::  gghhfpfj",
        "ffffgggggpggp  ::::::::::::::::  pppppgggppggf   ppf :::::::::::::::::::::::::::       ppppggg  ggg   :::      :::::::::::::::::::::::::::::::::::::::: ggggmmgg",
        "pffpggggppgg  :::::::::::::::::  dpppppgppgggf   ffp ::::::::::::::::::::::::::       pppppppgggggg   ::        :::::::::::::::::::::::::::::::::::::::   ggmmgg",
        "ggggggpppggg ::::::::::::::::::   ddpppppppggf   gg  ::::::::::::::::::::::::       ppdddddpppggffg       p     :::::::::::::::::::::::::::::::::::::::::  gppdd",
        "gggggpppgggg :::::::::::::::::::  ddpppdppgggf  pp  :::::::::::::::::::::::::    pppppdddddddppgffg             ::::::::::::::::::::::::::::::::::::::::::  ppdd",
        "pgggppfffff  :::::::::::::::::::   ddpdddpppgg    :::::::::::::::::::::::::::   ppddddddddpddddpgfff    ::      ::::::::::::::::::::::::::::::::::::::::::: gghp",
        "ppggppfgff  :::::::::::::::::::::  dpppdddpgg  :::::::::::::::::::::::::::::::  ppdddddpddddddppppfff  :::      ::::::::::::::::::::::::::::::::::::::::::: ghhh",
        "pppgpfffp  ::::::::::::::::::::::   ppppppggf  ::::::::::::::::::::::::::::::    gpddddddddpddppppppgg :::      ::::::::::::::::::::::::::::::::::::::::::: phmh",
        "pppppfff   :::::::::::::::::::::::   pppppgg  :::::::::::::::::::::::::::::::::   ppddpdddddddppppppgg ::::     ::::::::::::::::::::::::::::::::::::::::::: ppmh",
        "pppppggg   :::::::::::::::::::::::   ggg      ::::::::::::::::::::::::::::::::::  gpddddppppppppppggg  ::::     ::::::::::::::::::::::::::::::::::::::::::: ghmh",
        "ppppgggf   ::::::::::::::::::::::::      ::::::::::::::::::::::::::::::::::::::: ggpppddpppppgppppgg  ::::::    ::::::::::::::::::::::::::::::::::::::::::: ghmh",
        "ppggggg   :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: gppgg      ggggggg  ::::::     ::::::::::::::::::::::::::::::::::::::::::: phhg",
        "ppgggg    ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::             gg gg  ::::::   gh  :::::::::::::::::::::::::::::::::::::::::  phgg",
        "ppggg     ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::     :::::       ::::       gg   ::::::::::::::::::::::::::::::::::::::  phmpd",
        "ppgggg    ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  ff :::        gg       ::::::::::::::::::::::::::::::::::  phmdd",
        "ppgg     :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: gg :::     ph p           :::::::::::::::::::::::::::::::  pggpp",
        "ppg     :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::    ::::   php              :::::::::::::::::::::::::::::  ppggpp",
        "gg     ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  pgg               :::::::::::::::::::::::::::::  ggpppp",
        "g     ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ppg             :::::::::::::::::::::::::::::::::  gpppp",
        "      :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::             ::::::::::::::::::::::::::::::::::::: ggppg ",
        "     ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::          :::::::::::::::::::::::::::::::::::::::: gpppgg",
        "     ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::       ::::::::::::::::::::::::::::::::::::::::::: ppgpp ",
        "  g ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::   :::::::::::::::::::::::::::::::::::::::::::::: pppp  ",
        "    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  pppf ",
        "  ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::   pf ",
        "   :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::    ",
        "   ::::    ::    ::::    ::            ::::::::    ::::    ::::::    ::::        ::::::      ::      ::        ::::::::::::::    ::::::::      ::::::::         ",
        "aa      aa    aa      aa    aaaaaaaaaa          aa      aa        aa      aaaaaa        aaaa    aaaa    aa  aa                aa          aaaa          aaaaaa  ",
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    };
    std::string string;
    char charactor;
    int tile_x;
    int tile_y;
    tile_y = 0;
    tile_x = 0;
    for (std::string string : premap)
    {
        tile_x = 0;
        ++tile_y;
        //std::cout << "new Y row" << std::endl;
        for (char charactor : string)
        {
            --tile_x;
            //std::cout << "new X row" << std::endl;
            tuple<int, int, int> tile;
            int tile_type;
            //std::cout << string << std::endl;
            if (charactor == 'g')
            {
                tile_type = 0;
            }
            else if (charactor == 'd')
            {
                tile_type = 1;
            }
            else if (charactor == 'h')
            {
                tile_type = 2;
            }
            else if (charactor == 'm')
            {
                tile_type = 3;
            }
            else if (charactor == '+')
            {
                tile_type = 4;
            }
            else if (charactor == ' ')
            {
                tile_type = 5;
            }
            else if (charactor == ':')
            {
                tile_type = 6;
            }
            else if (charactor == 'p')
            {
                tile_type = 7;
            }
            else if (charactor == 't')
            {
                tile_type = 8;
            }
            else if (charactor == 'a')
            {
                tile_type = 9;
            }
            else if (charactor == 'f')
            {
                tile_type = 10;
            }
            else if (charactor == 'j')
            {
                tile_type = 11;
            }
            else if (charactor == 's')
            {
                tile_type = 12;
            }
            tile = make_tuple(tile_x, tile_y, tile_type);
            map.push_back(tile);

        }
    }
    return map;
}

int tile_dir(std::list<tuple <int, int, int>> game_map, tuple<int, int, int> tile)
{
    bool dn = false;
    bool de = false;
    bool ds = false;
    bool dw = false;
    int d = 0;
    int tile_x = get<0>(tile);
    int tile_y = get<1>(tile);
    int tile_type = get<2>(tile);
    for (auto test_tile : game_map)
    {
        if(get<0>(test_tile) == tile_x and get<1>(test_tile) == tile_y - 1 and get<2>(test_tile) == tile_type)
        {
            dn = true;
        }
        if (get<0>(test_tile) == tile_x + 1 and get<1>(test_tile) == tile_y and get<2>(test_tile) == tile_type)
        {
            de = true;
        }
        if (get<0>(test_tile) == tile_x and get<1>(test_tile) == tile_y + 1 and get<2>(test_tile) == tile_type)
        {
            ds = true;
        }
        if (get<0>(test_tile) == tile_x - 1 and get<1>(test_tile) == tile_y and get<2>(test_tile) == tile_type)
        {
            dw = true;
        }
    }
    if (dn == true)
    {
        d = 15;
    }
    if (de == true)
    {
        d = 8;
    }
    if (ds == true)
    {
        d = 12;
    }
    if (dw == true)
    {
        d = 14;
    }
    if (dn == true and de == true)
    {
        d = 7;
    }
    if (dn == true and ds == true)
    {
        d = 11;
    }
    if (dn == true and dw == true)
    {
        d = 13;
    }
    if (de == true and ds == true)
    {
        d = 4;
    }
    if (de == true and dw == true)
    {
        d = 6;
    }
    if (ds == true and dw == true)
    {
        d = 10;
    }
    if (dn == true and de == true and ds == true)
    {
        d = 3;
    }
    if (dn == true and de == true and dw == true)
    {
        d = 5;
    }
    if (dn == true and ds == true and dw == true)
    {
        d = 9;
    }
    if (ds == true and de == true and dw == true)
    {
        d = 2;
    }
    if (dn == true and de == true and ds == true and dw)
    {
        d = 1;
    }
    return d;
}

int coast_dir(std::list<tuple <int, int, int>> game_map, tuple<int, int, int> tile)
{
    bool dn = false;
    bool de = false;
    bool ds = false;
    bool dw = false;
    int d = 1;
    int tile_x = get<0>(tile);
    int tile_y = get<1>(tile);
    int tile_type = get<2>(tile);
    for (auto test_tile : game_map)
    {
        if (get<0>(test_tile) == tile_x and get<1>(test_tile) == tile_y - 1 and get<2>(test_tile) != tile_type and get<2>(test_tile) != 6)
        {
            dn = true;
        }
        if (get<0>(test_tile) == tile_x + 1 and get<1>(test_tile) == tile_y and get<2>(test_tile) != tile_type and get<2>(test_tile) != 6)
        {
            de = true;
        }
        if (get<0>(test_tile) == tile_x and get<1>(test_tile) == tile_y + 1 and get<2>(test_tile) != tile_type and get<2>(test_tile) != 6)
        {
            ds = true;
        }
        if (get<0>(test_tile) == tile_x - 1 and get<1>(test_tile) == tile_y and get<2>(test_tile) != tile_type and get<2>(test_tile) != 6)
        {
            dw = true;
        }
    }
    if (dn == true)
    {
        d = 2;
    }
    if (de == true)
    {
        d = 9;
    }
    if (ds == true)
    {
        d = 5;
    }
    if (dw == true)
    {
        d = 3;
    }
    if (dn == true and de == true)
    {
        d = 10;
    }
    if (dn == true and ds == true)
    {
        d = 6;
    }
    if (dn == true and dw == true)
    {
        d = 4;
    }
    if (de == true and ds == true)
    {
        d = 13;
    }
    if (de == true and dw == true)
    {
        d = 11;
    }
    if (ds == true and dw == true)
    {
        d = 7;
    }
    if (dn == true and de == true and ds == true)
    {
        d = 14;
    }
    if (dn == true and de == true and dw == true)
    {
        d = 12;
    }
    if (dn == true and ds == true and dw == true)
    {
        d = 8;
    }
    if (ds == true and de == true and dw == true)
    {
        d = 15;
    }
    if (dn == true and de == true and ds == true and dw)
    {
        d = 0;
    }
    return d;
}

class MyEventReceiver : public IEventReceiver
{
public:
    MyEventReceiver(SAppContext& context) : Context(context)
    {
        for (u32 i = 0; i < KEY_KEY_CODES_COUNT; ++i)
            KeyIsDown[i] = false;
    }

    struct SMouseState
    {
        core::position2di Position;
        bool LeftButtonDown;
        SMouseState() : LeftButtonDown(false) { }
    } MouseState;

    virtual bool OnEvent(const SEvent& event)
    {
        // Remember the mouse state

        if (event.EventType == irr::EET_MOUSE_INPUT_EVENT)
        {
            switch (event.MouseInput.Event)
            {
            case EMIE_LMOUSE_PRESSED_DOWN:
                MouseState.LeftButtonDown = true;
                break;

            case EMIE_LMOUSE_LEFT_UP:
                MouseState.LeftButtonDown = false;
                break;

            case EMIE_MOUSE_MOVED:
                MouseState.Position.X = event.MouseInput.X;
                MouseState.Position.Y = event.MouseInput.Y;
                break;

            default:
                // We won't use the wheel
                break;
            }
        }

        if (event.EventType == EET_GUI_EVENT)
        {
            s32 id = event.GUIEvent.Caller->getID();
            IGUIEnvironment* env = Context.device->getGUIEnvironment();
            ISceneManager* smgr = Context.device->getSceneManager();
            IVideoDriver* driver = Context.device->getVideoDriver();

            switch (event.GUIEvent.EventType)
            {
            case EGET_BUTTON_CLICKED:
                switch (id)
                {
                case GUI_ID_QUIT_BUTTON:
                    Context.device->closeDevice();
                    return true;

                case GUI_ID_NEW_GAME_BUTTON:
                {
                    view_window = 1;
                    env->clear();
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() - 250), (driver->getViewPort().getHeight() - 50), (driver->getViewPort().getWidth() - 50), (driver->getViewPort().getHeight() - 50) + 32), 0, GUI_ID_LAUNCH_BUTTON,
                        L"Launch!", L"Launch a new game");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() - 500), (driver->getViewPort().getHeight() - 50), (driver->getViewPort().getWidth() - 300), (driver->getViewPort().getHeight() - 50) + 32), 0, GUI_ID_MENU_BUTTON,
                        L"Back", L"Back to main menu");
                }
                return true;

                case GUI_ID_SETTINGS_SAVE_BUTTON:
                {
                    IrrlichtDevice* NullDevice = createDevice(irr::video::EDT_NULL);
                    irr::io::IXMLWriter* xwriter = NullDevice->getFileSystem()->createXMLWriter("client/settings.xml");
                    if (!xwriter)
                        return false;
                    xwriter->writeXMLHeader();
                    xwriter->writeElement(L"client");
                    xwriter->writeLineBreak();
                    xwriter->writeElement(L"video");
                    xwriter->writeLineBreak();
                    if (Context.ListboxDriver->getSelected() == 0)
                    {
                        xwriter->writeElement(L"setting", false, L"driver", L"Software");
                        xwriter->writeLineBreak();
                    }
                    if (Context.ListboxDriver->getSelected() == 1)
                    {
                        xwriter->writeElement(L"setting", false, L"driver", L"OpenGL");
                        xwriter->writeLineBreak();
                    }
                    if (Context.ListboxDriver->getSelected() == 2)
                    {
                        xwriter->writeElement(L"setting", false, L"driver", L"DirectX9");
                        xwriter->writeLineBreak();
                    }
                    if (Context.ListboxDriver->getSelected() == 3)
                    {
                        xwriter->writeElement(L"setting", false, L"driver", L"BurningsVideo");
                        xwriter->writeLineBreak();
                    }
                    xwriter->writeLineBreak();
                    xwriter->writeClosingTag(L"video");
                    xwriter->writeLineBreak();
                    xwriter->writeClosingTag(L"client");
                    xwriter->drop();
                    return true;
                }

                case GUI_ID_MENU_BUTTON:
                {
                    view_window = 0;
                    env->clear();
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) - 250, 400, (driver->getViewPort().getWidth() / 2) - 50, 400 + 32), 0, GUI_ID_NEW_GAME_BUTTON,
                        L"New Game", L"Launch a new game");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) - 250, 440, (driver->getViewPort().getWidth() / 2) - 50, 440 + 32), 0, GUI_ID_LOAD_GAME_BUTTON,
                        L"Load Game", L"Load a saved game");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) - 250, 480, (driver->getViewPort().getWidth() / 2) - 50, 480 + 32), 0, GUI_ID_LOAD_SCENARIO_BUTTON,
                        L"Load Scenario", L"Loads a prebuilt map");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) + 50, 400, (driver->getViewPort().getWidth() / 2) + 250, 400 + 32), 0, GUI_ID_NET_GAME_BUTTON,
                        L"Network Game", L"Join a network game");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) + 50, 440, (driver->getViewPort().getWidth() / 2) + 250, 440 + 32), 0, GUI_ID_SETTINGS_BUTTON,
                        L"Settings", L"Client Settings");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) + 50, 480, (driver->getViewPort().getWidth() / 2) + 250, 480 + 32), 0, GUI_ID_QUIT_BUTTON,
                        L"Exit", L"Quit");
                    smgr->clear();
                    ICameraSceneNode* camera = 0;
                    camera = smgr->addCameraSceneNode(0, core::vector3df(0, 0, 50),
                        core::vector3df(0, 0, 0));
                    // create test earth
                    ISceneNode* earth = 0;
                    IAnimatedMesh* earthMesh = smgr->getMesh("client/earth.x");
                    if (earthMesh)
                    {
                        //perform various task with the mesh manipulator
                        scene::IMeshManipulator* manipulator = smgr->getMeshManipulator();

                        // create mesh copy with tangent informations from original earth.x mesh
                        scene::IMesh* tangentSphereMesh =
                            manipulator->createMeshWithTangents(earthMesh->getMesh(0));

                        // scale the mesh by factor 10
                        core::matrix4 m;
                        m.setScale(core::vector3df(20, 20, 20));
                        manipulator->transform(tangentSphereMesh, m);

                        earth = smgr->addMeshSceneNode(tangentSphereMesh);
                        earth->setPosition(core::vector3df(0, 0, 0));

                        earth->setMaterialTexture(0,
                            driver->getTexture("client/earth_intro.jpg"));

                        // load heightmap, create normal map from it and set it
                        /*video::ITexture* earthNormalMap = driver->getTexture("client/earthbump.jpg");
                        if (earthNormalMap)
                        {
                            driver->makeNormalMapTexture(earthNormalMap, 20.0f);
                            earth->setMaterialTexture(1, earthNormalMap);
                            earth->setMaterialType(video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA);
                        }
                        if you want a texured earth, uncomment this code.
                        */

                        // adjust material settings
                        earth->setMaterialFlag(video::EMF_FOG_ENABLE, true);

                        // add rotation animator
                        scene::ISceneNodeAnimator* anim =
                            smgr->createRotationAnimator(core::vector3df(0, 0.1f, 0));
                        earth->addAnimator(anim);
                        anim->drop();

                        // drop mesh because we created it with a create.. call.
                        tangentSphereMesh->drop();
                    }

                    // add white light
                    smgr->addLightSceneNode(0, core::vector3df(-50, 0, 25),
                        video::SColorf(1.0f, 1.0f, 1.0f));

                    // set ambient light
                    smgr->setAmbientLight(video::SColor(0, 100, 100, 100));
                }
                return true;

                case GUI_ID_LOAD_SCENARIO_BUTTON:
                {
                    // There are some options for the file open dialog
                    // We set the title, make it a modal window, and make sure
                    // that the working directory is restored after the dialog
                    // is finished.
                    env->addFileOpenDialog(L"Please choose a scenario map.", true, 0, -1, true);
                    return true;
                }

                case GUI_ID_NET_GAME_BUTTON:
                {
                    Context.counter += 30;
                    if (Context.counter > 200)
                        Context.counter = 0;

                    IGUIWindow* window = env->addWindow(
                        rect<s32>(100 + Context.counter, 100 + Context.counter, 350 + Context.counter, 200 + Context.counter),
                        false, // modal?
                        L"Network Game");

                    env->addStaticText(L"This Feature doesn't work yet .Please close me",
                        rect<s32>(20, 35, 230, 50),
                        true, // border?
                        false, // wordwrap?
                        window);

                }
                return true;

                case GUI_ID_SETTINGS_BUTTON:
                {
                    Context.counter += 30;
                    if (Context.counter > 200)
                        Context.counter = 0;

                    IGUIWindow* window = env->addWindow(
                        rect<s32>(100 + Context.counter, 100 + Context.counter, 350 + Context.counter, 400 + Context.counter),
                        false, // modal?
                        L"Client Settings");

                    env->addStaticText(L"Please select a driver",
                        rect<s32>(20, 35, 230, 50),
                        true, // border?
                        false, // wordwrap?
                        window);

                    Context.ListboxDriver = env->addListBox(rect<s32>(20, 60, 230, 180), window, GUI_ID_DRIVER_BOX, true);

                    //add all available options to the driver choice listbox
                    Context.ListboxDriver->addItem(L"Software");
                    Context.ListboxDriver->addItem(L"OpenGL");
                    Context.ListboxDriver->addItem(L"Directx 9");
                    Context.ListboxDriver->addItem(L"BurningsVideo");

                    env->addButton(rect<s32>(20, 200, 230, 200 + 32), window, GUI_ID_SETTINGS_SAVE_BUTTON,
                        L"Save", L"Save Settings");
                }
                return true;

                case GUI_ID_LOAD_GAME_BUTTON:
                {
                    //Context.listbox->addItem(L"Load Game");
                    // There are some options for the file open dialog
                    // We set the title, make it a modal window, and make sure
                    // that the working directory is restored after the dialog
                    // is finished.
                    env->addFileOpenDialog(L"Please choose a save file.", true, 0, -1, true);
                    return true;
                }

                case GUI_ID_LAUNCH_BUTTON:
                {
                    view_window = 2;
                    env->clear();
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() - 250), (driver->getViewPort().getHeight() - 50), (driver->getViewPort().getWidth() - 50), (driver->getViewPort().getHeight() - 50) + 32), 0, GUI_ID_MENU_BUTTON,
                        L"Back", L"Back to main menu");
                    smgr->clear();
                    const scene::IGeometryCreator* geo = smgr->getGeometryCreator();
                    ICameraSceneNode* camera = smgr->addCameraSceneNode(0, core::vector3df(0, 10, 17),
                        core::vector3df(0, -1, 12.5));
                    std::list<tuple <int, int, int>> game_map;
                    game_map = map_gen();
                    ISceneNode* map_node = smgr->addEmptySceneNode();
                    for(auto tile : game_map)//tuple<int, int, int> tile = game_map.begin(); tile != game_map.end(); ++tile)
                    {
                        if (get<2>(tile) == 0)
                        {
                                IMesh* earthMesh = smgr->getMesh("art/terrain/flat/flat.obj");
                                if (!earthMesh)
                                {
                                    Context.device->drop();
                                    return 1;
                                }
                                IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                                int x = get<0>(tile);
                                int y = get<1>(tile);
                                earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                                earth->setScale(vector3df(0.5, 0.5, 0.5));
                                earth->setMaterialTexture(0, driver->getTexture("art/terrain/flat/flat.png"));
                                earth->setMaterialFlag(video::EMF_LIGHTING, false);
                                core::stringw name = L"Grass (X:";
                                name += (get<0>(tile) * -1);
                                name += ") (Y:";
                                name += get<1>(tile);
                                name += ")";
                                earth->setName(name);
                                earth->setParent(map_node);
                                ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);
                                earth->setTriangleSelector(selector);
                                selector->drop();
                                //std::cout << dir << std::endl;
                        }
                        if (get<2>(tile) == 1)
                        {
                                IMesh* earthMesh = smgr->getMesh("art/terrain/flat/flat.obj");
                                if (!earthMesh)
                                {
                                    Context.device->drop();
                                    return 1;
                                }
                                IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                                earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                                earth->setScale(vector3df(0.5, 0.5, 0.5));
                                /*io::path path = "art/terrain/flat/dunes/dunes";
                                io::path num = dir;
                                path = path + num;
                                path = path + ".png";
                                path.c_str();
                                earth->setMaterialTexture(0, driver->getTexture(path));*/
                                int dir = tile_dir(game_map, tile);
                                core::stringw path = "art/terrain/flat/dunes/dunes";
                                path += dir;
                                path += ".png";
                                earth->setMaterialTexture(0, driver->getTexture(path));
                                earth->setMaterialFlag(video::EMF_LIGHTING, false);
                                core::stringw name = L"Dunes (X:";
                                name += (get<0>(tile) * -1);
                                name += ") (Y:";
                                name += get<1>(tile);
                                name += ")";
                                earth->setName(name);
                                earth->setParent(map_node);
                                ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                                earth->setTriangleSelector(selector);
                                selector->drop();
                                //std::cout << "placed tile type dunes" << std::endl;
                        }
                        if (get<2>(tile) == 2)
                        {
                                IMesh* earthMesh = smgr->getMesh("art/terrain/hill/hill.obj");
                                if (!earthMesh)
                                {
                                    Context.device->drop();
                                    return 1;
                                }
                                IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                                earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                                earth->setScale(vector3df(0.5, 0.5, 0.5));
                                earth->setMaterialTexture(0, driver->getTexture("art/terrain/hill/hill.png"));
                                earth->setMaterialFlag(video::EMF_LIGHTING, false);
                                core::stringw name = L"Hill (X:";
                                name += (get<0>(tile) * -1);
                                name += ") (Y:";
                                name += get<1>(tile);
                                name += ")";
                                earth->setName(name);
                                earth->setParent(map_node);
                                ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                                earth->setTriangleSelector(selector);
                                selector->drop();
                                //std::cout << "placed tile type hill" << std::endl;
                        }
                        if (get<2>(tile) == 3)
                        {
                                IMesh* earthMesh = smgr->getMesh("art/terrain/mountain/mountain.obj");
                                if (!earthMesh)
                                {
                                    Context.device->drop();
                                    return 1;
                                }
                                IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                                earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                                earth->setScale(vector3df(0.5, 0.5, 0.5));
                                earth->setMaterialTexture(0, driver->getTexture("art/terrain/mountain/mountain.png"));
                                earth->setMaterialFlag(video::EMF_LIGHTING, false);
                                core::stringw name = L"Mountain (X:";
                                name += (get<0>(tile) * -1);
                                name += ") (Y:";
                                name += get<1>(tile);
                                name += ")";
                                earth->setName(name);
                                earth->setParent(map_node);
                                ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                                earth->setTriangleSelector(selector);
                                selector->drop();
                                //std::cout << "placed tile type mountain" << std::endl;
                        }
                        if (get<2>(tile) == 4)
                        {
                                IMesh* earthMesh = smgr->getMesh("art/terrain/water/water.obj");
                                if (!earthMesh)
                                {
                                    Context.device->drop();
                                    return 1;
                                }
                                IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                                earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                                earth->setScale(vector3df(0.5, 0.5, 0.5));
                                int dir = coast_dir(game_map, tile);
                                core::stringw path = "art/terrain/water/water/coast";
                                path += dir;
                                path += ".png";
                                earth->setMaterialTexture(0, driver->getTexture(path));
                                earth->setMaterialFlag(video::EMF_LIGHTING, false);
                                core::stringw name = L"Lake (X:";
                                name += (get<0>(tile) * -1);
                                name += ") (Y:";
                                name += get<1>(tile);
                                name += ")";
                                earth->setName(name);
                                earth->setParent(map_node);
                                ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                                earth->setTriangleSelector(selector);
                                selector->drop();
                                //std::cout << "placed tile type water" << std::endl;
                        }
                        if (get<2>(tile) == 5)
                        {
                            IMesh* earthMesh = smgr->getMesh("art/terrain/water/water.obj");
                            if (!earthMesh)
                            {
                                Context.device->drop();
                                return 1;
                            }
                            IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                            earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                            earth->setScale(vector3df(0.5, 0.5, 0.5));
                            //earth->setMaterialTexture(1, driver->getTexture("art/terrain/water/ocean.png"));
                            int dir = coast_dir(game_map, tile);
                            core::stringw path = "art/terrain/water/coast/coast";
                            path += dir;
                            path += ".png";
                            earth->setMaterialTexture(0, driver->getTexture(path));
                            earth->setMaterialFlag(video::EMF_LIGHTING, false);
                            core::stringw name = L"Coast (X:";
                            name += (get<0>(tile) * -1);
                            name += ") (Y:";
                            name += get<1>(tile);
                            name += ")";
                            earth->setName(name);
                            earth->setParent(map_node);
                            ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                            earth->setTriangleSelector(selector);
                            selector->drop();
                            //std::cout << "placed tile type ocean" << std::endl;
                        }
                        if (get<2>(tile) == 6)
                        {
                            IMesh* earthMesh = smgr->getMesh("art/terrain/water/water.obj");
                            if (!earthMesh)
                            {
                                Context.device->drop();
                                return 1;
                            }
                            IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                            earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                            earth->setScale(vector3df(0.5, 0.5, 0.5));
                            earth->setMaterialTexture(0, driver->getTexture("art/terrain/water/deep_ocean.png"));
                            earth->setMaterialFlag(video::EMF_LIGHTING, false);
                            core::stringw name = L"Ocean (X:";
                            name += (get<0>(tile) * -1);
                            name += ") (Y:";
                            name += get<1>(tile);
                            name += ")";
                            earth->setName(name);
                            earth->setParent(map_node);
                            ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                            earth->setTriangleSelector(selector);
                            selector->drop();
                            //std::cout << "placed tile type deep ocean" << std::endl;
                        }
                        if (get<2>(tile) == 7)
                        {
                            IMesh* earthMesh = smgr->getMesh("art/terrain/flat/flat.obj");
                            if (!earthMesh)
                            {
                                Context.device->drop();
                                return 1;
                            }
                            IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                            earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                            earth->setScale(vector3df(0.5, 0.5, 0.5));
                            int dir = tile_dir(game_map, tile);
                            core::stringw path = "art/terrain/flat/plains/plains";
                            path += dir;
                            path += ".png";
                            earth->setMaterialTexture(0, driver->getTexture(path));
                            earth->setMaterialFlag(video::EMF_LIGHTING, false);
                            core::stringw name = L"Plains (X:";
                            name += (get<0>(tile) * -1);
                            name += ") (Y:";
                            name += get<1>(tile);
                            name += ")";
                            earth->setName(name);
                            earth->setParent(map_node);
                            ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                            earth->setTriangleSelector(selector);
                            selector->drop();
                            //std::cout << "placed tile type plains" << std::endl;
                        }
                        if (get<2>(tile) == 8)
                        {
                            IMesh* earthMesh = smgr->getMesh("art/terrain/flat/flat.obj");
                            if (!earthMesh)
                            {
                                Context.device->drop();
                                return 1;
                            }
                            IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                            earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                            earth->setScale(vector3df(0.5, 0.5, 0.5));
                            int dir = tile_dir(game_map, tile);
                            core::stringw path = "art/terrain/flat/tundra/tundra";
                            path += dir;
                            path += ".png";
                            earth->setMaterialTexture(0, driver->getTexture(path));
                            earth->setMaterialFlag(video::EMF_LIGHTING, false);
                            core::stringw name = L"Tundra (X:";
                            name += (get<0>(tile) * -1);
                            name += ") (Y:";
                            name += get<1>(tile);
                            name += ")";
                            earth->setName(name);
                            earth->setParent(map_node);
                            ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                            earth->setTriangleSelector(selector);
                            selector->drop();
                            //std::cout << "placed tile type tundra" << std::endl;
                        }
                        if (get<2>(tile) == 9)
                        {
                            IMesh* earthMesh = smgr->getMesh("art/terrain/flat/flat.obj");
                            if (!earthMesh)
                            {
                                Context.device->drop();
                                return 1;
                            }
                            IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                            earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                            earth->setScale(vector3df(0.5, 0.5, 0.5));
                            int dir = tile_dir(game_map, tile);
                            core::stringw path = "art/terrain/flat/ice/ice";
                            path += dir;
                            path += ".png";
                            earth->setMaterialTexture(0, driver->getTexture(path));
                            earth->setMaterialFlag(video::EMF_LIGHTING, false);
                            core::stringw name = L"Ice (X:";
                            name += (get<0>(tile) * -1);
                            name += ") (Y:";
                            name += get<1>(tile);
                            name += ")";
                            earth->setName(name);
                            earth->setParent(map_node);
                            ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                            earth->setTriangleSelector(selector);
                            selector->drop();
                            //std::cout << "placed tile type tundra" << std::endl;
                        }
                        if (get<2>(tile) == 10)
                        {
                            IMesh* earthMesh = smgr->getMesh("art/terrain/forest/forest.obj");
                            if (!earthMesh)
                            {
                                Context.device->drop();
                                return 1;
                            }
                            IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                            earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                            earth->setScale(vector3df(0.5, 0.5, 0.5));
                            earth->setMaterialTexture(0, driver->getTexture("art/terrain/forest/forest.png"));
                            earth->setMaterialFlag(video::EMF_LIGHTING, false);
                            core::stringw name = L"Forest (X:";
                            name += (get<0>(tile) * -1);
                            name += ") (Y:";
                            name += get<1>(tile);
                            name += ")";
                            earth->setName(name);
                            earth->setParent(map_node);
                            ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                            earth->setTriangleSelector(selector);
                            selector->drop();
                            //std::cout << "placed tile type tundra" << std::endl;
                        }
                        if (get<2>(tile) == 11)
                        {
                            IMesh* earthMesh = smgr->getMesh("art/terrain/jungle/jungle.obj");
                            if (!earthMesh)
                            {
                                Context.device->drop();
                                return 1;
                            }
                            IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                            earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                            earth->setScale(vector3df(0.5, 0.5, 0.5));
                            earth->setMaterialTexture(0, driver->getTexture("art/terrain/jungle/jungle.png"));
                            earth->setMaterialFlag(video::EMF_LIGHTING, false);
                            //set the name of the tile
                            core::stringw name = L"Jungle (X:";
                            name += (get<0>(tile) * -1);
                            name += ") (Y:";
                            name += get<1>(tile);
                            name += ")";
                            earth->setName(name);
                            earth->setParent(map_node);
                            ITriangleSelector* selector = smgr->createTriangleSelector(earthMesh, earth);;
                            earth->setTriangleSelector(selector);
                            selector->drop();
                            //std::cout << "placed tile type tundra" << std::endl;
                        }
                        if (get<2>(tile) == 12)
                        {
                            IMesh* earthMesh = smgr->getMesh("art/terrain/flat/flat.obj");
                            if (!earthMesh)
                            {
                                Context.device->drop();
                                return 1;
                            }
                            IMeshSceneNode* earth = smgr->addMeshSceneNode(earthMesh);
                            earth->setPosition(core::vector3df(get<0>(tile), 0, get<1>(tile)));
                            earth->setScale(vector3df(0.5, 0.5, 0.5));
                            int dir = tile_dir(game_map, tile);
                            core::stringw path = "art/terrain/flat/swamp/swamp";
                            path += dir;
                            path += ".png";
                            earth->setMaterialTexture(0, driver->getTexture(path));
                            earth->setMaterialFlag(video::EMF_LIGHTING, false);
                            core::stringw name = L"Swamp (X:";
                            name += (get<0>(tile) * -1);
                            name += ") (Y:";
                            name += get<1>(tile);
                            name += ")";
                            earth->setName(name);
                            earth->setParent(map_node);
                            ITriangleSelector* selector = smgr->createOctreeTriangleSelector(earthMesh, earth);;
                            earth->setTriangleSelector(selector);
                            selector->drop();
                            //std::cout << "placed tile type plains" << std::endl;
                        }
                    }
                    //Sleep(10000);
                    //IMesh* temp_mesh = mesh;
                    ISceneNode* map_nodeleft = map_node->clone();
                    //auto map_node_collision = map_node->getTransformedBoundingBox();
                    tuple <int, int, int> last_tile;
                    for (auto tile : game_map)
                    {
                        last_tile = tile;
                    }
                    map_nodeleft->setPosition(core::vector3df(get<0>(last_tile), 0, 0));
                    ISceneNode* map_noderight = map_node->clone();
                    map_noderight->setPosition(core::vector3df(-(get<0>(last_tile)), 0, 0));
                    return true;
                }


                default:
                    return false;
                
                }
                break;

            case EGET_FILE_SELECTED:
            {
                view_window = 1;
                env->clear();
                env->addButton(rect<s32>(550, 550, 750, 550 + 32), 0, GUI_ID_LAUNCH_BUTTON,
                    L"Launch!", L"Launch a new game");
                env->addButton(rect<s32>(300, 550, 500, 550 + 32), 0, GUI_ID_MENU_BUTTON,
                    L"Back", L"Back to main menu");
                // show the model filename, selected in the file dialog
                //IGUIFileOpenDialog* dialog = (IGUIFileOpenDialog*)event.GUIEvent.Caller;
            }
            break;

            default:
                break;
            }
        }
        // Remember whether each key is down or up
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

        return false;
    }

    // This is used to check whether a key is being held down
    virtual bool IsKeyDown(EKEY_CODE keyCode) const
    {
        return KeyIsDown[keyCode];
    }
    
private:
    SAppContext& Context;
    // We use this array to store the current state of each key
    bool KeyIsDown[KEY_KEY_CODES_COUNT];
};

int main()
{
    video::E_DRIVER_TYPE driverType = driverChoiceConsole();
    if (driverType == video::EDT_COUNT)
        return 1;

    // create device

    IrrlichtDevice* device = createDevice(driverType,
        core::dimension2d<u32>(800, 600), 64, false, false, true);
    device->setResizable(true);

    if (device == 0)
        return 1; // could not create selected driver.

    device->setWindowCaption(L"Irrlicht Engine - Freeciv Client Test");
    //set up enviroment
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    IGUIEnvironment* env = device->getGUIEnvironment();
    //load images
    ITexture* bg_img = driver->getTexture("client/bg.png");
    ITexture* intro = 0;//driver->getTexture("client/intro.png");
    intro = driver->addRenderTargetTexture(core::dimension2d<u32>(510, 300), "IntroRT");
    //set up map rendering
    ITexture* map = 0;
    ICameraSceneNode* camera = 0;
    map = driver->addRenderTargetTexture(driver->getScreenSize(), "RTT1");
    camera = smgr->addCameraSceneNode(0, core::vector3df(0, 0, 50),
        core::vector3df(0, 0, 0));
    //set up fonts
    gui::IGUIFont* font = device->getGUIEnvironment()->getBuiltInFont();
    gui::IGUIFont* font2 =
        device->getGUIEnvironment()->getFont("client/fonthaettenschweiler.bmp");
    IGUISkin* skin = env->getSkin();
    if (font2)
        skin->setFont(font);
    //set up the main menu buttons
    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) - 250, 400, (driver->getViewPort().getWidth() / 2) - 50, 400 + 32), 0, GUI_ID_NEW_GAME_BUTTON,
        L"New Game", L"Launch a new game");
    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) - 250, 440, (driver->getViewPort().getWidth() / 2) - 50, 440 + 32), 0, GUI_ID_LOAD_GAME_BUTTON,
        L"Load Game", L"Load a saved game");
    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) - 250, 480, (driver->getViewPort().getWidth() / 2) - 50, 480 + 32), 0, GUI_ID_LOAD_SCENARIO_BUTTON,
        L"Load Scenario", L"Loads a prebuilt map");
    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) + 50, 400, (driver->getViewPort().getWidth() / 2) + 250, 400 + 32), 0, GUI_ID_NET_GAME_BUTTON,
        L"Network Game", L"Join a network game");
    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) + 50, 440, (driver->getViewPort().getWidth() / 2) + 250, 440 + 32), 0, GUI_ID_SETTINGS_BUTTON,
        L"Settings", L"Client Settings");
    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) + 50, 480, (driver->getViewPort().getWidth() / 2) + 250, 480 + 32), 0, GUI_ID_QUIT_BUTTON,
        L"Exit", L"Quit");

    // Store the appropriate data in a context structure.
    SAppContext context;
    context.device = device;
    context.counter = 0;

    int lastFPS = 0;

    // Then create the event receiver, giving it that context structure.
    MyEventReceiver receiver(context);

    // And tell the device to use our custom event receiver.
    device->setEventReceiver(&receiver);

    // create test earth
    ISceneNode* earth = 0;
    IAnimatedMesh* earthMesh = smgr->getMesh("client/earth.x");
    if (earthMesh)
    {
        //perform various task with the mesh manipulator
        scene::IMeshManipulator* manipulator = smgr->getMeshManipulator();

        // create mesh copy with tangent informations from original earth.x mesh
        scene::IMesh* tangentSphereMesh =
            manipulator->createMeshWithTangents(earthMesh->getMesh(0));

        // scale the mesh by factor 10
        core::matrix4 m;
        m.setScale(core::vector3df(20, 20, 20));
        manipulator->transform(tangentSphereMesh, m);

        earth = smgr->addMeshSceneNode(tangentSphereMesh);
        earth->setPosition(core::vector3df(0, 0, 0));

        earth->setMaterialTexture(0,
            driver->getTexture("client/earth_intro.jpg"));

        // load heightmap, create normal map from it and set it
        /*video::ITexture* earthNormalMap = driver->getTexture("client/earthbump.jpg");
        if (earthNormalMap)
        {
            driver->makeNormalMapTexture(earthNormalMap, 20.0f);
            earth->setMaterialTexture(1, earthNormalMap);
            earth->setMaterialType(video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA);
        }
        if you want a texured earth, uncomment this code.
        */

        // adjust material settings
        earth->setMaterialFlag(video::EMF_FOG_ENABLE, false);

        // add rotation animator
        scene::ISceneNodeAnimator* anim =
            smgr->createRotationAnimator(core::vector3df(0, 0.1f, 0));
        earth->addAnimator(anim);
        anim->drop();

        // drop mesh because we created it with a create.. call.
        tangentSphereMesh->drop();
    }

    // add white light
    smgr->addLightSceneNode(0, core::vector3df(-50, 0, 25),
        video::SColorf(1.0f, 1.0f, 1.0f));

    // set ambient light
    smgr->setAmbientLight(video::SColor(0, 100, 100, 100));

    //set up video quality settings
    //driver->getMaterial2D().TextureLayer[0].BilinearFilter = true;
    //driver->getMaterial2D().AntiAliasing = video::EAAM_FULL_BASIC;
    //set up collision
    scene::ISceneNode* highlightedSceneNode = 0;
    scene::ISceneCollisionManager* collMan = smgr->getSceneCollisionManager();
    /*scene::IBillboardSceneNode* bill = smgr->addBillboardSceneNode();
    bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
    bill->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
    bill->setMaterialFlag(video::EMF_LIGHTING, false);
    bill->setMaterialFlag(video::EMF_ZBUFFER, false);
    bill->setSize(core::dimension2d<f32>(20.0f, 20.0f));*/
    //set up control settings
    f32 MOVEMENT_SPEED = 5.f;
    u32 then = device->getTimer()->getTime();
    int last_width = driver->getViewPort().getWidth();
    int width = driver->getViewPort().getWidth();
    while (device->run() && driver)
    {
        if (device->isWindowActive())
        {
            u32 time = device->getTimer()->getTime();
            driver->beginScene(true, true, video::SColor(255, 0, 0, 0));
            // draw background
            if (view_window == 0)
            {
                if (driver->getViewPort().getWidth() != last_width)
                {
                    env->clear();
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) - 250, 400, (driver->getViewPort().getWidth() / 2) - 50, 400 + 32), 0, GUI_ID_NEW_GAME_BUTTON,
                        L"New Game", L"Launch a new game");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) - 250, 440, (driver->getViewPort().getWidth() / 2) - 50, 440 + 32), 0, GUI_ID_LOAD_GAME_BUTTON,
                        L"Load Game", L"Load a saved game");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) - 250, 480, (driver->getViewPort().getWidth() / 2) - 50, 480 + 32), 0, GUI_ID_LOAD_SCENARIO_BUTTON,
                        L"Load Scenario", L"Loads a prebuilt map");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) + 50, 400, (driver->getViewPort().getWidth() / 2) + 250, 400 + 32), 0, GUI_ID_NET_GAME_BUTTON,
                        L"Network Game", L"Join a network game");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) + 50, 440, (driver->getViewPort().getWidth() / 2) + 250, 440 + 32), 0, GUI_ID_SETTINGS_BUTTON,
                        L"Settings", L"Client Settings");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() / 2) + 50, 480, (driver->getViewPort().getWidth() / 2) + 250, 480 + 32), 0, GUI_ID_QUIT_BUTTON,
                        L"Exit", L"Quit");
                    last_width = driver->getViewPort().getWidth();
                }
                
                // draw intro image
                //driver->draw2DImage(intro, core::position2d<s32>(145, 50));
                //overwrite intro image with rendered earth
                //set up camera
                driver->setRenderTarget(intro, true, true, SColor(0, 0, 0, 0));
                smgr->setActiveCamera(camera);
                smgr->drawAll();
                driver->setRenderTarget(0, true, true, 0);
                // draw background and map
                driver->draw2DImage(bg_img, core::position2d<s32>(0, 0));
                driver->draw2DImage(bg_img, core::position2d<s32>(800, 0));
                driver->draw2DImage(bg_img, core::position2d<s32>(0, 600));
                driver->draw2DImage(bg_img, core::position2d<s32>(800, 600));
                driver->draw2DImage(intro, core::position2d<s32>((driver->getViewPort().getWidth() / 2) - 255, 50));
                //draw buttons
                env->drawAll();

                // draw some other text
                if (font2)
                    font2->draw(L"Welcome to the experimental Irrlicht engine 3D client for freeciv.",
                        core::rect<s32>((driver->getViewPort().getWidth()/2)-150, 20, 2000, 80),
                        video::SColor(255, 0, 0, 0));


            }
            if (view_window == 1)
            {
                if (driver->getViewPort().getWidth() != last_width)
                {
                    env->clear();
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() -250), (driver->getViewPort().getHeight() - 50), (driver->getViewPort().getWidth() - 50), (driver->getViewPort().getHeight() - 50) + 32), 0, GUI_ID_LAUNCH_BUTTON,
                        L"Launch!", L"Launch a new game");
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() - 500), (driver->getViewPort().getHeight() - 50), (driver->getViewPort().getWidth() - 300), (driver->getViewPort().getHeight() - 50) + 32), 0, GUI_ID_MENU_BUTTON,
                        L"Back", L"Back to main menu");
                    last_width = driver->getViewPort().getWidth();
                }
                // draw background and splash
                driver->draw2DImage(bg_img, core::position2d<s32>(0, 0));
                driver->draw2DImage(bg_img, core::position2d<s32>(800, 0));
                driver->draw2DImage(bg_img, core::position2d<s32>(0, 600));
                driver->draw2DImage(bg_img, core::position2d<s32>(800, 600));
                //draw buttons
                env->drawAll();

                // draw some other text
                if (font2)
                    font2->draw(L"This is the new game page.",
                        core::rect<s32>(20, 20, 650, 80),
                        video::SColor(255, 0, 0, 0));

            }
            if (view_window == 2)
            {
                if (driver->getViewPort().getWidth() != last_width)
                {
                    env->clear();
                    env->addButton(rect<s32>((driver->getViewPort().getWidth() - 250), (driver->getViewPort().getHeight() - 50), (driver->getViewPort().getWidth() - 50), (driver->getViewPort().getHeight() - 50) + 32), 0, GUI_ID_MENU_BUTTON,
                        L"Back", L"Back to main menu");
                    last_width = driver->getViewPort().getWidth();
                }
                // Work out a frame delta time.
                const u32 now = device->getTimer()->getTime();
                const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
                then = now;
                core::vector3df nodePosition = camera->getPosition();
                core::vector3df nodeLookat = camera->getTarget();

                if (receiver.IsKeyDown(irr::KEY_SUBTRACT))
                    nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
                else if (receiver.IsKeyDown(irr::KEY_ADD))
                    nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;

                if (receiver.IsKeyDown(irr::KEY_LEFT))
                {
                    nodePosition.X += MOVEMENT_SPEED * frameDeltaTime;
                    nodeLookat.X += MOVEMENT_SPEED * frameDeltaTime;
                }
                else if (receiver.IsKeyDown(irr::KEY_RIGHT))
                {
                    nodePosition.X -= MOVEMENT_SPEED * frameDeltaTime;
                    nodeLookat.X -= MOVEMENT_SPEED * frameDeltaTime;
                }


                if (receiver.IsKeyDown(irr::KEY_UP))
                {
                    nodePosition.Z -= MOVEMENT_SPEED * frameDeltaTime;
                    nodeLookat.Z -= MOVEMENT_SPEED * frameDeltaTime;
                }
                else if (receiver.IsKeyDown(irr::KEY_DOWN))
                {
                    nodePosition.Z += MOVEMENT_SPEED * frameDeltaTime;
                    nodeLookat.Z += MOVEMENT_SPEED * frameDeltaTime;
                }
                camera->setPosition(nodePosition);
                camera->setTarget(nodeLookat);
                //render map
                driver->removeTexture(map);
                map = driver->addRenderTargetTexture(driver->getScreenSize(), "RTT1");
                //set up camera
                driver->setRenderTarget(map, true, true, SColor(0, 0, 0, 0));
                smgr->setActiveCamera(camera);
                smgr->drawAll();

                /*if (highlightedSceneNode)
                {
                    highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTING, true);
                    highlightedSceneNode = 0;
                }*/
                line3d<f32> ray;
                position2d<s32> mouse = device->getCursorControl()->getPosition();
                //mouse.X -= 50;
                //mouse.Y -= 50;
                /*cout << mouse.X;
                cout << " ";
                cout << mouse.Y << endl;
                cout << camera->getPosition().X;
                cout << " ";
                cout << camera->getPosition().Z << endl;
                cout << camera->getPosition().X + mouse.X - 400;
                cout << " ";
                cout << camera->getPosition().Z + mouse.Y - 250 << endl;*/
                ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(
                    mouse, camera);
                //ray.start = vector3df(camera->getPosition().X + mouse.X - 400, camera->getPosition().Y, camera->getPosition().Z + mouse.Y - 250);
                //ray.end = vector3df(camera->getTarget().X + mouse.X - 400, camera->getTarget().Y, camera->getTarget().Z + mouse.Y - 250) * 2;//ray.start + (camera->getTarget() - ray.start).normalize() * 1000.0f;
                // Tracks the current intersection point with the level or a mesh
                core::vector3df intersection;
                // Used to show with triangle has been hit
                core::triangle3df hitTriangle;

                // This call is all you need to perform ray/triangle collision on every scene node
                // that has a triangle selector, including the Quake level mesh.  It finds the nearest
                // collision point/triangle, and returns the scene node containing that point.
                // Irrlicht provides other types of selection, including ray/triangle selector,
                // ray/box and ellipse/triangle selector, plus associated helpers.
                // See the methods of ISceneCollisionManager
                scene::ISceneNode* selectedSceneNode =
                    collMan->getSceneNodeAndCollisionPointFromRay(
                        ray,
                        intersection, // This will be the position of the collision
                        hitTriangle, // This will be the triangle hit in the collision
                        0); // Check the entire scene (this is actually the implicit default)

            // If the ray hit anything, move the billboard to the collision position
            // and draw the triangle that was hit.
                if (selectedSceneNode)
                {
                    //cout << selectedSceneNode->getName() << endl;
                    //driver->draw3DTriangle(hitTriangle, video::SColor(0, 255, 0, 0));
                }

                driver->setRenderTarget(0, true, true, 0);
                // draw background and map
                //driver->draw2DImage(bg, core::position2d<s32>(0, 0));
                driver->draw2DImage(map, core::position2d<s32>(0, 0));
                //draw buttons
                env->drawAll();

                // draw some other text
                if (font2)
                    font2->draw(L"This is the game page.",
                        core::rect<s32>(20, 20, 650, 80),
                        video::SColor(255, 255, 0, 0));
                if (font2 and selectedSceneNode)
                    font2->draw(selectedSceneNode->getName(),
                        core::rect<s32>(20, driver->getViewPort().getHeight()-80, 1000, driver->getViewPort().getHeight()-20),
                        video::SColor(255, 255, 0, 0));
                
                int fps = driver->getFPS();

                if (lastFPS != fps)
                {
                    core::stringw str = L"Irrlicht Engine - Freeciv Client Test [";
                    str += driver->getName();
                    str += "] FPS:";
                    str += fps;

                    device->setWindowCaption(str.c_str());
                    lastFPS = fps;
                }
            }
            driver->endScene();
        }
    }

    device->drop();

    return 0;
}