// px_internal.h
// `````````````

//
// PEXL-D
//

#pragma once
#ifndef PX_INTERNAL_SOURCE
#error "Please don't include this file!"
#endif

#include <string>
#include <map>
#include <vector>

#include "pexl.h"

enum tok_types {
	TOK_INVALID = 0,
	
	TOK_RULES_START = 100,
		TOK_RULE_ALL_IN_ORDER,
		TOK_RULE_OPTIONAL_REMAINING,
		TOK_RULE_ONE_OF_THE_REMAINING,
	TOK_RULES_END,

	TOK_COMPOUND_TYPES_START = 200,
		TOK_BODY, 
		TOK_BLOCK,
		TOK_STATEMENT,
		TOK_STATEMENT_INNER,

		TOK_DIM_VAR,
		TOK_TYPE_IDENTIFIER_LIST,
		TOK_TYPE_IDENTIFIER,
		//TOK_FNDEF_ANNOTATION,
		//TOK_FNDEF_ANNOTATION_LIST,
		TOK_FNDEF_ARGLIST,
		TOK_FNDEF,
		TOK_FNCALL_ARGLIST,
		TOK_FNCALL,

		TOK_RVALUE_INNER2,
		TOK_RVALUE_INNER,
		TOK_RVALUE,
		TOK_LVALUE,
		TOK_ASSIGNMENT,

		TOK_BINARY_OPERATOR,
		//TOK_BINARY_EXPR,

		TOK_RETURN_STATEMENT,
		TOK_IF_STATEMENT,
		TOK_FOR_STATEMENT,
		TOK_IMPORT_CLAUSE,

		TOK_SLICE, // slice_inner|rvalue
		TOK_SLICE_INNER, // rvalue ":" rvalue

		TOK_VECTOR_LITERAL,
		TOK_VECTOR_ELEMENT,

		TOK_INCLUDE_STATEMENT,
	TOK_COMPOUND_TYPES_END,

	TOK_PARSEABLES_START = 300,
		TOK_CBRACE_OPEN,
		TOK_CBRACE_CLOSE, 
		TOK_SBRACE_OPEN,
		TOK_SBRACE_CLOSE,
		TOK_PARENS_OPEN,
		TOK_PARENS_CLOSE,
		TOK_OP_QUOTE,
		TOK_OP_COMMA,
		TOK_OP_COLON,
		TOK_OP_SEMICOLON,
		TOK_OP_EQUALS_EQUALS,
		TOK_OP_EXCLAMATION_EQUALS,
		TOK_OP_EQUALS,
		TOK_OP_PLUS,
		TOK_OP_MINUS,
		TOK_OP_STAR,
		TOK_OP_FORWARDSLASH,
		TOK_OP_BACKSLASH,
		TOK_OP_PERCENT,
		TOK_OP_LESSTHAN,
		TOK_OP_GREATERTHAN,
		TOK_OP_PERIOD,
	TOK_PARSEABLES_END,

	TOK_RWORD_RETURN,
	TOK_RWORD_IF,
	TOK_RWORD_ELSE,
	TOK_RWORD_FOR,
	TOK_RWORD_WHILE,
	TOK_RWORD_DO,
	TOK_RWORD_IMPORT,
	TOK_RWORD_BREAK,
	TOK_RWORD_CONTINUE,
	TOK_RWORD_INCLUDE,

	TOK_LITERAL_TYPENAME,
	TOK_IDENTIFIER,
	TOK_NUMERIC_LITERAL,
	TOK_QUOTED_STRING,

	TOK_MAXTOKS
};

typedef std::map<enum tok_types, std::vector<tok_types>> syntax_map_t;
//typedef std::string PXType;

struct token;

struct PXType {
	std::string serialize();
	bool is_inferable();
	bool is_valid();
	bool is_scalar();
	bool is_scalar(const char* type);
	bool equals(const PXType& other);

	static PXType make_scalar(const char* type);
	static bool unserialize(std::string& typestr, PXType& ret);
	static bool unserialize(struct token* token, PXType& ret);
	
	std::string type;
	std::vector<PXType> children;
};

struct token {
	token() : type(TOK_INVALID), parent(NULL) {};
	token(enum tok_types type) : type(type), parent(NULL) {};
	~token() {this->clear(TOK_INVALID);}

	void clear(enum tok_types type) {
		this->type = type;
		data.clear();
		for (int i = 0, e = children.size(); i < e; i++) {
			delete children[i];
		}
		children.clear(); // normally not needed for bulk free'ing, but required for other uses of clear()
	}

	enum tok_types type; // Type of node
	PXType vartype;      // Resolved type of variable / function return type

	std::string data;
	int data_int;
	std::vector<token*> children;

	token* parent;
};

template<typename K, typename V>
struct lookup_chain {
	lookup_chain(lookup_chain<K,V>* parent) : parent(parent) {};

	void insert(K identifier, V ret) {
		locals[identifier] = ret;
	};

	V find(K identifier) {
		auto it = locals.find(identifier);
		if (it != locals.end()) {
			return it->second;

		} else if (parent) {
			return parent->find(identifier);

		} else {
			return NULL;
		}
	};

private:
	lookup_chain<K,V>* parent;
	std::map<K,V> locals;

	lookup_chain<K,V>(lookup_chain<K,V>& other); // don't accidentally call the copy constructor
};

// 
// PRETTY-PRINTER
//

const char* pexl_pprint_typename(enum tok_types t);
void pexl_pprint_ast(token* t, int indent = 0);

//
// AST FUNCTIONS
//

struct IAstMapper {
	virtual ~IAstMapper() {}
	// Return true to process children
	virtual bool action(token* t) = 0;

	void walk(token* t);
};

//
// STRING FUNCTIONS
//

#define MKSTR(sz, fmt, ...) sz.append( strf(fmt, __VA_ARGS__) ); 
std::string strf(const char* fmt, ...);
std::string escape_string(std::string& in);

std::string make_uniqid();
