#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#define CMD_LEN        10
#define NAME_LEN       10
#define POV_3D_SIZE    10
#define POV_SIZE       3

#define CMD_NULL       "-"
#define CMD_FWD        's'
#define CMD_ATTACK     'a'
#define CMD_FLEE       'f'

#define SC_TITLE    0
#define SC_SETNAME  1
#define SC_STATION  2
#define SC_MAP      3
#define SC_MONSTER  4

#define NORTH 0
#define EAST  1
#define WEST  2
#define SOUTH 3

#define TILE_WALL  'w'
#define TILE_FREE  ' '
#define TILE_DOOR  'd'
#define TILE_START 's'

typedef struct {
	int x;
	int y;
	char orientation;
} Position;

typedef struct {
	char name[NAME_LEN];
	int hp;
	int gold;
	int atk;
	int def;
} Character;

typedef struct {
	Character hero;
	Character monster;
	Position position;
	char screen;
} Gamestate;

const char* statepath = "w.state";
char pov[POV_SIZE][POV_SIZE];
unsigned char pov3d[POV_3D_SIZE][POV_3D_SIZE];
Gamestate state;

int mapsize = 0;
char* map = NULL;

Character monsters[1] = {
	{ "Alien", 1, 2, 1, 1 }
};

int dice()
{
	return 1 + (rand() % 6);
}

char charatpos(int x, int y)
{
	if (x < 0 || y < 0 || x >= mapsize || y >= mapsize)
	{
		return ' ';
	}
	return map[ (mapsize - y - 1) * mapsize + x];
}

void loadmap()
{
	FILE* file = fopen("w.map", "r");
	while (fgetc(file) != '\n')
	{
		mapsize++;
	}
	rewind(file);

	map = (char*)malloc(mapsize * mapsize * sizeof(char));

	char c = 0;
	int i = 0;
	while (c != EOF)
	{
		c = fgetc(file);
		if (c != '\n')
		{
			map[i++] = c;
		}
	}
	fclose(file);
}

void drawmap()
{
	for (int i = 0; i < mapsize; i++) {
		int y = mapsize - i - 1;
		for (int j = 0; j < mapsize; j++) {
			char toprint = map[i * mapsize + j];
			if (y == state.position.y && j == state.position.x)
			{
				if (state.position.orientation == NORTH) toprint = '^';
				if (state.position.orientation == SOUTH) toprint = 'v';
				if (state.position.orientation == EAST) toprint = '>';
				if (state.position.orientation == WEST) toprint = '<';
			}
			printf("%c", toprint);
		}
		printf("\n");
	}
}

void savestate()
{
	FILE* file = fopen(statepath, "w");
	fwrite(&state, sizeof(Gamestate), 1, file);
	fclose(file);
}

char currentchar()
{
	return charatpos(state.position.x, state.position.y);
}

void update3dpov()
{
	for (int i = 0; i < POV_3D_SIZE; i++)
	{
		memset(pov3d[i], ' ', POV_3D_SIZE);
	}

	// front constants
	for (int i = 2; i < 8; i++)
	{
		pov3d[1][i] = '_';
		pov3d[7][i] = '_';
	}

	if (pov[2][0] == TILE_FREE)
	{
		// front left free
		pov3d[1][0] = '_';
		pov3d[1][1] = '_';
		pov3d[7][0] = '_';
		pov3d[7][1] = '_';
	}
	else
	{
		// front left wall
		pov3d[0][0] = '\\';
		pov3d[1][1] = '\\';
		pov3d[8][1] = '/';
		pov3d[9][0] = '/';
		for (int i = 2; i < 8; i++)	pov3d[i][1] = '|';
	}

	if (pov[2][2] == TILE_FREE)
	{
		// front right free
		pov3d[1][8] = '_';
		pov3d[1][9] = '_';
		pov3d[7][8] = '_';
		pov3d[7][9] = '_';
	}
	else
	{
		// front right wall
		pov3d[8][8] = '\\';
		pov3d[9][9] = '\\';
		pov3d[0][9] = '/';
		pov3d[1][8] = '/';
		for (int i = 2; i < 8; i++)	pov3d[i][7+1] = '|';
	}

	if (pov[1][1] == TILE_FREE)
	{
		// far constants
		pov3d[3][4] = '_';
		pov3d[3][5] = '_';
		pov3d[5][4] = '_';
		pov3d[5][5] = '_';

		if (pov[0][0] == TILE_FREE)
		{
			// far left free
			pov3d[3][2] = '_';
			pov3d[3][3] = '_';
			pov3d[5][2] = '_';
			pov3d[5][3] = '_';

			// if front is free as well
			if (pov[2][0] == TILE_FREE)
			{
				pov3d[3][0] = '_';
				pov3d[3][1] = '_';
				pov3d[5][0] = '_';
				pov3d[5][1] = '_';
			}
		}
		else
		{
			// far left wall
			pov3d[2][2] = '\\';
			pov3d[3][3] = '\\';
			pov3d[7][2] = '/';
			pov3d[6][3] = '/';
			pov3d[4][3] = '|';
			pov3d[5][3] = '|';

			// ensure front view is complete
			for (int i = 2; i < 8; i++)	pov3d[i][1] = '|';
		}

		if (pov[0][2] == TILE_FREE)
		{
			// far right free
			pov3d[3][6] = '_';
			pov3d[3][7] = '_';
			pov3d[5][6] = '_';
			pov3d[5][7] = '_';

			// if front is free as well
			if (pov[2][2] == TILE_FREE)
			{
				pov3d[3][8] = '_';
				pov3d[3][9] = '_';
				pov3d[5][8] = '_';
				pov3d[5][9] = '_';
			}
		}
		else
		{
			// far right wall
			pov3d[6][6] = '\\';
			pov3d[7][7] = '\\';
			pov3d[2][7] = '/';
			pov3d[3][6] = '/';
			pov3d[4][5+1] = '|';
			pov3d[5][5+1] = '|';

			// ensure front view is complete
			for (int i = 2; i < 8; i++)	pov3d[i][7+1] = '|';
		}
	}

	const char doorchar = '#';
	if (pov[1][1] == TILE_DOOR)
	{
		// front door
		for (int i = 4; i <= 7; i++)
		{
			for (int j = 3; j < 7; j++)
				pov3d[i][j] = doorchar;
		}
	}
	if (pov[2][0] == TILE_DOOR)
	{
		for (int i = 5; i < 9; i++) pov3d[i][0] = doorchar;
	}
	if (pov[2][2] == TILE_DOOR)
	{
		for (int i = 5; i < 9; i++) pov3d[i][9] = doorchar;
	}

	if (currentchar() == TILE_START)
	{
		pov3d[0][4] = '[';
		pov3d[0][5] = ']';
	}
}

