#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "interface.h"
#include "sdl.h"
#include "parser.h"
#include "lpt_parser.h"
#include "input.h"

enum {
	PARSER_NORMAL,
	BACKGROUND,

	ADD_ENTITY,
	ADD_ENTITY_ARG2,

	ADD_MESSAGE,
	ADD_MESSAGE_ARG2
};

struct ENTITY {
	float x;
	float scale;
	SDL_Texture *texture;

	struct ENTITY *next;
};

void free_chain(struct ENTITY **ent) {
	while (*ent != NULL) {
		struct ENTITY *i = (*ent)->next;

		if ((*ent)->texture)
			SDL_DestroyTexture((*ent)->texture);

		free(*ent);
		*ent = i;
	}
}

int DisplayScene(SDL_Texture *background, TEXT author, TEXT message, APP *app, FONTS *fonts, CONFIG cfg, struct ENTITY *ent) {
	char run = LPTP_NORMAL;

	while (run == LPTP_NORMAL) {
		int status = Input(app, &run, fonts, IN_GAME);
		switch (status) {
			case EXIT:
				run = MENU;
				/* fallthrough */

			case SAVE:
				WriteCfg(cfg);
				break;

			case KEY_RIGHT:
				run = NEXT;
				break;

			default:
				break;
		}

		/* Background */
	   	SDL_SetRenderDrawColor(app->render, 0xFF, 0xFF, 0xFF, 0xFF);
		SDL_RenderClear(app->render);

		SDL_RenderCopy(app->render, background, NULL, NULL);

		/* Entities */
		struct ENTITY *bckp = ent;
		while (bckp != NULL) {
			struct ENTITY *i = bckp->next;

			int w = 0, h = 0;
			SDL_QueryTexture(bckp->texture, NULL, NULL, &w, &h);

			SDL_Rect rect = {
				.x = APPROX2ORIG(app->scr_width, bckp->x),
				.y = APPROX2ORIG(app->scr_height, 0),
				.w = w * bckp->scale,
				.h = h * bckp->scale
			};

			if (bckp->texture)
				SDL_RenderCopy(app->render, bckp->texture, NULL, &rect);

			bckp = i;
		}

		/* Dialog */
		AddPseudoButton(app, message, 0);
		AddPseudoButton(app, author, 1);

		/* Render */
		SDL_RenderPresent(app->render);
		SDL_Delay(20);
	}

	return run;
}

int lpt_parser(APP *app, FONTS *fonts, CONFIG *cfg) {
	char path[PATH_MAX + 1];
	snprintf(path, sizeof(path), "files/scenes/%d.lpt", cfg->saved_scene);

	FILE *fp = OpenFile(path, "r");
	if (fp == NULL) {
		fputs("Maybe it's error?? Or endgame\n", stderr);
		return ENDGAME;
	}

	char ret = ERROR;

	/* Parser */
	char *raw_str = NULL;
	ssize_t bytes = 0;
	size_t n = 0;
	size_t line = 0;
	char flag = PARSER_NORMAL;

	/* For scene */
	SDL_Texture *background = NULL;

	TEXT author, msg;
	memset(&author, 0, sizeof(author));
	msg = author;

	struct ENTITY *entities = NULL;
	struct ENTITY *ptr = NULL;

	while ((bytes = getline(&raw_str, &n, fp)) > 0) {
		line++;

		raw_str[strcspn(raw_str, "\n")] = '\0';

		char *str = raw_str;
		while (isspace(*str))
			str++;

		if (*str == '#')
			continue;

		if (flag == BACKGROUND) {
			if (background)
				SDL_DestroyTexture(background);

			background = LoadIMG_SDL(str, app);
			if (background == NULL)
				goto LPT_PARSER_CLOSE_UP;
		}

		else if (flag == ADD_MESSAGE) {
			if (author.init)
				SDL_DestroyTexture(author.texture);

			memset(&author, 0, sizeof(author));

			PrintText(fonts->font1, app, &author, str);
			if (author.init == 0)
				goto LPT_PARSER_CLOSE_UP;
		}

		else if (flag == ADD_MESSAGE_ARG2) {
			if (msg.init)
				SDL_DestroyTexture(msg.texture);

			memset(&msg, 0, sizeof(msg));

			PrintText(fonts->font1, app, &msg, str);
			if (msg.init == 0)
				goto LPT_PARSER_CLOSE_UP;

			if (DisplayScene(background, author, msg, app, fonts, *cfg, entities) == MENU) {
				ret = MENU;
				break;
			}
		}

		else if (flag == ADD_ENTITY) {
			entities = malloc(sizeof(struct ENTITY));
			if (entities == NULL) {
				fprintf(stderr, "LPT_parser: failed to add entitiy: malloc: %s\n", strerror(errno));
				goto LPT_PARSER_CLOSE_UP;
			}

			memset(entities, 0, sizeof(struct ENTITY));
			entities->texture = LoadIMG_SDL(str, app);
			if (entities->texture == NULL)
				goto LPT_PARSER_CLOSE_UP;
		}

		else if (flag == ADD_ENTITY_ARG2) {
			char *tok_rest = str;
			char *x_pos = strtok_r(tok_rest, " ", &tok_rest);
			char *scale = strtok_r(tok_rest, " ", &tok_rest);
			if (scale == NULL || x_pos == NULL) {
				fprintf(stderr, "SCENE %d:%ld: invalid syntax: %s\n", cfg->saved_scene, line, str); 
				goto LPT_PARSER_CLOSE_UP;
			}

			entities->x = atof(x_pos);
			entities->scale = atof(scale);

			entities->next = ptr;
			ptr = entities;
		}

		/* Parsing file */
		if (flag == PARSER_NORMAL && bytes > 1) {
			if (!strcmp(str, "Background:"))
				flag = BACKGROUND;

			else if (!strcmp(str, "AddEntity:"))
				flag = ADD_ENTITY;

			else if (!strcmp(str, "AddMessage:"))
				flag = ADD_MESSAGE;

			else if (!strcmp(str, "ClearEntity")) {
				free_chain(&entities);
				ptr = NULL;
			}

			else {
				fprintf(stderr, "SCENE %d:%ld : invalid syntax: %s\n", cfg->saved_scene, line, str);
				goto LPT_PARSER_CLOSE_UP;
			}
		}

		else {
			if (flag == ADD_MESSAGE)
				flag = ADD_MESSAGE_ARG2;

			else if (flag == ADD_ENTITY)
				flag = ADD_ENTITY_ARG2;

			else
				flag = PARSER_NORMAL;
		}
	}

	if (ret != MENU)
		ret = LPTP_NORMAL;

	/* Close up */
LPT_PARSER_CLOSE_UP:
	if (raw_str)
		free(raw_str);

	if (background)
		SDL_DestroyTexture(background);

	if (author.init)
		SDL_DestroyTexture(author.texture);

	if (msg.init)
		SDL_DestroyTexture(msg.texture);

	free_chain(&entities);

	fclose(fp);
	return ret;
}
