#include "MathTools.h"

	
void TabData::Init() {
	CtrlLayout(up);
	CtrlLayout(down);
	CtrlLayout(downL);
	CtrlLayout(downR);
	
	Add(splitter.SizePos());
	splitter.Vert(up.SizePos(), down.SizePos());
	down.splitterDown.Horz(downL.SizePos(), downR.SizePos());
	
	HSizePos().VSizePos();

	downL.gridDef.Clipboard().Editing().Appending().Removing().Sorting(false);
	downL.gridDef.AddColumn(t_("X Col"), 30).Edit(xCell);
	downL.gridDef.AddColumn(t_("From"), 40) .Edit(xFrom);
	downL.gridDef.AddColumn(t_("To"), 60)   .Edit(xTo);
	downL.gridDef.AddColumn(t_("Y Col"), 30).Edit(yCell);
	downL.gridDef.AddColumn(t_("Show"), 30) .Edit(option);
	downL.gridDef.AddColumn(t_("Name"), 60);
	
	option.WhenAction = THISBACK(OnUpdate);
	
	splitterPos = splitterDownPos = Null;
	bool initSplitter = false;
	if (!LoadFromJsonFile(*this) || !notFirstTime) {
		downL.nIsHeader <<= 0;
		down.butFitPM = true;
		notFirstTime = true;
		initSplitter = true;
	}
	if (initSplitter || IsNull(splitterPos)) {
		splitterPos = 5500;
		splitterDownPos = 2500;
	} 
	splitter.SetPos(splitterPos, 0);
	down.splitterDown.SetPos(splitterDownPos, 0);
	
	up.scatter.SetMouseHandling(true, true).ShowButtons().ShowContextMenu().ShowPropertiesDlg().ShowProcessDlg();

	const int totalCols = 50;
	
	editGrid.SetCount(totalCols);
	for(int i = 0; i < totalCols; i++) {
		String name = Format(t_("Column %d"), i + 1);
		downR.grid.AddColumn(name).Width(70).Edit(editGrid[i]);
	}
		
	downR.grid.SelectRow(false);
	downR.grid.SetRowCount(200);
	downR.grid.ResizePaintMode(1);
	downR.grid.Absolute().Clipboard().Editing().Sorting(false);
	downR.grid.WhenPaste = THISBACK(OnAutoset);
	
//	if (downr.gridDef.GetRowCount() == 0) 
//		downr.gridDef.SetRowCount(1);
	
	downR.butClear.WhenPush = THISBACK(OnClear);
	downL.butClearDef.WhenPush = THISBACK(OnClearDef);
	downL.butAutoset.WhenPush = THISBACK(OnAutoset);
	downL.butUpdate.WhenPush = THISBACK(OnUpdate);
	downL.butShowAll.WhenAction = THISBACK(OnShowAll);
	downL.nIsHeader.WhenAction = THISBACK(OnNIsheader);
	
	openMenuButton.Type(t_("Comma separated file"), "*.csv")
			  	  .Type(t_("Binary file"), "*.mtb")
  			  	  .Type(t_("Text file"), "*.txt")
  			  	  .DefaultExt("txt");
	openMenuButton.Attach(down.editFile);	
	openMenuButton.PreSelect(~down.editFile);
	
	butDown.SetImage(CtrlImg::down_arrow());
	butDown <<= THISBACK1(OnChangeFile, 1);
	down.editFile.AddFrame(butDown);
	
	butUp.SetImage(CtrlImg::up_arrow());
	butUp <<= THISBACK1(OnChangeFile, -1);
	down.editFile.AddFrame(butUp);
	
	openButton.SetImage(CtrlImg::menu_check());
	openButton <<= THISBACK2(OnOpen, true, true);
	down.editFile.AddFrame(openButton);
		
	down.editFile.WhenAction = THISBACK2(OnOpen, false, true);
	
	OnOpen(true, true);
}

void TabData::End() {
	splitterPos = splitter.GetPos(0);
	splitterDownPos = down.splitterDown.GetPos(0);
	StoreAsJsonFile(*this, NULL, true);
}

void TabData::OnClear() {
	int nRows = downR.grid.GetRowCount();
	downR.grid.SetRowCount(0);
	downR.grid.SetRowCount(nRows);
	downR.grid.SetCursor(Point(0, 0));
	up.scatter.RemoveAllSeries();
}

