//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 FIND_REPLACE_DLG_H
#define FIND_REPLACE_DLG_H

#include "StaticDialog.h"
#include "FindReplaceDlg_rc.h"
#include "Buffer.h"
#include "ScintillaEditView.h"
#include "StatusBar.h"

typedef bool DIALOG_TYPE;
#define REPLACE true
#define FIND false

#define DIR_DOWN true
#define DIR_UP false

//#define FIND_REPLACE_STR_MAX 256

typedef bool InWhat;
#define ALL_OPEN_DOCS true
#define FILES_IN_DIR false

const int REPLACE_ALL = 0;
const int MARK_ALL = 1;
const int COUNT_ALL = 2;
const int FIND_ALL = 3;

const int DISPLAY_POS_TOP = 2;
const int DISPLAY_POS_MIDDLE = 1;
const int DISPLAY_POS_BOTTOM = 0;

struct FoundInfo {
	FoundInfo(int start, int end, const char *foundLine, const char *fullPath)
		: _start(start), _end(end), _foundLine(foundLine), _fullPath(fullPath){};
	int _start;
	int _end;
	std::string _foundLine;
	std::string _fullPath;
};

class Finder : public ScintillaEditView {
public:
	Finder() : _markedLine(-1) {};

	void add(FoundInfo fi, int lineNb, const char *dir2search = NULL) {
		_foundInfos.push_back(fi);
		std::string str;
		if (dir2search)
		{
			size_t len = strlen(dir2search);
			str = fi._fullPath.substr(len, fi._fullPath.length() - len);
		}
		else
		{
			str = PathFindFileName(fi._fullPath.c_str());
		}
		str += " (";
		char lnb[16];
		str += itoa(lineNb, lnb, 10);
		str += "):";
		str += fi._foundLine;
		execute(SCI_APPENDTEXT, str.length(), (LPARAM)str.c_str());
	};

	void setFinderStyle();

	void removeAll() {
		_markedLine = -1;
		_foundInfos.clear();
		execute(SCI_CLEARALL);
	};

	FoundInfo & getInfo() {
		int nbInfo = _foundInfos.size();
		int index = getCurrentLineNumber();

		if ((!nbInfo) || (index >= nbInfo))
			throw int(255);
		return _foundInfos[index];
	};

	bool isEmpty() const {
		return _foundInfos.empty();
	};

	int getCurrentMarkedLine() const {return _markedLine;};
	void setCurrentMarkedLine(int line) {_markedLine = line;};

private:
	std::vector<FoundInfo> _foundInfos;
	int _markedLine;
};

struct FindOption {
	bool _isWholeWord;
	bool _isMatchCase;
	bool _isRegExp;
	bool _isWrapAround;
	bool _whichDirection;
	bool _isIncremental;
	FindOption() :_isWholeWord(true), _isMatchCase(true), _isRegExp(false),\
		_isWrapAround(true), _whichDirection(DIR_DOWN), _isIncremental(false){};
};

void addText2Combo(const char * txt2add, HWND comboID, bool isUTF8 = false);
string getTextFromCombo(HWND hCombo, bool isUnicode);


class FindInFilesDlg : public StaticDialog
{
public :
	void doDialog(bool isRTL = false);
	const string & getFilters() const {return _filters;};
	const string & getDirectory() const {return _directory;};

private :
	string _filters;
	string _directory;

	virtual BOOL CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam);


};

class FindReplaceDlg : public StaticDialog
{
friend class FindIncrementDlg;
public :
	FindReplaceDlg() : StaticDialog(), _pFinder(NULL), _isRTL(false) {};
	~FindReplaceDlg() {
		if (_pFinder)
		{
			delete _pFinder;
			_statusBar.destroy();
		}
	};

	void init(HINSTANCE hInst, HWND hPere, ScintillaEditView **ppEditView) {
		Window::init(hInst, hPere);
		if (!ppEditView)
			throw int(9900);
		_ppEditView = ppEditView;
	};

	virtual void create(int dialogID, bool isRTL = false);
	
	void initOptionsFromDlg()	{
		_options._isWholeWord = isCheckedOrNot(IDWHOLEWORD);
		_options._isMatchCase = isCheckedOrNot(IDMATCHCASE);
		_options._isRegExp = isCheckedOrNot(IDREGEXP);
		_options._isWrapAround = isCheckedOrNot(IDWRAP);
		_isInSelection = isCheckedOrNot(IDC_IN_SELECTION_CHECK);

		// Set Direction : Down by default
		_options._whichDirection = DIR_DOWN;
		::SendMessage(::GetDlgItem(_hSelf, IDDIRECTIONDOWN), BM_SETCHECK, BST_CHECKED, 0);

		_doPurge = isCheckedOrNot(IDC_PURGE_CHECK);
		_doMarkLine = isCheckedOrNot(IDC_MARKLINE_CHECK);
		_doStyleFoundToken = isCheckedOrNot(IDC_STYLEFOUND_CHECK);

		::EnableWindow(::GetDlgItem(_hSelf, IDCMARKALL), (_doMarkLine || _doStyleFoundToken));
		::SendMessage(::GetDlgItem(_hSelf, IDC_DISPLAYPOS_BOTTOM), BM_SETCHECK, BST_CHECKED, 0);
	};


    void doFindDlg(bool isRTL = false) {
		doDialog(FIND, isRTL);
	};