void updatepov()
{
	int x = state.position.x;
	int y = state.position.y;
	char orientation = state.position.orientation;

	for (int frontoffset = 2; frontoffset >= 0; frontoffset--)
	{
		for (int rtoloffset = -1; rtoloffset < 2; rtoloffset++)
		{
			int newX = x, newY = y;
			switch (orientation)
			{
			case NORTH:
				newX += rtoloffset;
				newY += frontoffset;
				break;
			case SOUTH:
				newX -= rtoloffset;
				newY -= frontoffset;
				break;
			case EAST:
				newX += frontoffset;
				newY -= rtoloffset;
				break;
			case WEST:
				newX -= frontoffset;
				newY += rtoloffset;
				break;
			}
			pov[2 - frontoffset][1 + rtoloffset] = charatpos(newX, newY);
		}
	}
}

void forward()
{
	if (pov[1][1] != TILE_WALL)
	{
		switch (state.position.orientation)
		{
		case NORTH:
			state.position.y += 2;
			break;
		case SOUTH:
			state.position.y -= 2;
			break;
		case EAST:
			state.position.x += 2;
			break;
		case WEST:
			state.position.x -= 2;
			break;
		}
	}
}

void right()
{
	switch (state.position.orientation)
	{
	case NORTH:
		state.position.orientation = EAST;
			break;
	case SOUTH:
		state.position.orientation = WEST;
			break;
	case EAST:
		state.position.orientation = SOUTH;
			break;
	case WEST:
		state.position.orientation = NORTH;
			break;
	}
}

void left()
{
	switch (state.position.orientation)
	{
	case NORTH:
		state.position.orientation = WEST;
			break;
	case SOUTH:
		state.position.orientation = EAST;
			break;
	case EAST:
		state.position.orientation = NORTH;
			break;
	case WEST:
		state.position.orientation = SOUTH;
			break;
	}
}

void dumpstate()
{
	printf("Name:%s\nx:%d\ny:%d\norientation:%c\n",
		state.hero.name,
		state.position.x,
		state.position.y,
		state.position.orientation);
}

void clearscreen()
{
#ifdef _WIN32
	system("cls");
#else
	system("clear");
#endif
}

void init()
{
	srand(time(NULL));
	FILE* file = fopen(statepath, "r");
	if (file)
	{
		fread(&state, sizeof(Gamestate), 1, file);
		fclose(file);
	}
	else
	{
		state.screen = SC_TITLE;
	}
	loadmap();
}