void TabData::OnClearDef() {
	downL.gridDef.Clear();
	up.scatter.RemoveAllSeries();
}

bool IsNumber2(const Value &val) {
	if (IsNull(val))
		return false;
	if (IsNumber(val))
		return true;
	if (IsString(val)) {
		double d = ScanDouble(val.ToString());
		if (!IsNull(d))
			return true;
	}
	return false;
}

void TabData::OnAutoset() {
	int titleRow = downL.nIsHeader - 1;
	
	downL.gridDef.Clear();
	
	int xcol = Null;
	for (int c = 0; c < downR.grid.GetColumnCount(); ++c) {
		if (IsNumber2(downR.grid.Get(titleRow+1, c))) {
			if (IsNull(xcol)) 
				xcol = c;
			else {
				String name = AsString(downR.grid.Get(titleRow, c));
				downL.gridDef.Add(xcol+1, titleRow+2, 0, c+1, true, name);	
			}
		}
	}
	if (downL.gridDef.GetRowCount() == 0) {
		Exclamation(t_("No data found&Maybe '# row is header' is not properly set"));
		return;
	}
	for (int rowdef = 0; rowdef < downL.gridDef.GetRowCount(); ++rowdef) {
		int c = int(downL.gridDef.Get(rowdef, 3))-1;
		int r;
		for (r = titleRow+1; r < downR.grid.GetRowCount(); ++r) {
			if (!IsNumber2(downR.grid.Get(r, c)))
				break;
		}
		downL.gridDef.Set(rowdef, 2, r);
	}
	OnUpdate();
}

void TabData::OnUpdate() {
	if (downR.grid.GetRowCount() == 0 || downL.gridDef.GetRowCount() == 0)
		return;
	
	for (int r = 0; r < downL.gridDef.GetRowCount(); ++r) {
		String name;
		
		int row = int(downL.gridDef.Get(r, 1)) - 1 - 1;
		if (row < 0)
			row = 0;
		int col = int(downL.gridDef.Get(r, 3)) - 1;
		name = AsString(downR.grid.Get(row, col));
		downL.gridDef.Set(r, 5, name);
	}
	
	up.scatter.RemoveAllSeries();
	ds.Clear();
	for (int grow = 0; grow < downL.gridDef.GetRowCount(); ++grow) {
		if (!downL.gridDef.Get(grow, 4))
			continue;
		int idX = int(downL.gridDef.Get(grow, 0))-1;
		int idY = int(downL.gridDef.Get(grow, 3))-1;
		int beginData = int(downL.gridDef.Get(grow, 1))-1;
		int numData = int(downL.gridDef.Get(grow, 2)) - beginData;
		GridCtrlSource &uds = ds.Add();
		uds.Init(downR.grid, idY, idX, true, beginData, numData);
		String serieName, xLegend;
		if (downL.nIsHeader > 0) {
			int row, col;
			row = int(~downL.nIsHeader) - 1;
			col = int(downL.gridDef.Get(grow, 0))-1;
			if (IsNull(col))
				return;
			xLegend = AsString(downR.grid.Get(row, col));
			col = int(downL.gridDef.Get(grow, 3))-1;
			if (IsNull(col))
				return;
			serieName = AsString(downR.grid.Get(row, col));
		} else
			serieName << t_("Series") << " " << grow;
		up.scatter.AddSeries(uds).Legend(serieName).NoMark().Stroke(2);
		if (!xLegend.IsEmpty()) {
			up.scatter.SetPlotAreaBottomMargin(45);
			up.scatter.SetLabelX(xLegend);
		} else {
			up.scatter.SetPlotAreaBottomMargin(30);
			up.scatter.SetLabelX("");
		}
	}
	if (down.butFitPM)
		up.scatter.ZoomToFit(true, true);
}

