#ifndef PARSER_H
#define PARSER_H

#include <stddef.h>

#include "lexer.h"
#include "token.h"

struct expresion {
	char reserved;
};

struct stmt_var_def {
	struct token type;
	struct token ident;
	struct expresion exp;
};

struct stmt {
	char reserved[sizeof(struct stmt_var_def)];
};

struct parser {
	struct lexer l;
	struct token cur_tk;
	struct token peek_tk;
	struct stmt prog[1024];
};

enum parser_err_code {
	PARSER_ERR_OK = 0,
	PARSER_ERR_FAILED_READ_NEXT_TOKEN
};

struct parser_err {
	enum parser_err_code code;
	const char *name;
};

struct parser parser_create(struct lexer l, struct parser_err *err);

void parser_next_token(struct parser *p, struct parser_err *err);

void parser_parse_program(struct parser *p, struct parser_err *err);

struct parser_err parser_err_create(enum parser_err_code code);
void _parser_set_err(struct parser_err *err, enum parser_err_code code);

#if defined(IMP) || defined(PARSER_IMP)

struct parser
parser_create(struct lexer l, struct parser_err *err)
{
	struct parser_err _err = {0};
	struct parser p_empty = {0};
	struct parser p = {0};
	p.l = l;
	parser_next_token(&p, &_err);
	if ( _err.code != PARSER_ERR_OK ) {
		goto ret_err;
	}
	parser_next_token(&p, &_err);
	if ( _err.code != PARSER_ERR_OK ) {
		goto ret_err;
	}
	return p;
ret_err:
	if ( err != NULL ) {
		*err = _err;
	}
	return p_empty;
}

void
parser_next_token(struct parser *p, struct parser_err *err)
{
	struct lexer_err l_err = {0};
	p->cur_tk = p->peek_tk;
	p->peek_tk = lexer_next_token(&p->l, &l_err);
	if ( l_err.code != LEXER_ERR_OK ) {
		_parser_set_err(err, PARSER_ERR_FAILED_READ_NEXT_TOKEN);
		return;
	}
	_parser_set_err(err, PARSER_ERR_OK);
	return;
}

void
parser_parse_program(struct parser *p, struct parser_err *err)
{
	struct parser_err _err = {0};
	goto jump_start;

loop:
	parser_next_token(p, &_err);
	if ( _err.code != PARSER_ERR_OK ) {
		goto ret_err;
	}
jump_start:

	switch ( p->cur_tk.typ.code ) {
	case TT_ILLEGAL:
	case TT_EOF:
		goto out_loop;
	case TT_TYPE: {
		struct token type = p->cur_tk;

		if ( p->peek_tk.typ.code == TT_IDENT ) {
			struct token ident = p->peek_tk;

			parser_next_token(p, &_err);
			if ( _err.code != PARSER_ERR_OK ) {
				goto ret_err;
			}

			if ( p->peek_tk.typ.code == TT_ASSIGN ) {
				/*struct stmt_var_def vd = {0};
				vd.type = type;
				vd.ident = ident;*/
				printf("type: `%.*s`, ident: `%.*s`\n", 
					(int)type.lit.size, type.lit.data,
					(int)ident.lit.size, ident.lit.data);
			}
		}
		goto loop; 
	}
	default:
		goto loop;
	}

out_loop:
	_parser_set_err(err, PARSER_ERR_OK);
	return;

ret_err:
	if ( err != NULL ) {
		*err = _err;
	}
	return;
}


struct parser_err
parser_err_create(enum parser_err_code code)
{
#define _PARSER_ERR_CASE(it) case it: pe.name = #it; break;
	struct parser_err pe = {0};
	pe.code = code;

	switch ( code ) {
	_PARSER_ERR_CASE(PARSER_ERR_OK);
	_PARSER_ERR_CASE(PARSER_ERR_FAILED_READ_NEXT_TOKEN);
	}

	return pe;
#undef _PARSER_ERR_CASE
}

void 
_parser_set_err(struct parser_err *err, enum parser_err_code code)
{
	if ( err != NULL ) {
		*err = parser_err_create(code);
	}
}

#endif /* defined(IMP) || defined(PARSER_IMP) */


#endif /* PARSER_H */
