//this file is part of notepad++
//Copyright (C)2003 Don HO ( donho@altern.org )
//
//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 2 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, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#ifndef NOTEPAD_PLUS_H
#define NOTEPAD_PLUS_H

#include "Window.h"
#include "ScintillaEditView.h"
#include "ToolBar.h"
#include "ImageListSet.h"
#include "DocTabView.h"

#include "StaticDialog.h"
#include "SplitterContainer.h"
#include "FindReplaceDlg.h"
#include "AboutDlg.h"
#include "RunDlg.h"
#include "UserDefineDialog.h"
#include "StatusBar.h"
#include "Parameters.h"
#include "lastRecentFileList.h"
#include "GoToLineDlg.h"
#include "WordStyleDlg.h"
#include "constant.h"
#include "trayIconControler.h"
#include "ContextMenu.h"
#include "PluginsManager.h"
#include "Notepad_plus_msgs.h"

#define NOTEPAD_PP_CLASS_NAME	"Notepad++"



#define MENU 0x01
#define TOOLBAR 0x02

//#define WM_LOADFILEBYPATH WM_USER

const bool MODE_TRANSFER = true;
const bool MODE_CLONE = false;

const unsigned char DOCK_MASK = 1;
const unsigned char TWO_VIEWS_MASK = 2;

const int MAIN_VIEW = 0;
const int SUB_VIEW = 1;

const int STATUSBAR_DOC_TYPE = 0;
const int STATUSBAR_DOC_SIZE = 1;
const int STATUSBAR_CUR_POS = 2;
const int STATUSBAR_EOF_FORMAT = 3;
const int STATUSBAR_UNICODE_TYPE = 4;
const int STATUSBAR_TYPING_MODE = 5;

const int blockSize = 128 * 1024 + 4;

// pour la fonction changeDlgLang()
const int DLG_FIND = 0;
const int DLG_GOTOLINE = 1;
const int DLG_RUN = 2;

struct iconLocator {
	int listIndex;
	int iconIndex;
	std::string iconLocation;

	iconLocator(int iList, int iIcon, const std::string iconLoc) 
		: listIndex(iList), iconIndex(iIcon), iconLocation(iconLoc){};
};


class Notepad_plus : public Window {
public:
	Notepad_plus();

	void init(HINSTANCE, HWND, const char *cmdLine);

	// ATTENTION : the order of the destruction is very important
	// because if the parent's window hadle is destroyed before
	// the destruction of its childrens' windows handle, 
	// its childrens' windows handle will be destroyed automatically!
	virtual ~Notepad_plus(){
		(NppParameters::getInstance())->destroyInstance();
		if (_pTrayIco)
			delete _pTrayIco;
	};

	void killAllChildren() {
		_toolBar.destroy();
		_rebar.destroy();

        if (_pMainSplitter)
        {
            _pMainSplitter->destroy();
            delete _pMainSplitter;
        }

        _mainDocTab.destroy();
        _subDocTab.destroy();

		_mainEditView.destroy();
        _subEditView.destroy();
		_invisibleEditView.destroy();

        _subSplitter.destroy();
        _statusBar.destroy();

	};

	virtual void destroy() {
		::DestroyWindow(_hSelf);
	};

    static const char * getClassName() {
        return _className;
    };
	
	void setTitleWith(const char *filePath);

	// For filtering the modeless Dialog message
	bool isDlgsMsg(MSG *msg) const {
		for (size_t i = 0; i < _hModelessDlgs.size(); i++)
		{
			if (::IsDialogMessage(_hModelessDlgs[i], msg))
				return true;
		}
/*
		if (_findReplaceDlg.isCreated())
		{
			if (::IsDialogMessage(_findReplaceDlg.getHSelf(), msg))
				return true;
		}
		if (_aboutDlg.isCreated())
		{
			if (::IsDialogMessage(_aboutDlg.getHSelf(), msg))
				return true;
		}

		if (_runDlg.isCreated())
		{
			if (::IsDialogMessage(_runDlg.getHSelf(), msg))
				return true;
		}

		if (_goToLineDlg.isCreated())
		{
			if (::IsDialogMessage(_goToLineDlg.getHSelf(), msg))
				return true;
		}

		if (_goToLineDlg.isCreated())
		{
			if (::IsDialogMessage(_goToLineDlg.getHSelf(), msg))
				return true;
		}

		UserDefineDialog *udd = _pEditView->getUserDefineDlg();
		if (udd->isCreated())
			if (::IsDialogMessage(udd->getHSelf(), msg))
				return true;
*/
		return false;
	};
    bool doOpen(const char *fileName);
	bool doSimpleOpen(const char *fileName);