int GuessCsvSeparator(FileIn &in) {
	Vector<int> sep;
	sep << ',' << '.' << ';' << ':' << '\t' << ' ';
	Vector<String> lines;
	for (int i = 0; i < 20 && !in.IsEof(); ++i) 
		lines << in.GetLine();
	if (lines.GetCount() >= 10)
		lines.Remove(0, 10);
	Vector<int> sepCount;
	sepCount.SetCount(sep.GetCount(), 0);
	for (int i = 0; i < lines.GetCount(); ) {
		String &s =	lines[i];
		bool instring = false;
		for (int ic = 0; ic < s.GetCount(); ++ic) {
			int c = s[ic];
			if(c == '\"') {
				if(instring && (i < lines.GetCount()-1) && s[ic+1] == '\"') 
					i++;
				else
					instring = !instring;
			} else if (!IsLeNum(c)) {
				for (int is = 0; is < sep.GetCount(); ++is) {
					if (c == sep[is])
						sepCount[is]++;
				}
			}
			i++;
		}
	}
	int separator = Null;
	IndexSort(sepCount, sep, StdGreater<int>());
	int idMax = (sep[0] == '.') ? 1 : 0;
	if (sepCount[idMax] > 0)
		separator = sep[idMax];	
	in.Seek(0);
	return separator;
}
		
void TabData::OnOpen(bool force, bool updateButtons) {
	if (!force && (~openMenuButton != ~down.editFile))
		return;
	
	WaitCursor wait;
	if (updateButtons) {
		butUp.Show(false);
		butDown.Show(false);
	}

	String fileName = TrimBoth(~down.editFile);
	if (fileName.IsEmpty())
		return;
	FindFile ff(fileName);
	if(!ff || !ff.IsFile()) {
		Exclamation(t_("File does not exist"));
		return;
	}
	openMenuButton.PreSelect(fileName);
	
	if (GetFileExt(fileName) == ".mtb") {
		ScatterDraw data;
		
		if (!LoadFromFile(data, fileName)) {
			Exclamation(t_("Problem reading binary file"));
			return;
		}
		if (data.GetCount() == 0 || data.GetSeries(0).GetCount() == 0) {
			Exclamation(t_("Data table not found"));
			return;
		}
		downR.grid.SetRowCount(0);
		/*for (int col = 0; col < saver.data[0].data.GetCount(); ++col) {
			for (int row = 0; row < saver.data[0].data[col].GetCount(); row++) 
				downR.grid.Set(row, col, saver.data[0].data[col][row]);	
		}*/
	} else {
		FileIn in(fileName);
		in.ClearError();
		
		int separator = GuessCsvSeparator(in);
		if (IsNull(separator))
			separator = ';';
		
		downR.grid.SetRowCount(0);
			
		byte charset = CHARSET_UTF8;
	
		for (int row = 0; ; row++) {
			String sline = in.GetLine();
			if (sline.IsVoid())
				break;
			StringStream line(sline);
			Vector<String> fields = GetCsvLine(line, separator, charset);	
			for (int col = 0; col < fields.GetCount(); ++col) {
				double d = ScanDouble(fields[col]);
				if (IsNull(d))
					downR.grid.Set(row, col, fields[col]);	
				else
					downR.grid.Set(row, col, d);	
			}
		}
	}
	if (down.butSetPM || downL.gridDef.GetRowCount() == 0)
		OnAutoset();
	else
		OnUpdate();
	up.scatter.SetTitle(GetFileName(fileName));
	if (updateButtons)
		OnChangeFile(0);
}

void TabData::OnChangeFile(int delta) {
	String fileName = ~down.editFile;
	
	String ext = GetFileExt(fileName);
	FindFile ff(AppendFileName(GetFileFolder(fileName), "*" + ext));
	Index<String> fileList;
	do {
		if (ff.IsFile()) 
			fileList << ff.GetPath();
	} while(ff.Next());
	if (fileList.IsEmpty()) {
		butUp.Show(false);
		butDown.Show(false);
		return;
	}
	int id = fileList.Find(fileName) + delta;
	butUp.Show(id != 0);
	butDown.Show(id != fileList.GetCount() - 1);
 	
 	if (delta == 0)
 		return;
	down.editFile <<= fileList[id];
	OnOpen(true, false);
}

void TabData::OnShowAll() {
	bool showAll = downL.butShowAll;
	for (int grow = 0; grow < downL.gridDef.GetRowCount(); ++grow) 
		downL.gridDef.Set(grow, 4, showAll);
	OnUpdate();
}

void TabData::OnNIsheader() {
	int n = downL.nIsHeader + 1;
	for (int grow = 0; grow < downL.gridDef.GetRowCount(); ++grow) 
		downL.gridDef.Set(grow, 1, n);
	OnUpdate();	
}

Tool *Construct1()
{
	static TabData tab;
	tab.name = t_("Data Handling");
	return &tab;
}

INITBLOCK {
	RegisterExample(Construct1, __FILE__);
}