	void doReplaceDlg(bool isRTL = false) {
		doDialog(REPLACE, isRTL);
	};

	void doDialog(DIALOG_TYPE whichType, bool isRTL = false) {
		if (!isCreated())
		{
			create(IDD_FIND_REPLACE_DLG, isRTL);
			_isRTL = isRTL;
		}

		enableReplaceFunc(whichType);
		::SetFocus(::GetDlgItem(_hSelf, IDFINDWHAT));
		display();
	};
	bool processFindNext(const char *txt2find, FindOption *options = NULL);
	bool processReplace();

	int processAll(int op, bool isEntire = false, const char *dir2search = NULL);
	void replaceAllInOpenedDocs();
	void findAllIn(InWhat op);
	void setSearchText(const char * txt2find, bool isUTF8 = false) {
		addText2Combo(txt2find, ::GetDlgItem(_hSelf, IDFINDWHAT), isUTF8);
	}

	void setFinderReadOnly(bool isReadOnly = true) {
		_pFinder->execute(SCI_SETREADONLY, isReadOnly);
	};

	bool isFinderEmpty() const {
		return _pFinder->isEmpty();
	};

	void clearFinder() {
		_pFinder->removeAll();
	};

	void putFindResult(int result) {
		_findAllResult = result;
	};

	/// Sets the direction in which to search.
	/// \param dir Direction to search (DIR_UP or DIR_DOWN)
	///
	void setSearchDirection(bool dir) {
		_options._whichDirection = dir;
	};

	const char * getDir2Search() const {return (_findInFilesDlg.getDirectory()).c_str();};

	void getPatterns(vector<string> & patternVect);

	void launchFindInFilesDlg() {
		::SendMessage(_hSelf, WM_COMMAND, IDC_FINDINFILES, 0);
	};

	string getText2search() const {
		bool isUnicode = (*_ppEditView)->getCurrentBuffer().getUnicodeMode() != uni8Bit;
		return getTextFromCombo(::GetDlgItem(_hSelf, IDFINDWHAT), isUnicode);
	};

protected :
	virtual BOOL CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam);

private :
	DIALOG_TYPE _currentStatus;
	FindOption _options;

	bool _doPurge;
	bool _doMarkLine;
	bool _doStyleFoundToken;
	bool _isInSelection;

	RECT _replaceCancelPos, _findCancelPos;
	size_t _replaceW, _replaceH, _findW, _findH;

	ScintillaEditView **_ppEditView;
	Finder  *_pFinder;
	StatusBar _statusBar;
	bool _isRTL;
	FindInFilesDlg _findInFilesDlg;

	int _findAllResult;

	void enableReplaceFunc(bool isEnable);

	bool isCheckedOrNot(int checkControlID) const {
		return (BST_CHECKED == ::SendMessage(::GetDlgItem(_hSelf, checkControlID), BM_GETCHECK, 0, 0));
	};

	int getDisplayPos() const {
		if (isCheckedOrNot(IDC_DISPLAYPOS_TOP))
			return DISPLAY_POS_TOP;
		else if (isCheckedOrNot(IDC_DISPLAYPOS_MIDDLE))
			return DISPLAY_POS_MIDDLE;
		else //IDC_DISPLAYPOS_BOTTOM
			return DISPLAY_POS_BOTTOM;
	};
	bool notify(SCNotification *notification);

	void resizeFinder();
	void resizeStatusBar();
	void updateCombos();
	void updateCombo(int comboID) {
		bool isUnicode = (*_ppEditView)->getCurrentBuffer().getUnicodeMode() != uni8Bit;
		HWND hCombo = ::GetDlgItem(_hSelf, comboID);
		addText2Combo(getTextFromCombo(hCombo, isUnicode).c_str(), hCombo);
	};
};

class FindIncrementDlg : public StaticDialog
{
public :
	//FindIncrementDlg() : _doSearchFromBegin()
	void init(HINSTANCE hInst, HWND hPere, FindReplaceDlg *pFRDlg) {
		Window::init(hInst, hPere);
		if (!pFRDlg)
			throw int(9910);
		_pFRDlg = pFRDlg;
	};

	void doDialog(bool isRTL = false) {
		if (!isCreated())
		{
			create(IDD_INCREMENT_FIND, isRTL);
			_isRTL = isRTL;
		}

		goToLowerLeft();
		::SetFocus(::GetDlgItem(_hSelf, IDC_INCFINDTEXT));
		display();
		_doSearchFromBegin = true;
	};
	void goToLowerLeft();
	void setSearchText(const char * txt2find, bool isUTF8 = false) {
		_doSearchFromBegin = false;
		if (!isUTF8)
		{
			::SendDlgItemMessage(_hSelf, IDC_INCFINDTEXT, WM_SETTEXT, 0, (LPARAM)txt2find);
			return;
		}
		WCHAR wchars[256];
		::MultiByteToWideChar(CP_UTF8, 0, txt2find, -1, wchars, 256 / sizeof(WCHAR));
		::SendDlgItemMessageW(_hSelf, IDC_INCFINDTEXT, WM_SETTEXT, 0, (LPARAM)wchars);
	}

private :
	bool _isRTL;
	FindReplaceDlg *_pFRDlg;
	bool _doSearchFromBegin;
	virtual BOOL CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam);
};

#endif //FIND_REPLACE_DLG_H