void update(char command[CMD_LEN])
{
	char c = command[0];
	if (state.screen == SC_MAP)
	{
		switch (c)
		{
		case CMD_FWD:
		case '\0':
			forward();
			if (dice() == 1)
			{
				memcpy(&state.monster, &(monsters[0]), sizeof(Character));
				state.screen = SC_MONSTER;
			}
			break;
		case 'x':
			right();
			break;
		case 'w':
			left();
			break;
		case 'r':
			if (currentchar() == TILE_START)
			{
				state.screen = SC_STATION;
			}
			break;
		default:
			break;
		}
		updatepov();
	}
	else if (state.screen == SC_TITLE)
	{
		if (strcmp(command, CMD_NULL) != 0)
		{
			state.screen = SC_SETNAME;
		}
	}
	else if (state.screen == SC_SETNAME)
	{
		if (strlen(command) > 0)
		{
			strcpy(state.hero.name, command);
			state.hero.hp = 10;
			state.hero.gold = 0;
			state.hero.atk = 1;
			state.hero.def = 1;
			state.screen = SC_STATION;
		}
	}
	else if (state.screen == SC_STATION)
	{
		if (c == 'n')
		{
			state.screen = SC_MAP;
			state.position.x = 1;
			state.position.y = 5;
			state.position.orientation = NORTH;
			updatepov();
		}
	}
	else if (state.screen == SC_MONSTER)
	{
		if (c == CMD_FLEE)
		{
			state.screen = SC_MAP;
		}
		else if (c == CMD_ATTACK)
		{
			// hero then monster turn.
			// possible outcomes: hero dead, monster dead, or still fight

			// hero attacks
			state.monster.hp--;

			// monster attacks
			state.hero.hp--;

			if (state.monster.hp <= 0)
			{
				state.hero.gold += state.monster.gold;
				state.screen = SC_MAP;
			}
			
		}
	}
}

void debugoutput()
{

}

void draw()
{
	clearscreen();

	int screenwidth = 30;
	for (int i = 0; i < screenwidth; i++) printf("-");
	printf("\n");

	if (state.screen == SC_TITLE)
	{
		printf("        _        _\n");
		printf("       / /      / /\n");
		printf("      / /      / /\n");
		printf("     / /__a   / /__égende\n");
		printf("    |____/   |____/\n");
		printf("\n");
		printf("        du  ___\n");
		printf("           / _ \\ \n");
		printf("          / / | |\n");
		printf("         / /_/ /ongeon\n");
		printf("        /____ /\n");
		printf("\n");
		printf("          _____\n");
		printf("   de l' / ___/\n");
		printf("        / /_\n");
		printf("       / /___space\n");
		printf("      /_____/\n");
	}
	else if (state.screen == SC_SETNAME)
	{
		printf("Bonjour.\n\n");
		printf("Bienvenue à la station\n");
		printf("spaciale intergalactique.\n\n");
		printf("Veuillez saisir votre nom.\n");
	}
	else if (state.screen == SC_MAP)
	{
		update3dpov();
		int shift = 9;
		for (int i = 0; i < shift; i++) printf(" ");
		printf("o----------o\n");

		for (int i = 0; i < POV_3D_SIZE; i++)
		{
			for (int s = 0; s < shift; s++) printf(" ");
			printf("|");
			for (int j = 0; j < POV_3D_SIZE; j++)
			{
				printf("%c", pov3d[i][j]);
			}
			printf("|\n");
		}
		for (int i = 0; i < shift; i++) printf(" ");
		printf("o----------o");

		if (charatpos(state.position.x, state.position.y) == 's')
		{
			printf("\n\nr: Retour à la station\n");
		}
		else
		{
			printf("\n               ^\n");
			printf("            <w s x>\n");
		}
		//drawmap();
	}
	else if (state.screen == SC_STATION)
	{
		printf("\n");
		printf("      STATION  SPACIALE\n");
		printf("       INTERGALACTIQUE\n");
		printf("\n");

		printf("n: Prendre la navette\n");
	}
	else if (state.screen == SC_MONSTER)
	{
		printf("Rencontre avec un %s\n", state.monster.name);
		printf("f: Tenter de fuir\n");
		printf("a: Attaquer\n");
	}

	if (state.screen == SC_MONSTER 
		|| state.screen == SC_STATION 
		|| state.screen == SC_MAP)
	{
		printf("\n");
		for (int i = 0; i < screenwidth; i++) printf("-");
		printf("\n");
		printf("%s | P.V. : %d | C.G. : %d", state.hero.name, state.hero.hp, state.hero.gold);

	}

	debugoutput();

	printf("\n");
	for (int i = 0; i < screenwidth; i++) printf("-");
	printf("\n> ");

}

void drawtopdownpov()
{
	for (int y = 0; y < POV_SIZE; y++)
	{
		for (int x = 0; x < POV_SIZE; x++)
		{
			if (y == 0 && pov[1][1] != ' ')
			{
				printf(" ");
			}
			else if (y == 2 && x == 1)
			{
				printf("^");
			}
			else
			{
				printf("%c", pov[y][x]);
			}
		}
		printf("\n");
	}
}

void getcommand(char* command)
{
	fgets(command, sizeof(char) * CMD_LEN, stdin);
	command[strcspn(command, "\n")] = 0;
}

int main()
{
	init();

	char command[CMD_LEN] = CMD_NULL;

	while (command[0] != 'q')
	{
		update(command);
		draw();
		getcommand(command);
	}
	savestate();
	return 0;
}