	void saveScintillaParams(bool whichOne) {
		ScintillaViewParams svp;
		ScintillaEditView *pView = (whichOne == SCIV_PRIMARY)?&_mainEditView:&_subEditView;

		svp._lineNumberMarginShow = pView->hasMarginShowed(ScintillaEditView::_SC_MARGE_LINENUMBER); 
		svp._bookMarkMarginShow = pView->hasMarginShowed(ScintillaEditView::_SC_MARGE_SYBOLE);
		svp._indentGuideLineShow = pView->isShownIndentGuide();
		svp._folderStyle = pView->getFolderStyle();
		svp._currentLineHilitingShow = pView->isCurrentLineHiLiting();
		svp._doWrap = pView->isWrap();
		svp._edgeMode = int(pView->execute(SCI_GETEDGEMODE));
		(NppParameters::getInstance())->writeScintillaParams(svp, whichOne);
	};

	void saveGUIParams(){
		NppGUI & nppGUI = (NppGUI &)(NppParameters::getInstance())->getNppGUI();
		nppGUI._statusBarShow = _statusBar.isVisible();
		nppGUI._toolBarStatus = _toolBar.getState();

		nppGUI._tabStatus = (TabBarPlus::doDragNDropOrNot()?TAB_DRAWTOPBAR:0) | (TabBarPlus::drawTopBar()?TAB_DRAGNDROP:0) | (TabBarPlus::drawInactiveTab()?TAB_DRAWINACTIVETAB:0) | (_toReduceTabBar?TAB_REDUCE:0);
		nppGUI._splitterPos = _subSplitter.isVertical()?POS_VERTICAL:POS_HORIZOTAL;
		UserDefineDialog *udd = _pEditView->getUserDefineDlg();
		bool b = udd->isDocked();
		nppGUI._userDefineDlgStatus = (b?UDD_DOCKED:0) | (udd->isVisible()?UDD_SHOW:0);
		
		// Save the position

		WINDOWPLACEMENT posInfo;

        posInfo.length = sizeof(WINDOWPLACEMENT);
		::GetWindowPlacement(_hSelf, &posInfo);

		nppGUI._appPos.left = posInfo.rcNormalPosition.left;
		nppGUI._appPos.top = posInfo.rcNormalPosition.top;
		nppGUI._appPos.right = posInfo.rcNormalPosition.right - posInfo.rcNormalPosition.left;
		nppGUI._appPos.bottom = posInfo.rcNormalPosition.bottom - posInfo.rcNormalPosition.top;
		nppGUI._isMaximized = (IsZoomed(_hSelf) || (posInfo.flags & WPF_RESTORETOMAXIMIZED));
		(NppParameters::getInstance())->writeGUIParams();
	};
	void saveUserDefineLangs() {
		if (ScintillaEditView::getUserDefineDlg()->isDirty())
			(NppParameters::getInstance())->writeUserDefinedLang();
	};

	void saveShortcuts() {
		if (_isCmdScModified || _isMacrosScModified || _isUserCmdScModified || _isScintillaKeyModified)
			(NppParameters::getInstance())->writeShortcuts(_isCmdScModified, _isMacrosScModified, _isUserCmdScModified, _isScintillaKeyModified);
	};

	void saveSession(const Session & session){
		(NppParameters::getInstance())->writeSession(session);
	};

