/*
Program WinCaml: Graphical User Interface
for interactive use of Caml-Light and Ocaml.
Copyright (C) 2005-2015 Jean Mouric 35700 Rennes France
email: jean.mouric@orange.fr

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. 
*/

// File CUndoManager.cpp

#include "CChildFrame.h"
#include "CInput.h"
#include "CUndoManager.h"

CUndoManager::CUndoManager(CInput* te)
{
	textEdit = te;
	v = new deque<CUndoRecord*>;
	i = 0;
	busy = true;
	type = UR_NONE;
	newType = true;
}
void CUndoManager::add(size_t offs, wstring replacingString, wstring replacedString)
{
	if (busy) return;
	CUndoRecord* ur;
	if (newType)
	{
		newType = false;

		while (v->size() > i) {
			v->pop_back();
		}
		i++;

		ur = new CUndoRecord(type, offs, replacingString, replacedString);
		ur->selStart1 = selStart;
		ur->selEnd1 = selEnd;
		v->push_back(ur);

		textEdit->cChildFrame->updateUndoMenuTitle();
		textEdit->cChildFrame->updateRedoMenuTitle();
	}
	else
	{
		merge(offs, replacingString, replacedString);
		ur = v->at(i - 1);
	}
	textEdit->getSelection(ur->selStart2, ur->selEnd2);
}
void CUndoManager::merge(size_t offs, wstring replacingString, wstring replacedString)
{
	CUndoRecord* ur = v->at(i - 1);
	size_t a = ur->offs > offs ? offs : ur->offs;
	size_t q = textEdit->getTextLength() - a;
	wstring s = textEdit->getText().substr(a);
	wstring sb(s);
	sb.replace(offs - a, replacingString.length(), replacedString);
	sb.replace(ur->offs - a, ur->replacingString.length(), ur->replacedString);

	size_t p = sb.length();
	while (p > 0  && q > 0 && sb.at(--p) == s.at(--q)){}
	if (p < sb.length() && sb.at(p) != s.at(q)) {p++; q++;}

	ur->offs = a;
	ur->replacedString = sb.substr(0, p);
	ur->replacingString = s.substr(0, q);
	ur->selStart1 = ur->offs;
	ur->selEnd1 = ur->offs + p;
}
void CUndoManager::undo()
{
	if (i == 0) return;

	busy = true;
	CUndoRecord* ur = (*v)[--i];
	setType(ur->type);
	textEdit->replace(ur->offs, ur->replacingString.length(), ur->replacedString);
	textEdit->setSelection(ur->selStart1, ur->selEnd1);
	busy = false;

	textEdit->cChildFrame->updateUndoMenuTitle();
	textEdit->cChildFrame->updateRedoMenuTitle();

	setType(UR_TYPING);
}
void CUndoManager::redo()
{
	if (i >= v->size()) return;

	busy = true;
	CUndoRecord* ur = v->at(i++);
	setType(ur->type);
	textEdit->replace(ur->offs, ur->replacedString.length(), ur->replacingString);
	textEdit->setSelection(ur->selStart2, ur->selEnd2);
	busy = false;

	textEdit->cChildFrame->updateUndoMenuTitle();
	textEdit->cChildFrame->updateRedoMenuTitle();

	setType(UR_TYPING);
}
int CUndoManager::getUndoType()
{
	if (i > 0) return (v->at(i - 1))->type;
	return UR_NONE;
}
int CUndoManager::getRedoType()
{
	if (i < v->size()) return (v->at(i))->type;
	return UR_NONE;
}
void CUndoManager::setType(int t)
{
	type = t;
	newType = true;
	textEdit->getSelection(selStart, selEnd);
}
void CUndoManager::setSelection()
{
	textEdit->getSelection(selStart, selEnd);
}
int CUndoManager::getType()
{
	return type;
}
void CUndoManager::stop()
{
	v->clear();
	i = 0;
	busy = true;
	type = UR_NONE;
	newType = true;
}
void CUndoManager::start()
{
	busy = false;
	type = UR_TYPING;
}
CUndoManager::~CUndoManager()
{
	v->clear();
	delete v;
}
