/*
 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 COutput.cpp

#include "CChildFrame.h"
#include "CFrame.h"
#include "COutput.h"

#include <deque>
#include <fstream>
#include <sstream>

void caretCoordinates(const wstring& text, size_t caretPosition, size_t SpaceCountPerTab, size_t& line, size_t& column);
string toLatin1(const wstring& wstr);
wstring fromLatin1(const string& str);

COutput::COutput(CChildFrame* cfr): CRichEdit(cfr)
{
	cChildFrame = cfr;
	setReadOnly();
	waitForOutput = true;
    forecolorCount = 3;
    T = new unsigned long[forecolorCount];
    setColors();
	nlCount = 0;
	setFont(cChildFrame->font2);
	setTabs(cChildFrame->OutputFontSize, cChildFrame->SpaceCountPerTab);
}
void COutput::reset()
{
	waitForOutput = true;
	nlCount = 0;
}
void COutput::handleCommand(CProcess* process)
{
	if (waitForOutput || process == NULL) return;
	deque<wstring>* commandArray = cChildFrame->commandArray;
	deque<CommandInfo>* commandInfoArray = cChildFrame->commandInfoArray;
	if (commandArray->size() <= cChildFrame->currentCommand) return;
	wstring str1 = getText();
	trim(str1);
	wstring command =  (*commandArray)[cChildFrame->currentCommand];
	size_t length = command.length();
	if ( str1.length() > 0 && str1.substr(str1.length() - 1) == L"#")
	{
		wstring s = command;
		trim(s);
		nlCount = 0;
		if (cChildFrame->CamlTop == OCAML)
			for (size_t cc = 0; cc < length; cc++)
			{
				if (command[cc] == L'\n')
					nlCount +=2;
			}
        wstring sub_str = command.substr(length - 3);
        if (length > 2 && command.substr(length - 3) == L";;\n")
            switch(cChildFrame->OutputMode)
        {
            case OUTPUT_LONG:
            {
                wstring s1 = s;
                size_t l = getTextLength();
                setText(l, 0, s1 + L"\n", cChildFrame->OutputCommandForeground);
                CommandInfo ci;
                ci.start = l;
                ci.expanded = true;
                commandInfoArray->push_back(ci);
                break;
            }
            case OUTPUT_MEDIUM:
            {
                int nl = 1;
                wstring s1;
                for (size_t cc1 = 0; cc1 < s.length(); cc1++)
                {
                    if (s[cc1] == L'\n')
                        nl++;
                }
                if (nl > 1)
                {
                    size_t cc2;
                    for (cc2 = 1; cc2 < s.length() && s[cc2] != L'\n'; cc2++){}
                    s1 = s.substr(0, cc2);
                }
                else
                {
                    s1 = s;
                }
                size_t l = getTextLength();
                CommandInfo ci;
                ci.start = l;
                ci.expanded = false;
                commandInfoArray->push_back(ci);
                setText(l, 0, s1 + L"\n", cChildFrame->OutputCommandForeground);
                break;
            }
            default:
            {
                CommandInfo ci;
                ci.start = getTextLength();
                ci.expanded = false;
                commandInfoArray->push_back(ci);
                waitForOutput = true;
                break;
            }
        }
        cChildFrame->currentCommand++;
		string ws = toLatin1(command);
        removeTabs(ws);
        process->write(ws.c_str());
	}
}
void COutput::listen(CProcess* process)
{
	if (!process)
	{
		return;
	}
	size_t n = 0;
	string byteArray = process->readAllStandardOutput();
	if ((n = byteArray.length()) != 0)
	{
		size_t k;
		for (k = 0; k < n && nlCount > 0 && byteArray[k] == ' '; k++, nlCount--){}
		if (k != n)
		{
			waitForOutput = false;
			size_t a = getTextLength();
			setText(a, 0, fromLatin1(byteArray.substr(k)), 0);
            a = getTextLength();
			setSelection(a, a);
        }
	}
}
void COutput::onKeyDown()
{
	selectionChanged =true;
}
void COutput::onKeyUp()
{
	selectionChanged = true;
}
void COutput::showHide()
{
	deque<wstring>* commandArray = cChildFrame->commandArray;
	deque<CommandInfo>* commandInfoArray = cChildFrame->commandInfoArray;
    
	size_t selStart, selEnd;
	getSelection(selStart, selEnd);
    
	size_t sz = commandArray->size();
	if (sz == 0)
	{
		return;
	}
	size_t a = 0;
	size_t i = 0;
	size_t j;
	for (j = 0; j < sz; j++) {
		CommandInfo* cmdInfo = &(*commandInfoArray)[j];
		a = cmdInfo->start;
		i = j;
		if (a > selStart + 1) {if (i > 0) a = (*commandInfoArray)[--i].start; break;}
	}
	switch (cChildFrame->OutputMode)
	{
        case OUTPUT_MEDIUM: case OUTPUT_LONG:
		{
			wstring command = (*commandArray)[i];
			trim(command);
			size_t n = command.length();
			size_t eol = n;
			for (j = 0; j < n; j++)
			{
				if (command[j] == L'\n')
				{
					eol = j;
					break;
				}
			}
			wstring s = command.substr(0, eol);
			CommandInfo* cmdInfo = &(*commandInfoArray)[i];
			if (cmdInfo->expanded)
			{
				cmdInfo->expanded = false;
				setText(a, n, s, cChildFrame->OutputCommandForeground);
				for (j = ++i; j < sz; j++)
				{
					CommandInfo* ci = &(*commandInfoArray)[j];
					ci->start += eol - n;
				}
			}
			else
			{
				cmdInfo->expanded = true;
				setText(a, eol, command, cChildFrame->OutputCommandForeground);
				for (j = ++i; j < sz; j++)
				{
					CommandInfo* ci = &(*commandInfoArray)[j];
					ci->start += n - eol;
				}
			}
			break;
		}
        default:
		{
			wstring command =(*commandArray)[i];
			trim(command);
			size_t n = command.length();
			CommandInfo* cmdInfo = &(*commandInfoArray)[i];
			if (cmdInfo->expanded)
			{
				cmdInfo->expanded = false;
				setText(a, n + 1, L"", cChildFrame->OutputCommandForeground);
				for (j = ++i; j < sz; j++)
				{
					CommandInfo* ci = &(*commandInfoArray)[j];
					ci->start -= n + 1;
				}
			}
			else
			{
				cmdInfo->expanded = true;
				setText(a, 0, command + L"\n", cChildFrame->OutputCommandForeground);
				for (j = ++i; j < sz; j++)
				{
					CommandInfo* ci = &(*commandInfoArray)[j];
					ci->start += n + 1;
				}
			}
			break;
		}
	}
	selectionChanged = true;
}
void COutput::onLeftButtonUp(bool ctrlDown)
{
	cChildFrame->textEdit = this;
	if (ctrlDown)
		showHide();
	selectionChanged = true;
	setSearchEdit(this);
}
void  COutput::onChar(int keyCode)
{
	if (keyCode == code_escape)
	{
		int mode;
		switch(cChildFrame->OutputMode)
		{
            case OUTPUT_MEDIUM:
                mode = OUTPUT_LONG;
                break;
            case OUTPUT_LONG:
                mode = OUTPUT_SHORT;
                break;
            default:
                mode = OUTPUT_MEDIUM;
                break;
		}
		cChildFrame->cFrame->updateOutputMenu(mode);
		setMode(mode);
		cChildFrame->cFrame->OutputMode = mode;
		selectionChanged = true;
	}
	else if (keyCode == code_return)
	{
		showHide();
		selectionChanged = true;
	}
}
void COutput::setMode(int mode)
{
	deque<wstring>* commandArray = cChildFrame->commandArray;
	deque<CommandInfo>* commandInfoArray = cChildFrame->commandInfoArray;
    
	size_t selStart, selEnd;
	getSelection(selStart, selEnd);
	size_t p;
	for(p = 0; p < commandInfoArray->size() && p < commandArray->size(); p++)
	{
		CommandInfo cmdInfo = (*commandInfoArray)[p];
		if (cmdInfo.start > selStart) break;
	}
	if (p > 0) p--;
    
	size_t d;
	size_t j;
	const size_t m = ((size_t)-1) / 2;
	switch(mode)
	{
        case OUTPUT_LONG:
		{
			d = m;
			for (j = 0; j < commandArray->size() && j < commandInfoArray->size(); j++)
			{
				wstring command = (*commandArray)[j];
				trim(command);
				CommandInfo* cmdInfo = &(*commandInfoArray)[j];
				size_t a =  cmdInfo->start;
				d < m ? a -= m - d : a += d - m;
				cmdInfo->start = a;
				size_t n = command.length();
				size_t eol = n;
				size_t i;
				for (i = 0; i < n; i++)
				{
					if (command[i] == L'\n')
					{
						eol = i;
						break;
					}
				}
				wstring s = command.substr(0, eol);
				if (cChildFrame->OutputMode == OUTPUT_SHORT)
				{
					if (!cmdInfo->expanded)
					{
						setText(a, 0, command + L"\n", cChildFrame->OutputCommandForeground);
						d += n + 1;
						cmdInfo->expanded = true;
					}
				}
				else if (cChildFrame->OutputMode == OUTPUT_MEDIUM)
				{
					if (!cmdInfo->expanded)
					{
						setText(a, eol, command, cChildFrame->OutputCommandForeground);
						d += n - eol;
						cmdInfo->expanded = true;
					}
				}
			}
			break;
		}
        case OUTPUT_MEDIUM:
		{
			d = m;
			for (j = 0; j < commandArray->size() && j < commandInfoArray->size(); j++)
			{
				wstring command = (*commandArray)[j];
				trim(command);
				CommandInfo* cmdInfo = &(*commandInfoArray)[j];
				size_t a = cmdInfo->start;
				d < m ? a += m - d : a -= (d - m);
				cmdInfo->start = a;
				size_t n = command.length();
				size_t eol = n;
				size_t i;
				for (i = 0; i < n; i++)
				{
					if (command[i] == L'\n')
					{
						eol = i;
						break;
					}
				}
				wstring s = command.substr(0, eol);
				if (cChildFrame->OutputMode == OUTPUT_SHORT)
				{
					if (cmdInfo->expanded)
					{
						setText(a, n, s, cChildFrame->OutputCommandForeground);
						d += n - eol;
						cmdInfo->expanded = false;
					}
					else
					{
						setText(a, 0, s + L"\n", cChildFrame->OutputCommandForeground);
						d -= eol + 1;
					}
				}
				else
				{
					if (cmdInfo->expanded)
					{
						setText(a, n, s, cChildFrame->OutputCommandForeground);
						d += n - eol;
						cmdInfo->expanded = false;
					}
				}
			}
			break;
		}
        default:
		{
			d = m;
			for (j = 0; j < commandArray->size() && j < commandInfoArray->size(); j++)
			{
				wstring command = (*commandArray)[j];
				trim(command);
				CommandInfo* cmdInfo = &(*commandInfoArray)[j];
				size_t a = cmdInfo->start;
				d < m ? a += m - d : a -= (d - m);
				cmdInfo->start = a;
				size_t n = command.length();
				size_t eol = n;
				size_t i;
				for (i = 0; i < n; i++)
				{
					if (command[i] == L'\n')
					{
						eol = i;
						break;
					}
				}
				if (cmdInfo->expanded)
				{
					setText(a, n + 1, L"", 0);
					d += n + 1;
				}
				else
				{
					setText(a, eol + 1, L"", 0);
					d += eol + 1;
				}
				cmdInfo->expanded = false;
			}
            
			break;
		}
	}
	if (commandInfoArray->size())
	{
		selStart = selEnd = (*commandInfoArray)[p].start;
		setSelection(selStart, selEnd);
	}
	cChildFrame->OutputMode = mode;
	selectionChanged = true;
}

void COutput::removeTabs(string& str)
{
	size_t i;
	size_t len = str.length();
	bool inString = false;
	for(i = 0; i < len; i++)
	{
		if (str[i] == '"') inString = !inString;
		if (!inString && str[i] == '\n')
		{
			size_t j = i + 1;
			char ch;
			while (j < len && ((ch = str[j]) == '\t' || ch == ' '))
			{
				if (ch == '\t') str[j] = ' ';
				j++;
			}
		}
	}
}

void COutput::clear()
{
	setText(0, getTextLength(), L"");
	cChildFrame->reset(0);
}
void COutput::colorize()
{
    setColors();
	deque<wstring>* commandArray = cChildFrame->commandArray;
	deque<CommandInfo>* commandInfoArray = cChildFrame->commandInfoArray;
	for(size_t i = 0; i < commandInfoArray->size() && i < commandArray->size(); i++)
	{
		CommandInfo* cmdInfo =  &(*commandInfoArray)[i];
		wstring command = (*commandArray)[i];
		trim(command);
		if (cmdInfo->expanded)
		{
			setTextColor(cmdInfo->start, command.length(), cChildFrame->OutputCommandForeground);
		}
		else if (cChildFrame->OutputMode != OUTPUT_SHORT)
		{
			size_t n = command.length();
			size_t eol = n;
			size_t i;
			for (i = 0; i < n; i++)
			{
				if (command[i] == L'\n')
				{
					eol = i;
					break;
				}
			}
			setTextColor(cmdInfo->start, eol, cChildFrame->OutputCommandForeground);
		}
	}
}

wstring COutput::status()
{
	size_t line;
	size_t column;
	caretCoordinates(getText(), getCaretPosition(), cChildFrame->SpaceCountPerTab, line, column);
	wstringstream format(wstringstream::in | wstringstream::out);
	format << L"Sortie:   Ligne " << line << L" Colonne " << column;
	return format.str();
}

void COutput::setColors()
{
    T[0] = 0;
    T[1] = cChildFrame->OutputCommandForeground;
    T[2] = rgb(255, 0, 0);
    CRichEdit::setColors();
}
