#ifndef PARSER_H_
#define PARSER_H_

#include <iostream>
#include <string>
#include <map>

#include "tablica.h"
#include "main.h"

enum TokenType {
	// NULL i EOF tokeni
	T_NULL, T_EOF,
	// pomocni znakovi
	T_COMMA, T_LPAREN, T_RPAREN, T_SEMICOLON, T_STAR,
	// relacijski operatori
	T_EQUAL, T_GREATEREQUAL, T_GREATER, T_LESSEQUAL, T_LESS, T_NOTEQUAL,
	// razne kljucne rijeci
	T_BY, T_CREATE, T_DROP, T_FROM, T_GROUP, T_HAVING, T_INSERT, T_INTO, T_SELECT, T_TABLE, T_VALUES,
	// tipovi podataka
	T_CHAR, T_BOOL, T_FLOAT, T_INTEGER, T_STRING,
	// agregatne funkcije
	T_COUNT, T_MIN, T_MAX, T_AVG, T_SUM,
	// vrijednosti i identifikatori
	T_BOOLVAL, T_CHARVAL, T_FLOATVAL, T_ID, T_INTVAL, T_STRINGVAL
};

enum ParserState {	S_IDLE, S_CREATE, S_DROP, S_INSERT, S_SELECT, S_FINISHED, S_ERROR };

class Parser {
	std::istream *ulaz;
	std::ostream *izlaz;
	Baza *baza;
	// Ovdje pamtim zadnji procitani token
	std::string savedToken;
	ParserState state;
	static std::map<std::string, TokenType> kljucneRijeci;
	
	TokenType nextToken();
	void create();
	void drop();
	void insert();
	void select();
	static bool tipPodatka(TokenType t) {
		return t == T_CHAR || t == T_BOOL || t == T_FLOAT || t == T_INTEGER || t == T_STRING;
	}
	static bool vrijednost(TokenType t) {
		// dozvoljavam i T_ID da bude vrijednost da izbjegnem to sto ponekad ljudi zele pisati stringove bez navodnika i sl.
		return t == T_BOOLVAL || t == T_CHARVAL || t == T_FLOATVAL || t == T_INTVAL || t == T_STRINGVAL || t == T_ID;
	}
	static bool funkcija(TokenType t) {
		return t == T_COUNT || t == T_MIN || t == T_MAX || t == T_AVG || t == T_SUM;
	}
	static bool reloperator(TokenType t) {
		return t == T_EQUAL || t == T_GREATEREQUAL || t == T_GREATER || t == T_LESSEQUAL || t == T_LESS || t == T_NOTEQUAL;
	}
	static AgregatnaFunkcija tokToAgregF(TokenType t) {
		switch (t) {
			case T_COUNT: return AF_COUNT;
			case T_AVG: return AF_AVG;
			case T_MIN: return AF_MIN;
			case T_MAX: return AF_MAX;
			case T_SUM: return AF_SUM;
			default: return AF_NONE;
		}
	}
	static RelacijskiOperator tokToRelOp(TokenType t) {
		switch (t) {
			case T_EQUAL: return OP_EQUAL;
			case T_GREATEREQUAL: return OP_GREATEREQUAL;
			case T_GREATER: return OP_GREATER;
			case T_LESSEQUAL: return OP_LESSEQUAL;
			case T_LESS: return OP_LESS;
			case T_NOTEQUAL: return OP_NOTEQUAL;
			default: return OP_NONE;
		}
	}
	static Tip tokToTip(TokenType t) {
		switch (t) {
			case T_BOOL: case T_BOOLVAL:
				return TIP_BOOL;
			case T_CHAR: case T_CHARVAL:
				return TIP_CHAR;
			case T_FLOAT: case T_FLOATVAL:
				return TIP_FLOAT;
			case T_INTEGER: case T_INTVAL:
				return TIP_INTEGER;
			case T_STRING: case T_STRINGVAL: case T_ID: // T_ID dozvoljavam da bude ucitan kao string
				return TIP_STRING;
			default:
				return TIP_NULL;
		}
	}
	void greska(const std::string &poruka = "Parsing error") {
		*izlaz << poruka << std::endl;
		if (state != S_IDLE && state != S_FINISHED) {
			TokenType token;
			while ((token = nextToken()) != T_EOF && token != T_SEMICOLON) // idi do EOF ili nove naredbe
				;
		}
		state = S_ERROR;
	}

public:
	Parser();
	std::string getSavedToken() { return savedToken; }
	ParserState getState() { return state; }
	void parse(Baza &, std::istream & = std::cin, std::ostream & = std::cout);
};

#endif /*PARSER_H_*/
