/*
 Program WinCaml: Graphical User Interface
 for interactive use of Caml-Light and Ocaml.
 Copyright (C) 2005-2017 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 wx/wxw.h

#pragma once
#include "wx/wx.h"
#include "wx/process.h"
#include "wx/stream.h"
#include "wx/datstrm.h"
#include "wx/toolbar.h"
#include "wx/stc/stc.h"
#include "wx/print.h"
#include "wx/arrstr.h"

#include <fstream>
#include <sstream>

#ifndef __WXMSW__
#include <unistd.h>
#endif
#include <deque>
#include <string>
#ifdef __WXMSW__
#include <windows.h>
#else
#include <signal.h>
#endif

#include "../All/resource.h"
#include "../All/Utils.h"

#define code_escape 27
#define code_return 13
#define idCancel wxID_CANCEL
#define idNo wxID_NO
#define idYes wxID_YES
#define endofline L"\n"

#ifdef __WXMSW__
#define MONOSPACE L"Courier New"
#else
#ifdef __WXMAC__
#define MONOSPACE L"Menlo"
#else
#define MONOSPACE L"DejaVu Sans Mono"
#endif
#endif

#define ARROW wxCURSOR_ARROW
#define EAST_WEST wxCURSOR_SIZEWE
#define SOUTH_NORTH wxCURSOR_SIZENS

typedef struct Rect
{
	long    left;
	long    top;
	long    right;
	long    bottom;
} Rect;

class CMDIFrame;
class CMDIChildFrame;
class CFindDialog;
class CFont;
class CRichEdit;
#ifndef __WXMAC__
class CToolBar;
class CMacApp;
#else
class CFileDropTarget;
#endif

using namespace std;

extern const wstring appName;
extern const wstring version, version1;
extern const bool liveSplit;

extern CMDIFrame* mainFrame;

void aboutMessage(const wstring& msg);
void errorMessage(const wstring& msg);
bool yesnoMessage(const wstring& msg);
int yesnocancelMessage(const wstring& msg);

#ifdef __WXMSW__
wstring applicationDirectoryPath();
#else
string applicationDirectoryPath();
#ifndef __WXMAC__
void signalHandler(int signum);
#endif
#endif

bool createDirectory(const wstring& path);
void closeFindDialog(bool enableMenus = false);
void findDialog();
void findReplaceDialog();
wstring fromBytes(const string& str);
unsigned long rgb(int r, int g, int b);
void setSearchEdit(CRichEdit* edit);

inline wstring redoShortCut()
{
#ifdef __WXMSW__
    return L"Ctrl+Y";
#else
	return L"Shift+Ctrl+Z";
#endif
}
inline wstring undoShortCut()
{
    return L"Ctrl+Z";
}

// class CMDIFrame ---------------------------------------------------------------------------------------------------

class CMDIFrame : public wxMDIParentFrame
{
    friend class CMDIChildFrame;
    friend class CMDIApp;
	friend class CFont;
	friend class CRichEdit;
#ifdef __WXMAC__
    friend class CFileDropTarget;
    friend class CMacApp;
#endif
    
public:
    
	int LeftMargin;
	int TopMargin;
	int RightMargin;
	int BottomMargin;
    
    deque<CMDIChildFrame*> mdiChildren;
    
	void enableMenuItem(int cmd, bool on);
	void setStatus(int index, const wstring& text);
    
	void highlightMenuEvent(wxMenuEvent& event);
    
	wxStatusBar* statusbar;
    
	CMDIChildFrame* getActiveChild();
    
protected:
#ifndef __WXMAC__
    wxTimer* timer;
#endif
	int xDim;
	int yDim;
    
	bool cascade;
	int frameLeft;
	int frameTop;
	int frameWidth;
	int frameHeight;
	deque<wstring*>* droppedFiles;
	bool Maximized;
	bool StatusBarVisible;
	bool ToolbarVisible;
    
	CMDIFrame();
	~CMDIFrame(){}
    
	void checkMenuItem(int cmd, bool on);
	void colorDialog(unsigned long& color);
	void fontDlg(CFont*& font);
    
	bool isIconic();
	void moveWindow(int left, int top, int width, int height);
	virtual void onClose();
	void onFileClose();
	void onCascadeWindow();
	void onTileWindow();
	void onFileExit();
	bool openFileDialog(wstring& fileName);
    
	void openIcon();
	void setActiveChild(CMDIChildFrame *childFrame);
	void setMenuItemText(int item, const wstring& title, bool enabled);
	void printSetUpDialog(int& leftMargin, int& topMargin, int& rightMargin, int& bottomMargin);
	bool saveFileDialog(wstring& title);
	bool saveFormattedDocumentDialog(wstring& title);
	void tabDialog(int& spaceNumPerTab);
	void updateFrame();
    
private:
    void OnQuit(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);
    
	virtual void onCommand(int cmd);
	virtual void onDrop();
    
	virtual void openFile(const wstring& fileName);
	virtual void openFiles(deque<wstring>& fileName);
	virtual bool reviewChanges();
    
    wxMenuItem* windowMenuSeparator;
    wxMenu* windowMenu;
    wxMenuBar* menuBar;
#ifndef __WXMAC__
    CToolBar* toolbar;
#else
    wxToolBar* toolbar;
#endif
	deque<wstring> filePaths;
    
	void activateEvent(wxActivateEvent& event);
    void closeEvent(wxCloseEvent& event);
#ifndef __WXMAC__
	void dropEvent(wxDropFilesEvent& event);
#endif
	void moveEvent(wxMoveEvent& event);
	void resizeEvent(wxSizeEvent& event);
    
	void createMenus();
	void createStatusbar();
	void createToolbar();
	void onOtherInstanceMessage();
	void startTimer();
    
private:
    void onCommandAux(wxCommandEvent& event);
    void onActivate();
#ifndef __WXMAC__
    void onTimerAux(wxTimerEvent& event);
    void onTimer();
#endif
    void updateWindowMenu(wxUpdateUIEvent& event);
    
    DECLARE_EVENT_TABLE()
};

void deleteFile(const wstring& fileName);
bool getConfigFolderPath(wstring& path);
string toBytes(const wstring& wstr);

// class CMDIChildFrame ---------------------------------------------------------------------------------------------------

class CMDIChildFrame : public wxMDIChildFrame
{
	friend class CMDIFrame;
public:
	CRichEdit* textEdit;
	wstring title;
    
	void drawFocusRect(int left, int top, int right, int bottom);
	void getClientSize(int& width, int& height);
	void hide();
	bool isMaximized();
	void setCursor(int cursorId);
	void setWindowModified(bool modified);
	void releaseCapture();
	void setCapture();
	void showMaximized();
    
	virtual void onMouseMove(int x, int y, bool leftButtonDown);
protected:
	CMDIChildFrame(CMDIFrame* mdiFrame, bool maximized);
	~CMDIChildFrame();
	virtual void onClose();
	virtual void onDestroy();
	void setTitle(const wstring& title);
    void showDefault();
	void showNormal();
	void startTimer();
    
private:
    int frameId;
    wxMenuItem* windowMenuItem;
	bool saveMaximized;
	virtual void onCommand(int cmd);
	virtual void onEditCopy();
	virtual void onEditCut();
	virtual void onEditPaste();
	virtual void onEditSelectAll();
	virtual void onFileSaveAs();
	virtual void onLeftButtonDown(int x, int y);
	virtual void onLeftButtonUp();
	virtual void onActivate();
	virtual void onSize();
	virtual bool reviewChange();
    
    void activateEvent(wxActivateEvent&);
	void closeEvent(wxCloseEvent& event);
	void mouseMoveEvent(wxMouseEvent& event);
	void mousePressEvent(wxMouseEvent& event);
	void mouseReleaseEvent(wxMouseEvent& event);
	void resizeEvent(wxSizeEvent& event);
    
	void setDocumentIcon();
    
private:
    void onTimerAux(wxTimerEvent& event);
	virtual void onTimer();
    wxTimer* timer1;
    
    DECLARE_EVENT_TABLE()
};

#include "../All/CHighlighter.h"

// class CRichEdit ---------------------------------------------------------------------------------------------------

class CRichEdit: public wxStyledTextCtrl
{
	friend class CFindDialog;
	friend class CFindReplaceDialog;
public:
	bool selectionChanged;
    wxFont fnt;
	unsigned long *T;
    unsigned int forecolorCount;
	CHighlighter* highlighter;
    size_t commandStart;
    size_t commandEnd;
    bool inBuffer;
    
    CRichEdit(CMDIChildFrame* win);
    ~CRichEdit();
    
    int appendFile(const wstring& fileName, int encoding);
    void copy();
    void cut();
    void focus();
    size_t getCaretPosition();
    void getSelection(size_t& selStart, size_t& selEnd);
    wstring getText();
    wstring getText(size_t start, size_t length);
    size_t getTextLength();
    void hideSelection();
    size_t lineFirstChar(size_t lineIndex);
    size_t lineFromChar(size_t charIndex);
    int loadFile(const wstring& fileName, bool guessEnc, int &preferredEncoding);
	void move(int left, int top, int right, int bottom);
    void paste();
    void printFormattedDialog();
    void replaceAll(const wstring& findWhat, const wstring& replaceWith, int flags = 0);
    void resumeLayout();
    int saveFile(const wstring& pszFileName, int encoding);
    void saveFormattedDocument(const wstring& pszFileName);
    void selectAll();
    void setFont(CFont* font);
    void setReadOnly(bool b = true);
    void setSelectedText(const wstring& s);
    void setTabs(int charSize, int spaceCountPerTab);
    void setText(size_t start, size_t length, const wstring& text, unsigned long color = 0);
    void setTextBackground(size_t a, size_t l, unsigned long backColor);
    virtual void setTextColor(size_t start, size_t length, unsigned long color);
    void setTextDefaultBackground(size_t a, size_t l);
    void setUseTabs(bool IndentWithTabs);
    void setWrapping(bool on);
    void showSelection();
	virtual wstring status();
    void suspendLayout();
	void updateCursor();
	void updateView();
    size_t visibleTextEnd();
    size_t visibleTextOffset();
protected:
	Rect margins;
    
    void beginPrint();
    void endPrint();
	virtual void setSelection(size_t selStart, size_t selEnd);
    virtual void setColors();
private:
	virtual void onChar(int vKeyCode);
	virtual void onEnterKeyDown();
	virtual void onKeyDown();
	virtual void onKeyUp();
	virtual void onLeftButtonUp(bool ctrlDown);
	virtual void onReplace(const wstring& replaceWith);
	virtual void onReplaceAll(const wstring& findWhat, const wstring& replaceWith, unsigned long flags = 0L);
	virtual void onReturnKeyDown();
	virtual void undo();
	virtual void redo();
    
	CMDIChildFrame* childFrame;
    
	void onFindClose();
    
#ifndef __WXMAC__
	void dropEvent(wxDropFilesEvent& event);
#endif
	void keyPressEvent(wxKeyEvent& event);
	void keyReleaseEvent(wxKeyEvent& event);
	void mouseReleaseEvent(wxMouseEvent& event);
    
    DECLARE_EVENT_TABLE()
};

// class CFindDialog ---------------------------------------------------------------------------------------------------

class CFindDialog : public wxDialog
{
public:
    CFindDialog(CMDIFrame* frame);
    ~CFindDialog();
	void init();
    void show(){Show(); hidden = false;}
    void hide(){Hide(); hidden = true;}
    bool isHidden(){return hidden;}
    wxTextCtrl* m_textFind;
private:
    bool hidden;
    wxCheckBox* m_chkWord;
    wxCheckBox* m_chkCase;
    wxRadioBox* m_radioDir;
    void find(wxCommandEvent& WXUNUSED(event));
    void cancel(wxCommandEvent& WXUNUSED(event));
    void close(wxCloseEvent& WXUNUSED(event));
    
    DECLARE_EVENT_TABLE()
};

// class CFindReplaceDialog ---------------------------------------------------------------------------------------------------

class CFindReplaceDialog : public wxDialog
{
public:
    CFindReplaceDialog(CMDIFrame* frame);
    ~CFindReplaceDialog();
    void init();
    void show(){Show(); hidden = false;}
    void hide(){Hide(); hidden = true;}
    bool isHidden(){return hidden;}
    wxTextCtrl* m_textFind;
    wxTextCtrl* m_textRepl;
private:
    bool hidden;
    wxCheckBox* m_chkWord;
    wxCheckBox* m_chkCase;
    wxRadioBox* m_radioDir;
    void find(wxCommandEvent& WXUNUSED(event));
    void replace(wxCommandEvent& WXUNUSED(event));
    void replaceAll(wxCommandEvent& WXUNUSED(event));
    void cancel(wxCommandEvent& WXUNUSED(event));
    void close(wxCloseEvent& WXUNUSED(event));
    
    DECLARE_EVENT_TABLE()
};

// class CFont ---------------------------------------------------------------------------------------------------

class CFont
{
	friend void CMDIFrame::fontDlg(CFont*& font);
	friend void CRichEdit::setFont(CFont* font);
public:
	wstring* fntName;
	int fntSize;
    bool bold;
    bool italic;
    
	CFont(const wstring& fontName, int fontSize, bool fntBold = false, bool fntItalic = false)
    {
        wxFontWeight fontWeight = fntBold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL;
        wxFontStyle fontStyle = fntItalic ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL;
        font = new wxFont(fontSize, wxFONTFAMILY_DEFAULT, fontStyle, fontWeight, false, fontName);
        fntName = new wstring(fontName);
        fntSize = fontSize;
        bold = fntBold;
        italic = fntItalic;
    }
	~CFont()
    {
        delete font;
        font = NULL;
        delete fntName;
        fntName = NULL;
    }
private:
	wxFont* font;
};

// class CProcess ---------------------------------------------------------------------------------------------------

#ifndef __WXMSW__
class CProcess
{
    wxProcess* p;
public:
	CProcess()
	{
	}
    void start(const string& commandLine)
    {
        p = new wxProcess();
        p->Redirect();
        wxExecute(wxString(commandLine), wxEXEC_ASYNC, p, NULL);
    }
    bool interrupt()
    {
        if (isRunning())
        {
            ::kill((int)p->GetPid(), SIGINT);
#ifndef __WXMAC__
            write("\n");
#endif
        }
        return true;
    }
    bool isRunning()
    {
        return p && wxProcess::Exists((int)p->GetPid());
    }
    string readAllStandardOutput()
    {
        if (!p) return "";
        string s;
        char buffer[1024];
        wxInputStream* is1 = p->GetErrorStream();
        wxInputStream* is2 = p->GetInputStream();
        if (!is1 || !is2) return "";
        while (is1->CanRead()) {
            buffer[is1->Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = '\0';
            s += (char*)buffer;
        }
        while (is2->CanRead()) {
            buffer[is2->Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = '\0';
            s += (char*)buffer;
        }
        return s;
    }
    void stop()
    {
        interrupt();
        write("exit 0;;\n");
        ::usleep(100);
        if (isRunning()) {
            wxProcess::Kill((int)p->GetPid());
            p = NULL;
        }
    }
    void write(const char* s)
    {
        if (isRunning())
        {
            wxOutputStream* tos = p->GetOutputStream();
            tos->Write(s, strlen(s));
        }
    }
};
#else
#define BUFFERSIZE 32768
class CProcess
{
public:
    CProcess()
    {
        ::memset(&pi,0,sizeof(PROCESS_INFORMATION));
        ::memset(&startInfo,0,sizeof(STARTUPINFO));
        exitCode = 0;
        buffer = new char[BUFFERSIZE];
    }
    bool interrupt()
    {
        if (!isRunning()) return true;
        if (::GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pi.dwProcessId))
        {
            Sleep(100);
            writeToPipe("\015\012");
            return true;
        }
        return false;
    }
    bool isRunning()
    {
        if (pi.hProcess) ::GetExitCodeProcess(pi.hProcess, &exitCode);
        else exitCode = 0;
        return exitCode == STILL_ACTIVE;
    }
    string read()
    {
        int count;
        while ((count = readFromPipe(buffer, BUFFERSIZE - 1))== 0);
        buffer[count] = 0;
        string st(buffer);
        return st;
    }
    string readAllStandardOutput()
    {
        bA.erase();
        for(;;)
        {
            DWORD count = readFromPipe(buffer, BUFFERSIZE - 1);
            if (count == 0) break;
            buffer[count] ='\0';
            bA.append(buffer);
        }
        return bA;
    }
    bool start(const wstring& commandLine)
    {
        ::memset(&pi,0,sizeof(PROCESS_INFORMATION));
        ::memset(&startInfo,0,sizeof(STARTUPINFO));
        exitCode = 0;
        
        SECURITY_ATTRIBUTES saAttr;
        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
        saAttr.bInheritHandle = TRUE;
        saAttr.lpSecurityDescriptor = NULL;
        
        if (! ::CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 8192))
            return false;
        
        if (! ::CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 8192))
            return false;
        
        startInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
        startInfo.wShowWindow = SW_SHOW;
        startInfo.hStdOutput = hStdoutWr;
        startInfo.hStdError = hStdoutWr;
        startInfo.hStdInput = hStdinRd;
        
        wstring appDirPath = applicationDirectoryPath();
        
        return ::CreateProcess(NULL,LPWSTR(commandLine.c_str()),NULL,NULL,TRUE,
                               CREATE_NEW_PROCESS_GROUP|NORMAL_PRIORITY_CLASS,NULL,LPWSTR(appDirPath.c_str()),&startInfo, &pi
                               ) ? true : false;
    }
	void stop()
    {
        if (pi.hProcess)
        {
            ::GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pi.dwProcessId);
            Sleep(100);
            writeToPipe("\015\012");
            Sleep(100);
            ::TerminateProcess(pi.hProcess, 0);
            ::memset(&pi,0,sizeof(PROCESS_INFORMATION));
            ::memset(&startInfo,0,sizeof(STARTUPINFO));
            exitCode = 0;
        }
    }
    void write(const char *data)
    {
        writeToPipe(data);
    }
private:
	char* buffer;
	string bA;
	HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr;
	PROCESS_INFORMATION pi;
	STARTUPINFO startInfo;
	DWORD exitCode;
    
	int readFromPipe(char *data,int len)
	{
		DWORD count;
		::PeekNamedPipe(hStdoutRd,data,len,NULL,&count,NULL);
		if (count == 0)
			return 0;
		if (! ::ReadFile(hStdoutRd, data, len, &count, NULL) || count == 0)
			return 0;
		return (int)count;
	}
	int writeToPipe(const char *data)
	{
		DWORD count;
		if (! ::WriteFile(hStdinWr, data, (DWORD)strlen(data), &count, NULL))
			return 0;
		return (int)count;
	}
};
#endif

// class CPrintout ---------------------------------------------------------------------------------------------------

extern wxPageSetupDialogData* g_pageSetupData;
extern wxPrintData* g_printData;
string fromUnicode(const wxString& wstr, int code);
wxString toUnicode(const string& str, int code);

class CPrintout: public wxPrintout
{
    CRichEdit* editor;
#ifdef __WXGTK__
    bool hasPageInfo;
    int pageCount;
#endif
    int left;
    int top;
    int right;
    int bottom;
    
    int xPos;
    int yPos;
    size_t tokenIndex;
    int lineHeight;
    string text;
    wxString s;
    string ss;
	deque<tokenClass*>* tokens;
    wxDC * dc;
    unsigned long color;
    
    virtual bool HasPage(int)
    {
        return tokenIndex < tokens->size();
    }
    
public:
    void OnPreparePrinting()
    {
        dc = GetDC();
        if (!dc) return;
        
        PrintScaling (dc);
        
        dc->SetFont(editor->fnt);
        wxFontMetrics fm = dc->GetFontMetrics();
        lineHeight = fm.height;
        text = fromUnicode(editor->GetText(), ENCODING_UTF8);
        tokens = editor->highlighter->getTokenArray();
        
        // get print page informations and convert to printer pixels
        wxSize ppiScr;
        GetPPIScreen (&ppiScr.x, &ppiScr.y);
        wxSize page = g_pageSetupData->GetPaperSize();
        wxPrintOrientation orientation = g_printData->GetOrientation();
        if (orientation == (wxPrintOrientation)wxPORTRAIT)
        {
            page.x = static_cast<int> (page.x * ppiScr.x / 25.4);
            page.y = static_cast<int> (page.y * ppiScr.y / 25.4);
        }
        else
        {
            int t = page.x;
            page.x = static_cast<int> (page.y * ppiScr.y / 25.4);
            page.y = static_cast<int> (t * ppiScr.x / 25.4);
            
        }
        
		left = mainFrame->LeftMargin;
		top = mainFrame->TopMargin;
		right = mainFrame->RightMargin;
		bottom = mainFrame->BottomMargin;
        
        top = static_cast<int> (top * ppiScr.y / 25.4);
        bottom = static_cast<int> (bottom * ppiScr.y / 25.4);
        left = static_cast<int> (left * ppiScr.x / 25.4);
        right = static_cast<int> (right * ppiScr.x / 25.4);
        
        right = page.x - right;
        bottom = page.y - bottom;
        
        xPos = left;
        yPos = top;
        tokenIndex = 0;
        
        tokenClass* tk = tokens->at(tokenIndex++);
        ss = text.substr(tk->getTokenBegin(), tk->getTokenEnd() - tk->getTokenBegin());
        s = toUnicode(ss, ENCODING_UTF8);
        color = editor->T[tk->getTokenColor()];
#ifdef __WXGTK__ // Get page count
        hasPageInfo = false;
        pageCount = 0;
        while (tokenIndex < tokens->size())
        {
            while (yPos + lineHeight <= bottom && s != "")
            {
                if (s == "\n")
                {
                    xPos = left;
                    yPos += lineHeight;
                    s = "";
                }
                else {
                    int x;
                    int y;
                    dc->GetTextExtent(s, &x, &y);
                    if (x + left > right)
                    {
                        if (xPos > left)
                        {
                            xPos = left;
                            yPos += lineHeight;
                        }
                        int x1 = 0;
                        int y1;
                        int i = 1;
                        while (x1 + xPos < right)
                        {
                            dc->GetTextExtent(s.Mid(0, i), &x1, &y1);
                            i++;
                        }
                        i--;
                        xPos = left;
                        yPos += lineHeight;
                        s = s.Mid(i);
                    }
                    else if (xPos + x < right)
                    {
                        xPos += x;
                        s = "";
                    }
                    else
                    {
                        xPos = left;
                        yPos += lineHeight;
                    }
                }
                if (s == "" && tokenIndex < tokens->size())
                {
                    tokenClass* tk = tokens->at(tokenIndex++);
                    ss = text.substr(tk->getTokenBegin(), tk->getTokenEnd() - tk->getTokenBegin());
                    s = toUnicode(ss, ENCODING_UTF8);
                }
            }
            
            xPos = left;
            yPos = top;
            pageCount++;
        }
        xPos = left;
        yPos = top;
        tokenIndex = 0;
        tk = tokens->at(tokenIndex++);
        ss = text.substr(tk->getTokenBegin(), tk->getTokenEnd() - tk->getTokenBegin());
        s = toUnicode(ss, ENCODING_UTF8);
#endif
    }
    
private:
    bool OnPrintPage(int)
    {
        if (tokenIndex >= tokens->size()) return false;
#ifndef __WXGTK__
        dc->SetFont(editor->fnt);
        dc->SetTextForeground(wxColour(color));
#endif
        while (yPos + lineHeight <= bottom && s != "")
        {
            if (s == "\n") {
                xPos = left;
                yPos += lineHeight;
                s = "";
            }
            else {
                int x;
                int y;
                dc->GetTextExtent(s, &x, &y);
                if (x + left > right)
                {
                    if (xPos > left)
                    {
                        xPos = left;
                        yPos += lineHeight;
                    }
                    int x1 = 0;
                    int y1;
                    int i = 1;
                    while (x1 + xPos < right)
                    {
                        dc->GetTextExtent(s.Mid(0, i), &x1, &y1);
                        i++;
                    }
                    i--;
                    dc->DrawText(s.Mid(0, i), xPos, yPos);
                    xPos = left;
                    yPos += lineHeight;
                    s = s.Mid(i);
                }
                else if (xPos + x < right)
                {
                    dc->DrawText(s, xPos, yPos);
                    xPos += x;
                    s = "";
                }
                else
                {
                    xPos = left;
                    yPos += lineHeight;
                }
            }
            if (s == "" && tokenIndex < tokens->size())
            {
                tokenClass* tk = tokens->at(tokenIndex++);
                ss = text.substr(tk->getTokenBegin(), tk->getTokenEnd() - tk->getTokenBegin());
                s = toUnicode(ss, ENCODING_UTF8);
                color = editor->T[tk->getTokenColor()];
                dc->SetTextForeground(wxColour(color));
            }
        }
        xPos = left;
        yPos = top;
        return true;
    }
    
#ifdef __WXGTK__
    void GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
    {
        if (hasPageInfo)
        {
            wxPrintout::GetPageInfo(minPage, maxPage, selPageFrom, selPageTo);
        }
        else
        {
            *minPage = *selPageFrom = 1;
            *maxPage = *selPageTo = pageCount;
            hasPageInfo = true;
        }
    }
#endif
    
    bool PrintScaling (wxDC *dc){
        
        // check for dc, return if none
        if (!dc) return false;
        
        // get printer and screen sizing values
        wxSize ppiScr;
        GetPPIScreen (&ppiScr.x, &ppiScr.y);
        if (ppiScr.x == 0)
        { // most possible guess 96 dpi
            ppiScr.x = 96;
            ppiScr.y = 96;
        }
        wxSize ppiPrt;
        GetPPIPrinter (&ppiPrt.x, &ppiPrt.y);
        if (ppiPrt.x == 0)
        { // scaling factor to 1
            ppiPrt.x = ppiScr.x;
            ppiPrt.y = ppiScr.y;
        }
        wxSize dcSize = dc->GetSize();
        wxSize pageSize;
        GetPageSizePixels (&pageSize.x, &pageSize.y);
        
        // set user scale
        float scale_x = (float)(ppiPrt.x * dcSize.x) /
        (float)(ppiScr.x * pageSize.x);
        float scale_y = (float)(ppiPrt.y * dcSize.y) /
        (float)(ppiScr.y * pageSize.y);
        dc->SetUserScale (scale_x, scale_y);
        
        return true;
    }
public:
    CPrintout(CRichEdit* re, const wxChar* title = wxT("")): wxPrintout(title) {editor = re;}
    ~CPrintout(){}
};

// class CToolBar ---------------------------------------------------------------------------------------------------

#ifndef __WXMAC__
class CToolBar: public wxToolBar
{
public:
	CToolBar(wxWindow* parent): wxToolBar(parent, 1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_FLAT){}
	void onToolEnter(wxCommandEvent& event);
	DECLARE_EVENT_TABLE()
};
#endif

// class CFileDropTarget ---------------------------------------------------------------------------------------------------

#ifdef __WXMAC__
class CFileDropTarget: public wxFileDropTarget
{
public:
    CFileDropTarget(){}
    bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& fileNames);
};
#endif

// class CMacApp ---------------------------------------------------------------------------------------------------
#ifdef __WXMAC__
class CMacApp: public wxApp
{
    void MacOpenFile(const wxString& fileName)
    {
        mainFrame->openFile(fileName.ToStdWstring());
        if (mainFrame->isIconic())
        {
            mainFrame->openIcon();
        }
    }
};
#endif

// class CMDIApp ---------------------------------------------------------------------------------------------------

class CMDIApp
{
public:
#ifdef __WXMSW__
	CMDIApp()
	{
		mdiApp = new wxApp();
		wxApp::SetInstance(mdiApp);
		init = new wxInitializer();
		argc = 0;
		argv = NULL;
		ini =false;
		wstring path;
		getConfigFolderPath(path);
		createDirectory(path);
		alreadyExists = appExists();
	}
#else
#ifdef __WXMAC__
	CMDIApp(int argc, char* argv[])
	{
		mdiApp = new CMacApp();
		wxApp::SetInstance(mdiApp);
		init = new wxInitializer(argc, argv);
		wstring path;
		getConfigFolderPath(path);
		createDirectory(path);
    }
#else
	CMDIApp(int argc, char* argv[])
	{
		mdiApp = new wxApp();
		wxApp::SetInstance(mdiApp);
		init = new wxInitializer(argc, argv);
		this->argc = argc;
		this->argv = argv;
		ini = false;
		wstring path;
		getConfigFolderPath(path);
		createDirectory(path);
		alreadyExists = appExists();
	}
#endif
#endif
    
#ifndef __WXMAC__
	bool alreadyExists;
	int argc;
#ifdef __WXMSW__
	LPWSTR* argv;
#else
	char** argv;
#endif
    
	bool appExists()
	{
		bool alreadyExists = false;
#ifdef __WXMSW__
		argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
#endif
		ini = (argc == 2 && wxString(argv[1]) == "init");
		wstring path;
		if (getConfigFolderPath(path))
		{
			fstream fs((toBytes(path) + "lockfile").c_str(), fstream::in);
			if (!fs.fail())
			{
				fs.close();
				alreadyExists = true;
				if (ini)
				{
					deleteFile((path + L"lockfile").c_str());
					alreadyExists = false;
				}
			}
			else
			{
				fstream fs1((toBytes(path) + "lockfile").c_str(), fstream::out);
				if (!fs1.fail()) fs1.close();
				alreadyExists = false;
			}
		}
		if (!alreadyExists)
		{
			if (ini)
			{
				wxMessageDialog messageDialog(NULL, L"R\u00E9tablir les r\u00E9glages d'origine ?", "WinCaml", wxYES_NO | wxCENTER);
                messageDialog.SetYesNoLabels("Oui", "Non");
				if (messageDialog.ShowModal() & wxYES)
				{
					wstring path;
					if (getConfigFolderPath(path))
					{
						path +=  appName + version + L".txt";
						deleteFile(path.c_str());
					}
				}
				goto fin;
			}
		}
		else
		{
			fstream fs((toBytes(path) + "0").c_str(), fstream::out);
			if (!fs.fail())
			{
				for (int i = 1; i < argc; i++)
				{
                    
#ifdef __WXMSW__
					wstring s(argv[i]);
					string s1;
					wstring::iterator it;
					for (it=s.begin(); it < s.end(); it++)
					{
						s1 += *it;
					}
					fs << s1.c_str() << endl;
#else
                    
					fs << argv[i] << endl;
#endif
				}
				fs.close();
				while (!wxRename(path + L"0", path + L"1"))
					;
			}
		}
    fin:
		return alreadyExists;
	}
#endif
    
	int run(CMDIFrame* cFrame)
	{
		int res;
		if (init->IsOk())
		{
#ifdef __WXMSW__
			::AllocConsole();
			::ShowWindow(::GetConsoleWindow(), SW_HIDE);
			if (!ini && argv)
			{
				for (int i = 1; i < argc; i++)
				{
					cFrame->openFile(argv[i]);
				}
			}
#else
#ifdef __WXMAC__
			cFrame->SetSize(wxSize(800, 75));
#else
			if (!ini)
			{
				for (int i = 1; i < argc; i++)
				{
					cFrame->openFile(fromBytes(argv[i]));
				}
			}
#endif
#endif
			cFrame->Show();
			mdiApp->OnRun();
			res = 0;
		}
		else
		{
			res = 1;
		}
#ifndef __WXMAC__
		wstring path;
		if (getConfigFolderPath(path))
		{
			deleteFile(path + L"lockfile");
		}
#endif
		return res;
	}
    
	~CMDIApp()
	{
#ifdef __WXMSW__
		LocalFree(argv);
#endif
	}
    
private:
	wxInitializer* init;
#ifdef __WXMAC__
    CMacApp* mdiApp;
#else
	bool ini;
	wxApp* mdiApp;
#endif
};

// class CSplitterBase ---------------------------------------------------------------------------------------------------

#ifdef __WXGTK__
#define MINSIZE     4
#define HALFBARWIDTH   4
#else
#define MINSIZE     2
#define HALFBARWIDTH   2
#endif

class CSplitterBase: public wxEvtHandler
{
#ifdef __WXGTK__
    wxWindow* sash;
    int xsash;
    int ysash;
    void setCursor(wxMouseEvent&)
    {
        sash->SetCursor(verticalBar ? EAST_WEST : SOUTH_NORTH);
    }
    
    void onMouseMoveEvent(wxMouseEvent& event)
    {
        childFrame->onMouseMove(xsash + event.m_x, ysash + event.m_y, event.m_leftDown);
    }
    
    void onLeftButtonDown(wxMouseEvent& event)
    {
        onLeftButtonDown(xsash + event.m_x, ysash + event.m_y);
    }
    
    void onLeftButtonUp(wxMouseEvent&)
    {
        onLeftButtonUp();
    }
    
    
    virtual void onLeftButtonDown(int, int)
    {
        
    }
    
    virtual void onLeftButtonUp()
    {
        
    }
    
protected:
    CSplitterBase(CMDIChildFrame* cfr)
    {
        sash = new wxWindow((wxWindow*)cfr, wxID_ANY);
        sash->Bind(wxEVT_ENTER_WINDOW, &CSplitterBase::setCursor, this);
        sash->Bind(wxEVT_MOTION, &CSplitterBase::onMouseMoveEvent, this);
        sash->Bind(wxEVT_LEFT_DOWN, &CSplitterBase::onLeftButtonDown, this);
        sash->Bind(wxEVT_LEFT_UP, &CSplitterBase::onLeftButtonUp, this);
    }
    
    ~CSplitterBase()
    {
        delete sash;
    }
    
    void moveVerticalSash(int xSplit)
    {
        xsash = xSplit - HALFBARWIDTH;
        ysash = 0;
        sash->SetSize(xsash, 0, 2 * HALFBARWIDTH, height);
    }
    
    void moveHorizontalSash(int ySplit)
    {
        xsash = 0;
        ysash = ySplit - HALFBARWIDTH;
        sash->SetSize(0, ysash, width, 2 * HALFBARWIDTH);
    }
#else
protected:
    CSplitterBase(CMDIChildFrame*)
    {
    }
    
    void moveVerticalSash(int)
    {
    }
    
    void moveHorizontalSash(int)
    {
    }
#endif
    
    CMDIChildFrame* childFrame;
    bool verticalBar;
    int height;
    int width;
};