	void getCurrentOpenedFiles(Session & session) {
		session._actifIndex = _mainEditView.getCurrentDocIndex();
		
		for (size_t i = 0 ; i < _mainEditView.getNbDoc() ; i++)
		{
			const Buffer & buf = _mainEditView.getBufferAt((size_t)i);
			if (PathFileExists(buf._fullPathName))
				session._files.push_back(string(buf._fullPathName));
		}

		for (size_t i = 0 ; i < _subEditView.getNbDoc() ; i++)
		{
			const Buffer & buf = _subEditView.getBufferAt((size_t)i);
			if (PathFileExists(buf._fullPathName))
				session._files.push_back(string(buf._fullPathName));
		}
	};

	void fileSaveSession();
	void fileLoadSession();

	void changeDlgLang(int dlgID);
	void changeConfigLang();
	void changeUserDefineLang();
	void changeMenuLang();
	const char * getNativeTip(int btnID);
	void changeToolBarIcons();

	bool doBlockComment();
	bool doStreamComment();
	void doTrimTrailing() {
		_pEditView->execute(SCI_BEGINUNDOACTION);
		int nbLines = _pEditView->execute(SCI_GETLINECOUNT);
		for (int line = 0 ; line < nbLines ; line++)
		{
			int lineStart = _pEditView->execute(SCI_POSITIONFROMLINE,line);
			int lineEnd = _pEditView->execute(SCI_GETLINEENDPOSITION,line);
			int i = lineEnd - 1;
			char c = (char)_pEditView->execute(SCI_GETCHARAT,i);

			for ( ; (i >= lineStart) && (c == ' ') || (c == '\t') ; c = (char)_pEditView->execute(SCI_GETCHARAT,i))
				i--;

			if (i < (lineEnd - 1))
			{
				_pEditView->execute(SCI_SETTARGETSTART, i + 1);
				_pEditView->execute(SCI_SETTARGETEND, lineEnd);
				_pEditView->execute(SCI_REPLACETARGET, 0, (LPARAM)"");
			}
		}
		_pEditView->execute(SCI_ENDUNDOACTION);
	};

	HACCEL getAccTable() const {
		return _accelerator.getAccTable();
	};

	void addCurrentMacro();

private:
	static const char _className[32];
	char _nppPath[MAX_PATH];
    Window *_pMainWindow;
	TiXmlNode *_nativeLang, *_toolIcons;

    unsigned char _mainWindowStatus;

    DocTabView _mainDocTab;
    DocTabView _subDocTab;
    DocTabView *_pDocTab;

    ScintillaEditView _mainEditView;
    ScintillaEditView _subEditView;
	ScintillaEditView _invisibleEditView;

    ScintillaEditView *_pEditView;

    SplitterContainer *_pMainSplitter;
    SplitterContainer _subSplitter;

    ContextMenu _tabPopupMenu, _tabPopupDropMenu;

	ToolBar	_toolBar;
	IconList _docTabIconList;
    StatusBar _statusBar;
	bool _toReduceTabBar;
	ReBar _rebar;

	// Dialog
	FindReplaceDlg _findReplaceDlg;
	FindIncrementDlg _incrementFindDlg;
    AboutDlg _aboutDlg;
	RunDlg _runDlg;
    GoToLineDlg _goToLineDlg;
	WordStyleDlg _configStyleDlg;

	// a handle list of all the Notepad++ dialogs
	vector<HWND> _hModelessDlgs;

	LastRecentFileList _lastRecentFileList;

	vector<iconLocator> _customIconVect;

	// For FullScreen feature
	bool _isfullScreen;
	RECT _rcWorkArea;
	WINDOWPLACEMENT _winPlace;
	void fullScreenToggle();

	// For restore real window size
	//RECT _normalWindowRect;

	// Keystroke macro recording and playback
	Macro _macro;
	bool _recordingMacro;

	//Synchronized Scolling
	
	struct SyncInfo {
		int _line;
		int _column;
		bool _isSynScollV;
		bool _isSynScollH;
		SyncInfo():_line(0), _column(0), _isSynScollV(false), _isSynScollH(false){};
		bool doSync() const {return (_isSynScollV || _isSynScollH); };
	} _syncInfo;

