#include "pgAdmin3.h"
#include "utils/align/AlignWrap.h"
#include <vector>


wxString AlignWrap::build(wxString& strsrc, int config,wxString linesep)
{
	str = strsrc;
	cfg = config;
	lnsep = linesep;
	bool br = false;
	int p = 0;
	list.clear();
	std::vector<size_t> nline; // позиции в list начала строк
	int len = strsrc.Length();
	nline.push_back(0);
	int nrow = -1;
	// parse items
	while (p < len) {
		Item i = parseItem(p, br);
		if (i.type == Item::type::SPACE) break;
		if (i.type == Item::type::COMMENT) {
			if (list.size() == 0) return strsrc;
			Item k = list[list.size() - 1];
			wxString cc = i.getValue();
			cc = cc.Trim(true);

			if (k.br) {
				cc = lnsep + cc;
				//if (CHKCFGPARAM(cfg, ALL_LINES))
				br = false; // чтобы повторно не добавлять новую строку в nline
			}
			k.setComment(k.getComment()+cc);
			k.br = i.br;
			list.erase(list.end()-1);
			i = k;
			
		};
		list.push_back(i);
#ifdef _DEBUG
		//std::cerr << i.println();
#endif
		if (br) {
			nline.push_back(list.size()); // следующий элемент будет с новой строки
			nrow++;
		}
		br = false;
	}
	if (list.size() == 0) return strsrc;
	nline.push_back(list.size() - 1);
	if (CHKCFGPARAM(cfg, ONLY_SINGLE_SPACE)) {
		wxString a;
		for (int j = 0; j < list.size() - 1; j++) {
			if (list[j].rs > 1) list[j].rs = 1;
		}

	}
	else {
		// 
		bool iscompact=false;
		if (CHKCFGPARAM(cfg, COMPACT_VIEW)) iscompact=true;

		for (size_t l = 1; l < nline.size() - 1; l++)
		{
			int index_u = nline[l - 1L];
			int index_c = nline[l];
			int CountItemU = index_c - index_u;
			int maxIdxU = index_c;
			int maxIdxC = nline[l + 1L];

			size_t ll = l;
			if (CHKCFGPARAM(cfg, FIND_UP_LONG_LINE)) {
				while ((ll > 0) && ((((int)nline[ll] - (int)nline[ll - 1L]) - (maxIdxC - maxIdxU)) < 0)) {
					// верхняя строка короче чем текущяя поднимемся вверх для поиска более длинной
					ll--;
				}
				if (ll == 0) ll = l;
			}
			index_u = nline[ll - 1L];
			maxIdxU = nline[ll];
			int size_u = 0;
			int size_c = 0;
			//  : 3456,(wwww)
			//  , : 7,(a)
			while (index_u < maxIdxU && index_c < maxIdxC) {
				int idxU = find(index_u, maxIdxU, list[index_c]);
				// проверим обратную ситуацию верхний найдём в текущей строке
				int idxC = find(index_c, maxIdxC, list[index_u]);
				if (idxU < 0)  idxU = index_u;
				if (idxC < 0)  idxC = index_c;
				int dc = (idxC - index_c);
				int du = (idxU - index_u);
				size_u = range_size(index_u, idxU);
				size_c = range_size(index_c, idxC);
				if (du == 0 && dc == 0) {
					// элементы совпадают и их можно выровнять
					// определим в какой строке это будем делать
					list[index_c].setParent(index_u);
				}
				else {
					// 4021196,'fffff',(
					// (4155,'aaaa'
					if (iscompact && du==0) {
						list[idxC].setParent(idxU);
					} else {
						if (size_u > size_c) { // фиксируем верхний 
							// будем нижний подгонять под верхний
							size_u = range_size(index_u);
							idxU = index_u;
						}
						else
							if (size_u < size_c) {
								size_c = range_size(index_c);
								idxC = index_c;
							}
						list[idxC].setParent(idxU);
					}
				}
				if (size_u < size_c) {
					// вырхний короче, его дополняем
					list[index_u].add_space(Item::align::LEFT, size_c - size_u);
					Resize(index_u, list[index_u].getMaxSize());
					idxU = idxU + 1;
					idxC++;

				}
				if (size_u > size_c) {
					// 
					list[index_c].add_space(Item::align::LEFT, size_u - size_c);
					Resize(index_c, list[index_c].getMaxSize());
					idxC = idxC + 1;
					idxU++;
				}
				if (size_u == size_c) {
					idxC++;
					idxU++;
				}
				index_c = idxC;
				index_u = idxU;
			}
			// 
			if (CHKCFGPARAM(cfg, FIRST_LINE)) {
				if (index_u >= maxIdxU) {
					list[(size_t)index_c - 1].br = true;
					size_t id = (nline.size() - nrow - 1);

					if (nline[id] != index_c)
						nline.insert(nline.end() - nrow - 1, index_c);
					else nrow--;
				}
				else
				{
					// из за коментарией -- мы внесли перевод в неожидааных местах и их нужно учесть когда до них дойдём
					if (nrow == 0) break;
					nrow--;
					cfg = cfg | FIND_UP_LONG_LINE;
				}
			}
		}
	}
	wxString a;
	for (int j = 0; j < nline.size() - 1; j++) {
		int d = 1;
		if (j == nline.size() - 2) d = 0;
		a+=range_print(nline[j], nline[j + 1] - d);
	}
	//range_print(nline[0], nline[1L]-1);
	//range_print(nline[l], nline[l + 1L]-1);
	return a;
}

