#pragma once
#ifdef __linux__
#else
#include <windows.h>
#endif
#include <array>
#include <sstream>
#include <iostream>
#include <unordered_map>
#ifdef WIN32
// this works
//#include <concurrent_unordered_map.h>
//using namespace Concurrency;
// this might work?
//#include "show/glew.h" // LAND
#else
// this does not work
//#include "tbb/concurrent_unordered_map.h"
//using namespace tbb;
// this might work but probably not?
//#include <GLES3/gl3.h> // LAND
#endif

// reboot park.exe if you change these
#define RATE 30
#define SIZE 2000
#define YARN 2
#define CUBE 15
#define LAND 5

using namespace std;

struct Vec3 { float x = 0, y = 0, z = 0; };
struct Quat { float x = 0, y = 0, z = 0, w = 0; };

const int UNIT = 1; // character mesh VAO from .dae
const int PART = 2; // static mesh VAO from .obj
const int SKIN = 3; // texture from .tga
const int MOVE = 4; // animation from .dae
const int TONE = 5; // sound from .wav
const int BONE = 6; // bone attachement point
const int SALT = 7; // id/salt with find/user (salt is both sign hash salt and session key)
const int NAME = 8; // id/name with find/user

/* game -> engine, these are found on the Game struct */
typedef void (*SEND)(const char *message); // using
typedef void (*PLAY)(int tone, Vec3 spot, Vec3 pace);
typedef int (*FIND)(int type, const char *name);
typedef const char * (*USER)(int type, int id);
typedef Quat (*LOOK)();

/* data structures concurrently changed by all threads */

struct Node { // Array of 64 byte structure
    short mesh, skin; // model, texture
    Vec3 spot, pace; // position, velocity
    Quat look, spin; // look direction, rotation speed (to be used later in deterministic physics)
    float zoom; // scale
};

struct Link {
    char **json; // properties
    int *unit; // for each item id you link the unit id that holds that item:
    int *bone; // game->gear.unit[4] = 2;
               // game->gear.bone[4] = game->find(BONE, "left_hand");
               // means item 4 is held by unit 2 in left hand.
};

struct Cube {
    //const int BITS = 5;
    //const int SIZE = 1 << BITS;
    char*** type; // blocks [15][15][15]
    int* mesh; // packed model x, y, z (16); type (32); side(8); glow and skew (64)
    int* list; // index
};

struct Shot { // projectiles
    int type;
    Vec3 spot, pace; // position, velocity
};

struct Land { Cube*** cube; }; // chunks [5][5][5]

struct Game {
    const int size = SIZE;
    const int yarn = YARN;
    const int cube = CUBE;

    char **name, **chat;
    Node *unit, *item, *part; // character, sword or gun, house or tree
    int *move, *lead; // character animation frames and offset
    Shot *shot;

    Link gear; // link item to character bone
    Land land; // the voxel world

    SEND send; PLAY play; FIND find;
    USER user; LOOK look;
};

/* do not edit above */

// GAME CODE
// |
// v

//concurrent_
unordered_map<string, int> salt_pull;
//concurrent_
unordered_map<int, string> salt_push;

string move(Game *game, int id, const char *move, float pace) {
    game->move[id] = game->find(MOVE, move);
    game->unit[id].pace.x = pace;
    stringstream ss;
    ss << "move|";
    ss << game->unit[id].spot.x << "," << game->unit[id].spot.y << "," << game->unit[id].spot.z << ";";
    ss << game->unit[id].pace.x << "," << game->unit[id].pace.y << "," << game->unit[id].pace.z << ";";
    ss << game->unit[id].look.x << "," << game->unit[id].look.y << "," << game->unit[id].look.z << "," << game->unit[id].look.w;
    ss << ";" << move;
    return ss.str();
}

void salt(int id, string key) {
    salt_push[id] = move(key); // move(key)
    salt_pull[key] = id;
}

bool once = true;
bool slow = false;
bool fast = false;
bool walk = false;

/* once per hot-deploy of the .dll/.so
 */
void _init(Game *game) {
    cout << ".dll/.so hot-deployed!? win 10 sucks... yes/no/huh" << endl;
    game->unit[0].mesh = game->find(UNIT, "pirate");
    game->unit[0].skin = game->find(SKIN, "texture_1");
    salt(0, "fd43");
    salt(1, "y2dt");
    salt(2, "u248");
}

/* once per frame
 * this is where you calculate movement
 */
void _tick(Game *game, int yarn) {
    //game->push("yo");
    //Quat view = game->look();
    if(once) {
     cout << "123-yo" << endl;
     once = false;
    }
    //cout << "!" << flush;
}

#ifdef WIN32
#define NOINLINE __declspec (noinline)
#else
#define NOINLINE __attribute__ ((noinline))
#endif

NOINLINE void make_crash_yo() {
    int *foo = (int*)-1; // make a bad pointer
    printf("%d\n", *foo); // causes segfault
}

NOINLINE void add_some_depth() {
    int a = 0;
    make_crash_yo();
}

/* once per binary move action press keyboard/mouse,
 * thumb stick gets abstracted down to WASD with ctrl
 * /shift for fast/slow.
 */
void _push(Game *game, int code, bool down) {
    //cout << code << endl;
    if(code == 16) fast = down; // shift
    if(code == 17) slow = down; // control
    if(code == 'W') walk = down;
    if(walk && (code == 16 || code == 17 || code == 'W') || code == 'W') {
        const char *pace = fast ? "fast" : slow ? "slow" : "walk";
        float speed = fast ? 4.0f : slow ? 1.0f : 2.0f;
        string back = move(game, 0, walk ? pace : "idle", walk ? speed : 0.0f);
        game->send(back.c_str());
    }
    //cout << code << endl;
    if(code == 52) { add_some_depth(); }
}

/* once per network message input
 */
void _pull(Game *game, const char *message) {
    // TODO
}

// ^
// |
// GAME CODE

/* engine -> game
 * do not edit below.
 * leave this at bottom.
 */
extern "C" {
    void init(Game *game) { _init(game); }
    void tick(Game *game, int yarn) { _tick(game, yarn); }
    void push(Game *game, int code, bool down) { _push(game, code, down); }
    void pull(Game *game, const char *message) { _pull(game, message); }
}