	bool _isUDDocked;

	trayIconControler *_pTrayIco;
	int _zoomOriginalValue;

	Accelerator _accelerator;

	bool _isCmdScModified;
	bool _isMacrosScModified;
	bool _isUserCmdScModified;
	bool _isScintillaKeyModified;

	PluginsManager _pluginsManager;

	bool _isRTL;

	static LRESULT CALLBACK Notepad_plus_Proc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
	LRESULT runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);

	BOOL notify(SCNotification *notification);
	void command(int id);

	void fileNew(){
		setTitleWith(_pDocTab->newDoc(NULL));

		updateStatusBar();
		dynamicCheckMenuAndTB();
		checkDocState();
		setWorkingDir(NULL);
	};

	void fileOpen();
	bool fileClose();
	bool fileCloseAll();
	bool fileCloseAllButCurrent();

	void hideCurrentView();

	int doSaveOrNot(const char *fn) {
		char phrase[512] = "Save file \"";
		strcat(strcat(phrase, fn), "\" ?");
		return ::MessageBox(_hSelf, phrase, "Save", MB_YESNOCANCEL | MB_ICONQUESTION | MB_APPLMODAL);
	};
	
	int doReloadOrNot(const char *fn) {
		char phrase[512] = "The file \"";
		strcat(strcat(phrase, fn), "\" is modified by another program. Reload this file?");
		return ::MessageBox(_hSelf, phrase, "Reload", MB_YESNO | MB_ICONQUESTION | MB_APPLMODAL);
	};

	int doCloseOrNot(const char *fn) {
		char phrase[512] = "The file \"";
		strcat(strcat(phrase, fn), "\" doesn't exist anymore. Keep this file in editor ?");
		return ::MessageBox(_hSelf, phrase, "Keep non existing file", MB_YESNO | MB_ICONQUESTION | MB_APPLMODAL);
	};
	
	bool fileSave();
	bool fileSaveAll();
	bool fileSaveAs();
	void filePrint(bool showDialog);
	bool doSave(const char *filename, UniMode mode);
	void enableMenu(int cmdID, bool doEnable) const {
		int flag = doEnable?MF_ENABLED | MF_BYCOMMAND:MF_DISABLED | MF_GRAYED | MF_BYCOMMAND;
		::EnableMenuItem(::GetMenu(_hSelf), cmdID, flag);
	}
	void enableCommand(int cmdID, bool doEnable, int which) const;
	void checkClipboard();
	void checkDocState();
	void checkUndoState();
	void checkMacroState();
	void checkSyncState();
	void dropFiles(HDROP hdrop);
	void checkModifiedDocument();
	void reload(const char *fileName);

    void docGotoAnotherEditView(bool mode);
    void dockUserDlg();
    void undockUserDlg();

    void getToolBarClientRect(RECT &rc) const;
    void getMainClientRect(RECT & rc) const;
    void getStatusBarClientRect(RECT & rc) const;

    void switchEditViewTo(int gid);
	
	void dynamicCheckMenuAndTB() const;

	void enableConvertMenuItems(formatType f) const {
		enableCommand(IDM_FORMAT_TODOS, (f != WIN_FORMAT), MENU);
		enableCommand(IDM_FORMAT_TOUNIX, (f != UNIX_FORMAT), MENU);
		enableCommand(IDM_FORMAT_TOMAC, (f != MAC_FORMAT), MENU);
	};

	void checkUnicodeMenuItems(UniMode um) const;

    int getCurrentView() const {
        return (_pEditView == &_mainEditView)?MAIN_VIEW:SUB_VIEW;
    };

	int getNonCurrentView() const {
        return (_pEditView == &_mainEditView)?SUB_VIEW:MAIN_VIEW;
    };

    DocTabView * getNonCurrentDocTab() {
        return (_pDocTab == &_mainDocTab)?&_subDocTab:&_mainDocTab;
    };

    ScintillaEditView * getNonCurrentEditView() {
        return (_pEditView == &_mainEditView)?&_subEditView:&_mainEditView;
    };

    void synchronise();

    void setLangStatus(LangType langType);

	void setDisplayFormat(formatType f) {
		std::string str;
		switch (f)
		{
			case MAC_FORMAT :
				str = "MAC";
				break;
			case UNIX_FORMAT :
				str = "UNIX";
				break;
			default :
				str = "Dos\\Windows";
		}
		_statusBar.setText(str.c_str(), STATUSBAR_EOF_FORMAT);
	};

	void setUniModeText(UniMode um)
	{
		char *uniModeText;
		switch (um)
		{
			case uniUTF8:
				uniModeText = "UTF-8"; break;
			case uni16BE:
				uniModeText = "UCS-2 Big Endian"; break;
			case uni16LE:
				uniModeText = "UCS-2 little Endian"; break;
			case uniCookie:
				uniModeText = "ANSI as UTF-8"; break;
			default :
				uniModeText = "ANSI";
		}
		_statusBar.setText(uniModeText, STATUSBAR_UNICODE_TYPE);
	};

	int langTypeToCommandID(LangType lt) const;

	void checkLangsMenu(int id) const ;

    void setLanguage(int id, LangType langType) {
        _pEditView->setCurrentDocType(langType);
        setLangStatus(langType);
		checkLangsMenu(id);
    };

    int getToolBarState() const {
        HMENU hMenu = ::GetMenu(_hSelf);

        if (::GetMenuState(hMenu, IDM_VIEW_TOOLBAR_HIDE, MF_BYCOMMAND) == MF_CHECKED)
            return IDM_VIEW_TOOLBAR_HIDE;
        
        if (::GetMenuState(hMenu, IDM_VIEW_TOOLBAR_REDUCE, MF_BYCOMMAND) == MF_CHECKED)
            return IDM_VIEW_TOOLBAR_REDUCE;

        if (::GetMenuState(hMenu, IDM_VIEW_TOOLBAR_ENLARGE, MF_BYCOMMAND) == MF_CHECKED)
            return IDM_VIEW_TOOLBAR_ENLARGE;

        if (::GetMenuState(hMenu, IDM_VIEW_TOOLBAR_STANDARD, MF_BYCOMMAND) == MF_CHECKED)
            return IDM_VIEW_TOOLBAR_STANDARD;

		return -1;
    };

    int getFolderMarginStyle() const {
        HMENU hMenu = ::GetMenu(_hSelf);

        if (::GetMenuState(hMenu, IDM_VIEW_FOLDERMAGIN_SIMPLE, MF_BYCOMMAND) == MF_CHECKED)
            return IDM_VIEW_FOLDERMAGIN_SIMPLE;
        
        if (::GetMenuState(hMenu, IDM_VIEW_FOLDERMAGIN_ARROW, MF_BYCOMMAND) == MF_CHECKED)
            return IDM_VIEW_FOLDERMAGIN_ARROW;

        if (::GetMenuState(hMenu, IDM_VIEW_FOLDERMAGIN_CIRCLE, MF_BYCOMMAND) == MF_CHECKED)
            return IDM_VIEW_FOLDERMAGIN_CIRCLE;

        if (::GetMenuState(hMenu, IDM_VIEW_FOLDERMAGIN_BOX, MF_BYCOMMAND) == MF_CHECKED)
            return IDM_VIEW_FOLDERMAGIN_BOX;

		return 0;
    };

    void checkToolBarMenu(int id2Check) const {
        ::CheckMenuRadioItem(::GetMenu(_hSelf), IDM_VIEW_TOOLBAR_HIDE, IDM_VIEW_TOOLBAR_STANDARD, id2Check, MF_BYCOMMAND);
    };

	void checkFolderMarginStyleMenu(int id2Check) const {
		::CheckMenuRadioItem(::GetMenu(_hSelf), IDM_VIEW_FOLDERMAGIN_SIMPLE, IDM_VIEW_FOLDERMAGIN_BOX, id2Check, MF_BYCOMMAND);
	};

    int checkStatusBar() const {
        HMENU hMenu = ::GetMenu(_hSelf);
        int check = (::GetMenuState(hMenu, IDM_VIEW_STATUSBAR, MF_BYCOMMAND) == MF_CHECKED)?MF_UNCHECKED:MF_CHECKED;
        ::CheckMenuItem(hMenu, IDM_VIEW_STATUSBAR, MF_BYCOMMAND | check);
        return check;
    };

    int getFolderMaginStyleIDFrom(folderStyle fStyle) const {
        switch (fStyle)
        {
            case FOLDER_STYLE_SIMPLE : return IDM_VIEW_FOLDERMAGIN_SIMPLE;
            case FOLDER_STYLE_ARROW : return IDM_VIEW_FOLDERMAGIN_ARROW;
            case FOLDER_STYLE_CIRCLE : return IDM_VIEW_FOLDERMAGIN_CIRCLE;
            case FOLDER_STYLE_BOX : return IDM_VIEW_FOLDERMAGIN_BOX;
			default : return FOLDER_TYPE;
        }
        //return 
    };

	void checkMenuItem(int itemID, bool willBeChecked) const {
		::CheckMenuItem(::GetMenu(_hSelf), itemID, MF_BYCOMMAND | (willBeChecked?MF_CHECKED:MF_UNCHECKED));
	};
	void charAdded(char chAdded);
	void MaintainIndentation(char ch);

    void bookmarkAdd(int lineno) const {
		if (lineno == -1)
			lineno = _pEditView->getCurrentLineNumber();
		if (!bookmarkPresent(lineno))
			_pEditView->execute(SCI_MARKERADD, lineno, MARK_SYMBOLE);
	};
    void bookmarkDelete(int lineno) const {
		if (lineno == -1)
			lineno = _pEditView->getCurrentLineNumber();
		if ( bookmarkPresent(lineno))
			_pEditView->execute(SCI_MARKERDELETE, lineno, MARK_SYMBOLE);
	};
    bool bookmarkPresent(int lineno) const {
		if (lineno == -1)
			lineno = _pEditView->getCurrentLineNumber();
		LRESULT state = _pEditView->execute(SCI_MARKERGET, lineno);
		return ((state & (1 << MARK_SYMBOLE)) != 0);
	};
    void bookmarkToggle(int lineno) const {
		if (lineno == -1)
			lineno = _pEditView->getCurrentLineNumber();

		if (bookmarkPresent(lineno))
			bookmarkDelete(lineno);
		else
    		bookmarkAdd(lineno);
	};
    void bookmarkNext(bool forwardScan);
	void bookmarkClearAll() const {
		_pEditView->execute(SCI_MARKERDELETEALL, MARK_SYMBOLE);
	};

    void findMatchingBracePos(int & braceAtCaret, int & braceOpposite);
    void braceMatch();
   
    void activateNextDoc(bool direction);

	void updateStatusBar();
	void showAutoComp();
	void getApiFileName(LangType langType, std::string &fn);

	void changeStyleCtrlsLang(HWND hDlg, int *idArray, const char **translatedText);
	bool replaceAllFiles();
	bool findInOpenedFiles();
	bool findInFiles();

	bool matchInList(const char *fileName, const vector<string> & patterns);
	void getMatchedFileNames(const char *dir, const vector<string> & patterns, vector<string> & fileNames);

	void doSynScorll(HWND hW);
	void changeMenuShortcut(unsigned long cmdID, const char *shortcutStr);
	void setWorkingDir(char *dir) {
		if (!dir || !PathIsDirectory(dir))
		{
			char nppPath[MAX_PATH];
			::GetModuleFileName(NULL, nppPath, sizeof(nppPath));
			PathRemoveFileSpec(nppPath);
			::SetCurrentDirectory(nppPath);
		}
		else
			::SetCurrentDirectory(dir);
	}
	bool str2Cliboard(const char *str2cpy);
	void mkPosIncFindDlg() {
		if (!_incrementFindDlg.isCreated())
			return;
		if (!_incrementFindDlg.isVisible())
			return;
		_incrementFindDlg.goToLowerLeft();
	};
};

#endif //NOTEPAD_PLUS_H
