import std.stdio;
import std.file;
import std.string;
import std.conv : to;


import gtk.Main;
import gtk.MainWindow;
import gtk.TreeView;
import gtk.ListStore;
import gtk.TreeViewColumn;
import gtk.TreeIter;
import gtk.Label;
import gtk.Entry;
import gtk.CellRenderer;
import gtk.CellRendererText;
import gtk.VBox;
import gtk.Statusbar;
import gtk.Button;
import gtk.ScrolledWindow;

import gtk.HBox;

void main(string[] args)
{
	Main.init(args);

	//Window
	auto window = new MainWindow("2DA-Edit");

	auto cont = new VBox(false, 0);
	window.add(cont);
	cont.setSizeRequest(0, 100);

	auto buttonSave = new Button("document-save-symbolic", GtkIconSize.MENU);
	buttonSave.setTooltipText("Save");
	auto buttonOpen = new Button("document-open-symbolic", GtkIconSize.MENU);
	buttonOpen.setTooltipText("Open 2DA");
	auto buttonInsert = new Button("format-text-direction-ltr-symbolic", GtkIconSize.SMALL_TOOLBAR);
	buttonInsert.setTooltipText("Insert row after");
	auto buttonDelete = new Button("user-trash-symbolic", GtkIconSize.SMALL_TOOLBAR);
	buttonDelete.setTooltipText("Delete row");
	auto buttonRenumber = new Button("view-list-symbolic", GtkIconSize.SMALL_TOOLBAR);
	buttonRenumber.setTooltipText("Renumber all rows");

	version(Windows){
		//Menu bar
		import gtk.HBox;

		auto cont2 = new HBox(false, 0);
		cont.packStart(cont2, false, false, 0);

		cont2.packStart(buttonOpen, false, false, 0);
		cont2.packEnd(buttonSave, false, false, 0);
	}
	else{
		//Header bar
		import gtk.HeaderBar;

		auto header = new HeaderBar();
		window.setTitlebar(header);
		header.setTitle("2DAEdit");
		header.setProperty("show-close-button", true);

		header.packStart(buttonOpen);
		header.packEnd(buttonSave);
	}

	//Status bar
	auto statusbar = new HBox(false, 0);
	cont.packEnd(statusbar, false, false, 5);
	statusbar.packStart(buttonRenumber, false, false, 5);
	statusbar.packStart(buttonInsert, false, false, 5);
	statusbar.packStart(buttonDelete, false, false, 5);

	//TreeView to display database
	auto scroll = new ScrolledWindow(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
	cont.packEnd(scroll, true, true, 0);

	auto tree = new TreeView();
	scroll.add(tree);

	tree.setHeadersVisible(true);
	tree.setEnableSearch(true);
	tree.setProperty("enable-grid-lines", GtkTreeViewGridLines.VERTICAL);
	tree.setProperty("tooltip-column", 0);
	tree.setProperty("reorderable", true);
	tree.setProperty("headers-clickable", true);


	//Configure button callbacks
	buttonSave.addOnClicked((Button){
		Save(tree);
	});

	buttonOpen.addOnClicked((Button){
		import gtk.Dialog;
		import gtk.FileChooserDialog;

		auto fc = new FileChooserDialog("Open 2DA", window, FileChooserAction.OPEN);
		auto res = fc.run();
		if(res==GtkResponseType.OK){
			string filename = fc.getFilename();
			version(Windows)	Open(filename, tree, cast(Object)window);
			else 				Open(filename, tree, cast(Object)header);
		}
		fc.destroy();
	});

	buttonInsert.addOnClicked((Button){
		TreeIter it = tree.getSelectedIter();
		auto store = cast(ListStore)tree.getModel();

		if(store !is null){
			store.insertAfter(it, it);
			store.setValue(it, 0, 0);
			foreach(i ; 1..store.getNColumns()){
				store.setValue(it, cast(int)i, "_");
			}
		}
	});

	buttonDelete.addOnClicked((Button){
		TreeIter it = tree.getSelectedIter();
		auto store = cast(ListStore)tree.getModel();

		if(store !is null)
			store.remove(it);
	});

	buttonRenumber.addOnClicked((Button){
		auto store = cast(ListStore)tree.getModel();

		if(store !is null){
			TreeIter it = new TreeIter();
			if(store.getIterFirst(it)){
				int id = 0;
				do{
					store.setValue(it, 0, id++);
					
				}while(store.iterNext(it));
			}
		}
	});



	//Open if exists
	if(args.length>=2 && exists(args[1])){
		version(Windows)	Open(args[1], tree, cast(Object)window);
		else 				Open(args[1], tree, cast(Object)header);
	}

	window.showAll();
	
	Main.run();

}

void Save(ref TreeView tree){
	auto store = cast(ListStore)tree.getModel();
	if(store !is null){
		TreeIter it = new TreeIter();
		if(store.getIterFirst(it)){
			do{
				std.stdio.write("\t",store.getValueInt(it, 0));
				foreach(i ; 1..store.getNColumns()){
					std.stdio.write("\t",store.getValueString(it, i));
				}
				writeln();
			}while(store.iterNext(it));
		}
	}
	else
		writeln("Nothing to save !");

}

void Open(string file, ref TreeView tree, Object header){

	auto twoda = new TwoDA(file);

	version(Windows)	(cast(Window)header).setTitle(file);
	else{
		import gtk.HeaderBar;
		(cast(HeaderBar)header).setSubtitle(file);
	} 				

	//Delete old store
	auto oldstore = cast(ListStore)tree.getModel();
	if(oldstore !is null)
		oldstore.destroy();

	//Remove columns from TreeView
	foreach(i ; 0..tree.getNColumns)
		tree.removeColumn(tree.getColumn(0));

	//Set store types
	GType type[];
	type~=GType.INT;
	foreach(i;1..twoda.header.length)type~=GType.STRING;

	//Create new store
	auto store = new ListStore(type);
	tree.setModel(store);


	//Setup TreeView columns
	foreach(index, s ; twoda.header){

		CellRendererText cr = new CellRendererText();
		cr.setProperty("editable", true);
		if(index==0){
			cr.setProperty("background-rgba", cast(ulong)(new GdkRGBA(0.36, 0.13, 0.4, 1.0)));
			cr.setProperty("background-set", true);

			cr.addOnEdited((string path, string newval, CellRendererText crt){
				try{
					int n = newval.to!int;
					TreeIter t = new TreeIter(tree.getModel(), path);
					store.setValue(t, cast(int)crt.getData("colnumber"), n);
				}
				catch(Exception e){
					writeln("Not a number");
				}

			});
		}
		else{
			cr.addOnEdited((string path, string newval, CellRendererText crt){
				TreeIter t = new TreeIter(tree.getModel(), path);
				store.setValue(t, cast(int)crt.getData("colnumber"), newval);
			});
		}

		cr.setData("colnumber", cast(void*)cast(int)index);


		auto col = new TreeViewColumn(s, cr, "text", cast(int)index);
		col.setResizable(true);
		//col.setReorderable(true);
		tree.appendColumn(col);
	}

	//Fill database
	TreeIter iter = new TreeIter();

	for(int i=0 ; i<=twoda.lastLine ; i++){
		store.append(iter);
		store.setValue(iter, 0, i);

		if(i in twoda.values){
			foreach(index, v ; twoda.values[i]){
				store.setValue(iter, cast(int)index+1, v);
			}
		}
		else{
			foreach(index ; 1..twoda.header.length){
				store.setValue(iter, cast(int)index+1, "_");
			}
		}
	}

	//Autosize columns
	tree.columnsAutosize();

	tree.setSizeRequest(50, 50);
}



class TwoDA{
	import std.regex;
	import std.file;

	this(string filepath){
		lastLine = 0;
		foreach(index, line ; readText(filepath).splitLines()){
			
			string data[]; 

			auto results = matchAll(line, rgxField);
			foreach(res ; results){
				string s;
				if(res[0][0]=='"')	data~= res[2];
				else				data~= res[1];
			}

			if(index==0){
				header = data;
				//writeln(header);
			}
			else{
				int nLine = data[0].to!int;
				values[nLine] = data[1..$];
				//writeln(values[nLine]);

				if(nLine > lastLine)
					lastLine = nLine;
			}

		}

	}


	string[] header;
	string[][uint] values;
	uint lastLine;
	enum rgxField = ctRegex!"(?:\\b([^\\s]+?)\\b|\"([^\"]+?)\")";
}

