// TinyJS_Functions2.cpp

#include "TinyJS_Functions2.h"
#include <Windows.h>
#include <string>

void js_Array_join(CScriptVar *v, void* data) {
	std::string delimiter = v->getParameter("delim")->getString();
	std::string result;

	CScriptVarLink* fc = v->firstChild->var->firstChild;
	if (! fc) return;
	result += fc->var->getString();
	while (fc = fc->nextSibling) {
		result += delimiter;
		result += fc->var->getString();
	}

	v->getReturnVar()->setString(result);
}

void js_Array_push(CScriptVar *v, void* data) {
	CScriptVar* el = v->getParameter("el");
	CScriptVarLink* fc = v->firstChild;

	char idx_str[16]; // Supports up to 10^15 ~= 2^32
	sprintf_s(idx_str, "%d", fc->var->getArrayLength());

	fc->var->addChild(idx_str, el);
}

void js_String_fcc(CScriptVar *v, void* data) {
	int ci = v->getParameter("i")->getInt();
	char c[2] = {ci, 0};
	v->getReturnVar()->setString(c);
}

void js_String_split(CScriptVar *v, void* data) {
	std::string delimiter = v->getParameter("delim")->getString();
	std::string text = v->firstChild->var->getString();

	// Prepare the output
	CScriptVar ret("", SCRIPTVAR_ARRAY);

	// Iterate over the text
	size_t i = 0, last = 0, needle_len = delimiter.length();
	int idx = 0;
	for(;;) {
		i = text.find(delimiter, last);
		if (i == text.npos) {
			// Reached the end of the string, push the remaining characters
			char idx_str[16];
			sprintf_s(idx_str, "%d", idx++);
			CScriptVar* child = new CScriptVar(text.substr(last));
			ret.addChild(idx_str, child);
			break;
		}

		// Push the area surrounded by delimiters
		char idx_str[16];
		sprintf_s(idx_str, "%d", idx++);
		CScriptVar* child = new CScriptVar(text.substr(last, i-last));
		ret.addChild(idx_str, child);

		last = i+needle_len;
	}

	// Done
	v->getReturnVar()->copyValue(&ret);
}

std::string md5(std::string data) {
	HCRYPTPROV hCp; HCRYPTHASH hHash;
	CryptAcquireContext(&hCp, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
	CryptCreateHash(hCp, CALG_MD5, NULL, 0, &hHash);

	unsigned char theHash[16] = {0};
	DWORD theHashLen = 16;

	CryptHashData(hHash, (const unsigned char*) data.c_str(), data.length(), 0);
	CryptGetHashParam(hHash, HP_HASHVAL, theHash, &theHashLen, 0);

	CryptDestroyHash(hHash);
	CryptReleaseContext(hCp, 0);

	char hashOutput[64] = {0};
	for (int i = 0; i < 16; i++) {
		sprintf(hashOutput + (2*i), "%02x", theHash[i]);
	}

	return hashOutput;
}

void js_md5(CScriptVar *v, void*) {
	std::string data = v->getParameter("data")->getString();
	v->getReturnVar()->setString( md5(data) );
}

void registerMoreFunctions(CTinyJS *js) {
	js->addNative("function Array.join(delim)",     js_Array_join,   0);
	js->addNative("function Array.push(el)",        js_Array_push,   0);
	js->addNative("function String.split(delim)",   js_String_split, 0);
	js->addNative("function String.fromCharCode(i)",js_String_fcc,   0);
	js->addNative("function md5(data)",             js_md5,          0);
}