#include "ximage.h"

#include "tcl.h"
#include "tk.h"

#include "TTW.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include <locale.h>
#include <stdlib.h>
#include <tchar.h>

#include <cstdio>
#include <cctype>

#include <string>
#include <vector>

#include "xlsxwriter.h"

extern "C" {
int Vfs_Init(Tcl_Interp *);
int Memchan_Init(Tcl_Interp *);
int Blt_Init(Tcl_Interp *);
int Tkhtml_Init(Tcl_Interp *);
void TkWinSetHINSTANCE(HINSTANCE hInstance);
}

#pragma region Tclモジュール読み込み処理
std::string read_vfs_script(const char *tarfile){
	if (!tarfile) return "";
	FILE *fp = std::fopen(tarfile, "rb");
	if (!fp) return "";

	std::string script;
	char buf[513];
	buf[512] = '\0';
	int datablocks = 0;
	long filesize = 0;
	bool add_to_script = false;
	while(!std::feof(fp)){
		if (datablocks == 0){ // header
			size_t sz = fread(buf, 512, 1, fp);
			char typeflag = buf[156];
			switch(typeflag){
				case '1': case '2': case '3': case '4': case '5': case '6': case '7':
				continue;
			}
			filesize = std::strtol(buf + 124, 0, 8);
			datablocks = (filesize + 511) / 512;
			add_to_script = (std::strstr(buf, "vfs1.3/tarvfs.tcl") != 0) || (std::strstr(buf, "vfs1.3/vfsUtils.tcl") != 0);
		}else if (add_to_script){
			size_t sz = std::fread(buf, 512, 1, fp);
			--datablocks;
			if (!add_to_script) continue;
			if (filesize < 512) buf[filesize] = '\0';
			script += buf;
			filesize -= 512;
		}else{
			std::fseek(fp, datablocks * 512, SEEK_CUR);
			datablocks = filesize = 0;
		}
	}

	std::fclose(fp);
	return script;
}

void LoadTarRT(Tcl_Interp *interp, const char *exedir_){
	int rc;
	::Tcl_StaticPackage(interp, "Tcl", Tcl_Init, 0);
	::Tcl_StaticPackage(interp, "Tk", Tk_Init, 0);
	::Tcl_StaticPackage(interp, "vfs", Vfs_Init, 0);
	::Tcl_StaticPackage(interp, "memchan", Memchan_Init, 0);

	::TkWinSetHINSTANCE(::GetModuleHandle("OSSLibs.dll"));
	// 実行ファイルのディレクトリを取得
	std::string exedir = exedir_;
	for (size_t i = 0; i < exedir.size(); ++i) if (exedir[i] == '\\') exedir[i] = '/';
	exedir = exedir.substr(0, exedir.find_last_of('/')+1);
	for (size_t i = exedir.size() - 1; i >= 1; --i){
		if (exedir[i-1] == '/' && exedir[i] == '/') exedir.erase(i, 1);
	}

	std::string tcl_lib_dir = (exedir + "lib/tcl8.6/");
	::Tcl_SetVar2(interp, "tcl_library", 0, tcl_lib_dir.c_str(), TCL_GLOBAL_ONLY);
	::Tcl_SetVar2(interp, "env", "TCL_LIBRARY", tcl_lib_dir.c_str(), TCL_GLOBAL_ONLY);

	// tarファイル をマウントするために必要なモジュール群をロード
	rc = ::Vfs_Init(interp);
	rc = ::Memchan_Init(interp);

	// tarファイル をマウントするために必要なスクリプトをtarファイルの中から見つけて取得
	std::string scr = read_vfs_script((exedir + "tcl_rt.tar").c_str());
	rc = ::Tcl_Eval(interp, scr.c_str());

	// tarファイル をマウント
	rc = ::Tcl_Eval(interp, "package require vfs::tar;");
	rc = ::Tcl_Eval(interp, "vfs::tar::Mount $tcl_library/../../tcl_rt.tar $tcl_library/../;");
	// ↑ interp ではなく、 Tcl_FS の仕組み自体にマウントされるため、 interp とは関係なしに Tcl_FS系の関数で tcl_rt.tar の中身にアクセスできる。
}
#pragma endregion

void CreateRef(const char *prefix, const char *sheet, lxw_row_t row, lxw_col_t col, const char *surfix, TTW_DString &name){
	name << prefix;
	if (sheet){
		name << sheet << "!";
	}
	int prefix_cnt = name.Length();
	name.SetLength(prefix_cnt + 100);
	lxw_rowcol_to_cell(name + prefix_cnt, row, col);
	name.SetLength(std::strlen(name));
	name << surfix;
	std::printf("ref:%s\n", name.Value());
}


int CreateCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]){
	TTW_DString filename;
	filename.Utf8ToSys(objv[1]);

	const char *sheet_name = "Sheet";
	int chart_col_skip = 8, chart_row_skip = 13, chart_cols = 3;

	lxw_workbook  *workbook  = workbook_new(filename.Value());
    lxw_worksheet *worksheet = workbook_add_worksheet(workbook, sheet_name);
    lxw_format *format = workbook_add_format(workbook);

	lxw_row_t row = 0;
	lxw_col_t col = 0;

	TTW_SplitUTF8String ss_line(TTW_GetString(objv[2]), "\n");
	const char *line_start; int line_len;

	enum{
		LT_Title, LT_Axis, LT_Value
	}LineType;

	std::vector<lxw_chart *> charts;
	lxw_chart *chart = 0;
	lxw_chart_series *series = 0;

	TTW_DString ref;

	lxw_row_t current_category_row = 0;
	lxw_col_t current_category_col_max = 0;

	while(ss_line.Next(line_start, line_len)){
		TTW_DString line(line_start, line_len);
		TTW_SplitUTF8String ss_token(line, "\t");

		col = 0;
		const char *token_start; int token_len;

		while(ss_token.Next(token_start, token_len)){
			TTW_DString token(token_start, token_len);
			std::printf("[%s]\t", token.Value());
			if (col == 0){
				char c = token[0];
				if (c == '['){
					LineType = LT_Title;
				    chart = workbook_add_chart(workbook, LXW_CHART_SCATTER_SMOOTH_WITH_MARKERS);
					chart_title_set_name_range(chart, sheet_name, row, col);
					chart_legend_set_position(chart, LXW_CHART_LEGEND_OVERLAY_TOP_RIGHT);
					charts.push_back(chart);
					current_category_row = 0;
					current_category_col_max = 0;
				}else if (c == '*'){
					LineType = LT_Axis;
					current_category_row = row;
					current_category_col_max = 0;
				}else{
					LineType = LT_Value;
					series = chart_add_series(chart, 0, 0);
					chart_series_set_categories(series, sheet_name, current_category_row, 1, current_category_row, current_category_col_max);
					chart_series_set_values(series, sheet_name, row, 1, row, current_category_col_max);
					chart_series_set_name_range(series, sheet_name, row, 0);
				}
			    worksheet_write_string(worksheet, row, col, token.Value(), format);
			}else{
				if(LineType == LT_Title){
					worksheet_write_string(worksheet, row, col, token.Value(), format);
				}else{
				    worksheet_write_number(worksheet, row, col, std::strtod(token.Value(), 0), format);
				}
				if (LineType == LT_Axis){
					current_category_col_max = col;
				}
			}

			++col;
		}
		++row;
		std::printf("\n");
	}

	int chart_row = row, chart_col = 0;
	for (size_t i = 0; i < charts.size(); ++i){
		worksheet_insert_chart(worksheet, chart_row, chart_col, charts[i]);
		chart_col += chart_col_skip;
		if (chart_col >= chart_col_skip * chart_cols){
			chart_col = 0; chart_row += chart_row_skip;
		}
	}

	workbook_close(workbook);

	return TCL_OK;
}

#if 0
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    int argc = __argc;
    char **argv = __argv;
#else
int main(int argc, char *argv[]){
#endif

	const char *exedir_ = argv[0];

	Tcl_Interp *interp = ::Tcl_CreateInterp();
	LoadTarRT(interp, exedir_);
	Tcl_FindExecutable(argv[0]);
	int rc = 0;
	rc = Tcl_Init(interp);
	rc = Tk_Init(interp);

	::Tcl_CreateObjCommand(interp, "::create_chart::create", CreateCmd, 0, 0); 

	::Tcl_Eval(interp, 
		"wm title . {Create Chart};\n"
		"set filename {chart.xlsx};\n"
		"text .txt -width 80 -height 20;\n"
		"label .lfile -text {output filename};\n"
		"entry .efile -textvariable filename;\n"
		"button .bcreate -text {create} -command {::create_chart::create $filename [.txt get 1.0 end]};\n"
		"grid .txt   -        -sticky news;\n"
		"grid .lfile .efile   -sticky ew;\n"
		"grid x      .bcreate -sticky e;\n"
	);
	::Tk_MainLoop();

    return 0;
}

