
#include "ml_util.h"

#include <string>
#include <vector>

    const std::vector<std::string> commands {
	"eval", "type", "parse", "do", "if", "for", "while", "scope", "quote", "defun", 
	"define", "lambda", "benchmark", "=", "!=", ">", "<", ">=", "<=", "+", "-", "*", "/", "%", 
	"list", "insert", "index", "remove", "len", "push", "pop", "head", "tail", "first", "last", 
	"range", "map", "filter", "reduce", "exit", "quit", "print", "input", "random", "include", 
	"read-file", "write-file", "read-url", "system-cmd", "ls-dir", "is-file?", "is-dir?", 
	"parse-csv", "parse-json", "get-universal-time", "date-to-str", "str-to-date", "date-add", "debug", 
	"sprintf", "display", "string-replace", "string-regex?", "string-pad", "int", "float", "string",
	"benchmark", "thread-create", "thread-under-lock", "thread-sleep", "threads-join", "try", "throw",
	"usql"
	};

std::string get_history_file_dir() {
	// TODO not portable and in function
	std::string file{"/.ml_history.txt"};
	const char *t = std::getenv("HOME");

	if (t == nullptr) return "/tmp/" + file;
	else return std::string{t} + "/" + file;
}	

// TODO fujtajbl
MlEnvironment * repl_env = nullptr;

void setup_linenoise(const MlEnvironment &env) {
    repl_env = (MlEnvironment*) &env;

	std::string history_file = get_history_file_dir();

	linenoiseHistorySetMaxLen(500);
	linenoiseSetCompletionCallback(completion);
	linenoiseSetHintsCallback(hints);
	linenoiseSetMultiLine(1);
	linenoiseHistoryLoad(history_file.c_str()); 
}

void linenoise_line_read(char *line) {
    linenoiseHistoryAdd(line);
}

void close_linenoise() {
    std::string history_file = get_history_file_dir();

    linenoiseHistorySave(history_file.c_str());
}

size_t last_token_index( std::string str ) {
    // remove trailing white space
    while( !str.empty() && std::isspace( str.back() ) ) str.pop_back() ;

    // locate the last white space
    return str.find_last_of( "() \t\n" ) ;
}

void completion(const char *buf, linenoiseCompletions *lc) {
    if (buf != nullptr) {
        std::string str{buf};

        const auto pos = last_token_index(str); 
        if (pos == std::string::npos)
            return;     // cannot find what to complete

        std::string token = str.substr(pos+1);
        std::string begining = str.substr(0, pos+1);

        // TODO optimize not to get all lambdas, but those begining with token
        std::vector<std::string> lambdas = repl_env->get_lambdas_list();
    	lambdas.insert(end(lambdas), begin(commands), end(commands));

        for (std::vector<std::string>::iterator t = lambdas.begin(); t != lambdas.end(); ++t) {
            if(t->find(token) == 0) {
                std::string completion_string = begining + *t;
                linenoiseAddCompletion(lc, completion_string.c_str());
            }
        }
    }
}

char *hints(const char *buf, int *color, int *bold) {
//     if (!strcasecmp(buf,"hello")) {
//         *color = 35;
//         *bold = 0;
//         return " World";
//     }
    return nullptr;
}
