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

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

#define PLACE_TITLE		0
#define PLACE_STATION	1
#define PLACE_MAP		2

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

typedef struct {
	char name[NAME_LEN];
	Location location;
	char place;
} 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;

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.location.y && j == state.location.x)
			{
				if (state.location.orientation == 'n') toprint = '^';
				if (state.location.orientation == 's') toprint = 'v';
				if (state.location.orientation == 'e') toprint = '>';
				if (state.location.orientation == 'w') toprint = '<';
			}
			printf("%c", toprint);
		}
		printf("\n");
	}
}

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

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] == ' ')
	{
		// 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] == ' ')
	{
		// 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] == ' ')
	{
		// far constants
		pov3d[3][4] = '_';
		pov3d[3][5] = '_';
		pov3d[5][4] = '_';
		pov3d[5][5] = '_';

		if (pov[0][0] == ' ')
		{
			// 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] == ' ')
			{
				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] == ' ')
		{
			// 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] == ' ')
			{
				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 = 177;
	if (pov[1][1] == 'd')
	{
		// front door
		for (int i = 4; i <= 7; i++)
		{
			for (int j = 3; j < 7; j++)
			pov3d[i][j] = doorchar;
		}
	}
	if (pov[2][0] == 'd')
	{
		for (int i = 5; i < 9; i++) pov3d[i][0] = doorchar;
	}
	if (pov[2][2] == 'd')
	{
		for (int i = 5; i < 9; i++) pov3d[i][9] = doorchar;
	}
}

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

	for (int frontoffset = 2; frontoffset >= 0; frontoffset--)
	{
		for (int rtoloffset = -1; rtoloffset < 2; rtoloffset++)
		{
			int newX = x, newY = y;
			switch (orientation)
			{
			case 'n':
				newX += rtoloffset;
				newY += frontoffset;
				break;
			case 's':
				newX -= rtoloffset;
				newY -= frontoffset;
				break;
			case 'e':
				newX += frontoffset;
				newY -= rtoloffset;
				break;
			case 'w':
				newX -= frontoffset;
				newY += rtoloffset;
				break;
			}
			pov[2 - frontoffset][1 + rtoloffset] = charatpos(newX, newY);
		}
	}

	update3dpov();
}

void forward()
{
	if (pov[1][1] != 'w')
	{
		switch (state.location.orientation)
		{
		case 'n':
			state.location.y += 2;
			break;
		case 's':
			state.location.y -= 2;
			break;
		case 'e':
			state.location.x += 2;
			break;
		case 'w':
			state.location.x -= 2;
			break;
		}
	}
}

void right()
{
	switch (state.location.orientation)
	{
	case 'n':
		state.location.orientation = 'e';
			break;
	case 's':
		state.location.orientation = 'w';
			break;
	case 'e':
		state.location.orientation = 's';
			break;
	case 'w':
		state.location.orientation = 'n';
			break;
	}
}

void left()
{
	switch (state.location.orientation)
	{
	case 'n':
		state.location.orientation = 'w';
			break;
	case 's':
		state.location.orientation = 'e';
			break;
	case 'e':
		state.location.orientation = 'n';
			break;
	case 'w':
		state.location.orientation = 's';
			break;
	}
}

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

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

void init()
{
	FILE* file = fopen(statepath, "r");
	if (file)
	{
		fread(&state, sizeof(Gamestate), 1, file);
		fclose(file);
	}
	else
	{
		state.place = PLACE_TITLE;
	}
	loadmap();
}

void update(char command[CMD_LEN])
{
	char c = command[0]; 
	if (state.place == PLACE_MAP)
	{
		switch (c)
		{
		case 's':
			forward();
			break;
		case 'x':
			right();
			break;
		case 'w':
			left();
			break;
		default:
			break;
		}
		updatepov();
	}
	else if (state.place == PLACE_TITLE)
	{
		if (strlen(command) > 0)
		{
			strcpy(state.name, command);
			state.place = PLACE_STATION;
		}
	}
	else if (state.place == PLACE_STATION)
	{
		if (c == 'n')
		{
			state.place = PLACE_MAP;
			state.location.x = 1;
			state.location.y = 7;
			state.location.orientation = 'n';
			updatepov();
		}
	}
}

void draw()
{
	clearscreen();
	
	if (state.place == PLACE_TITLE)
	{
		printf("\n\n      La légende des dongeons de l'espace\n\n\n");
		printf("Bonjour.\n");
		printf("Bienvenue la station spaciale intergalactique.\n");
		printf("Veuillez saisir votre nom:\n");
		printf("> ");

	}
	else if (state.place == PLACE_MAP)
	{
		int shift = 5;
		for (int i = 0; i < shift; i++) printf("\n");

		for (int i = 0; i < shift; i++) printf(" ");
		printf("o----------o\n");

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

		//drawmap();
	}
	else if (state.place == PLACE_STATION)
	{
		printf("%s est à la station intergalactique.\n\n", state.name);
		printf("n: prendre la navette pour le dongeon de l'espace\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] = "";
	
	while (command[0] != 'q')
	{
		update(command);
		draw();
		getcommand(command);
	}
	savestate();
	return 0;
}