void AlignWrap::Resize(int idx,int newSize) {
	int pr = idx;
	while (pr>=0) {
	Item &i=list[pr];
	int oldsz = i.getMaxSize();
	pr = i.getParent();
	i.setMaxSize(newSize);
	//if (pr>=0) Resize(pr,newSize);
	}
}


int AlignWrap::range_size(int s) {
	int se = 0;
	se = list[s].getMaxSize();
	return se;
}
int AlignWrap::range_size(int s, int e) {
	int se = 0;
	for (int j = s; j <= e; j++) {
		se += list[j].getMaxSize();
		}
	return se;
}
wxString AlignWrap::range_print(int s, int e) {
	wxString ss = "";
	for (int j = s; j <= e; j++) {
		ss = ss+list[j].print();
		
	}
	ss += lnsep;
	//std::cout << ss;
	return ss;
}

int AlignWrap::find(int s,int e, Item &k) {

	std::vector<Item>::iterator it;
	if (e >= list.size()) e = list.size() - 1;
	for (int j = s; j <= e; j++) {
		if (list[j] == k) {
			return j;
		}
	}
		return -1;
}
Item AlignWrap::parseItem(int& pos, bool& breakline) {

	wxChar c;
	wxChar c2;
	int p = pos;
	int len = str.Length();
	int l = 0;
	int sp = 0;
	int spl = 0;
	int spbe = 0;
	int type;
	int comment = 0;
	while (true) {
		spl = chkspace(p, breakline);
		if (breakline) {
			// empty line ignore
			breakline = false;
			spl = 0;
			continue;
		}
		pos = p;
		break;
	}
	if (p >= len) {
		Item i("", 0);
		return i;
	}
	c = str[p];
	if (c == '\'' || c == '"') {
		p++;
		
		while (p < len)
		{
			c2 = str[p];
			// end line and unclose literal
			if ((c2 == '\n' || c2 == '\r') /* && CHKCFGPARAM(cfg, ALL_LINES) */ ) {
				break;
			}
			if (c2 == c) {
				if (p + 1 < len)
					if (str[p+1] == c) { p = p + 2; continue; }
				p++;
				break;
			}
			p++;
		}
		l = p - pos;
		type = Item::type::LITERAL;
		goto theend;
		
	}
	if ((p + 1) < len) c2 = str[p + 1];

	if (wxIsalpha(c)) {
		// WORD
		p++;
		while (p < len)
		{
			c = str[p];
			if (!(wxIsalnum(c) || c == '_' || c == '$'|| c==':'|| c=='.')) break;
			p++;
		}
		l = p - pos;
		type = Item::type::WORD;
		goto theend;

	}
	if ((c == '-' || wxIsdigit(c)) &&(c2!='-')) {
		p++;
		while (p < len)
		{
			c = str[p];
			if (!(wxIsdigit(c) || c == '.' || c == 'e' || c == 'E')) break;
			p++;
		}
		l = p - pos;
		type = Item::type::NUM;
		goto theend;

	}
	type = Item::type::ANCHOR;
	if (p + 1 < len) {
		p++;
		c2 = str[p];
		p++;
		l = p - pos;
		// 2-х символьные операторы
		if (c == '<' && c2 == '=') goto theend;
		if (c == '>' && c2 == '=') goto theend;
		if (c == '<' && c2 == '>') goto theend;
		if (c == '-' && c2 == '-') {
			int cntNL = 0;
			while (p < len)
			{
				if (str[p] == '\n' || str[p] == '\r') {
					cntNL++;
				}
				else {
					if (cntNL > 0) break;
				}
				p++;

			}
			l = p - pos;
			comment = 1;
			type = Item::type::COMMENT;
			goto theend;
		}
		if (c == '/' && c2 == '*') {
			while (p < (len - 1))
			{
				if (str[p] == '*' || str[p + 1] == '/')
					break;
				p++;
			}
			p=p+2;
			l = p - pos;
			type = Item::type::COMMENT;
			goto theend;
		}
		p = p - 2;
	}
	p++;
	l = p - pos;
	
	goto theend;


theend:
		wxString v = str.substr(pos, l);
		sp = chkspace(p, breakline);
		if (comment == 1) breakline = true;
		Item i(v,type);
		i.rs = sp;
		//i.ls = spl;
		i.br = breakline;
		pos = p;
		return i;
}
int AlignWrap::chkspace(int &pos, bool& br) {
	wxChar c,prev='\0';
	int nl = parserows;
	int l = 0;
	bool endline = false;
	while (pos < str.Length()) {
		c = str[pos++];
		if (c == '\n' || c == '\r') {
			if (c=='\r' && str[pos] == '\n') {
				// windows
				endline = true;
				pos++;
			}
			parserows++;
			if (CHKCFGPARAM(cfg,ALL_LINES)) {
				br = true;
				break;
			}
			if ((CHKCFGPARAM(cfg,FIRST_LINE)) && parserows == 1)
			{
				// first row
				br = true;
				break;
			}
			continue;
		}

		if (c == ' ' || c == '\t') { l++; continue; }
		pos--;
		break;
	}
	// Не будем заменять переводы строк на пробел для ALL_LINES
	if (nl < parserows && !CHKCFGPARAM(cfg,ALL_LINES)) l++;
	return l;
}