#include <farplug-wide.h>
#include <fardlgbuilder.hpp>
#include "WrapLng.hpp"
#include "AutoWrap.hpp"

#include "WrapMix.icpp"

#include <utils.h>
#include <KeyFileHelper.h>

#define INI_LOCATION InMyConfig("plugins/autowrap/config.ini")
#define INI_SECTION  "Settings"

SHAREDSYMBOL int WINAPI EXP_NAME(GetMinFarVersion)()
{
	return FARMANAGERVERSION;
}

SHAREDSYMBOL void WINAPI EXP_NAME(SetStartupInfo)(const struct PluginStartupInfo *Info)
{
	::Info = *Info;
	FSF = *Info->FSF;
	::Info.FSF = &FSF;
	KeyFileReadSection kfh(INI_LOCATION, INI_SECTION);
	Opt.Wrap = kfh.GetInt(("Wrap"), 0);
	Opt.RightMargin = kfh.GetInt(("RightMargin"), 75);
	kfh.GetChars(Opt.FileMasks, ARRAYSIZE(Opt.FileMasks), "FileMasks", L"*.*");
	kfh.GetChars(Opt.ExcludeFileMasks, ARRAYSIZE(Opt.ExcludeFileMasks), "ExcludeFileMasks", L"");
}

SHAREDSYMBOL HANDLE WINAPI EXP_NAME(OpenPlugin)(int OpenFrom, INT_PTR Item)
{
	PluginDialogBuilder Builder(Info, MAutoWrap, NULL);
	Builder.AddCheckbox(MEnableWrap, &Opt.Wrap);
	FarDialogItem *RightMargin = Builder.AddIntEditField(&Opt.RightMargin, 3);
	Builder.AddTextAfter(RightMargin, MRightMargin);
	Builder.AddSeparator();
	Builder.AddText(MFileMasks);
	Builder.AddEditField(Opt.FileMasks, ARRAYSIZE(Opt.FileMasks), 65);
	Builder.AddText(MExcludeFileMasks);
	Builder.AddEditField(Opt.ExcludeFileMasks, ARRAYSIZE(Opt.ExcludeFileMasks), 65);
	Builder.AddOKCancel(MOk, MCancel);
	if (Builder.ShowDialog()) {
		KeyFileHelper kfh(INI_LOCATION);
		kfh.SetInt(INI_SECTION, ("Wrap"), Opt.Wrap);
		kfh.SetInt(INI_SECTION, ("RightMargin"), Opt.RightMargin);
		kfh.SetString(INI_SECTION, ("FileMasks"), Opt.FileMasks);
		kfh.SetString(INI_SECTION, ("ExcludeFileMasks"), Opt.ExcludeFileMasks);
	}
	return INVALID_HANDLE_VALUE;
}

SHAREDSYMBOL int WINAPI EXP_NAME(ProcessEditorInput)(const INPUT_RECORD *Rec)
{
	if (!Opt.Wrap)
		return (FALSE);

	static int Reenter = FALSE;

	if (Reenter || Rec->EventType != KEY_EVENT || !Rec->Event.KeyEvent.bKeyDown
			|| Rec->Event.KeyEvent.wVirtualKeyCode == VK_F1)
		return (FALSE);

	struct EditorInfo startei;
	Info.EditorControl(ECTL_GETINFO, &startei);

	struct EditorGetString prevegs;
	prevegs.StringNumber = -1;
	Info.EditorControl(ECTL_GETSTRING, &prevegs);

	Reenter = TRUE;
	Info.EditorControl(ECTL_PROCESSINPUT, (void *)Rec);
	Reenter = FALSE;

	for (int Pass = 1;; Pass++) {
		EditorInfo ei;
		Info.EditorControl(ECTL_GETINFO, &ei);
#ifdef UNICODE
		LPWSTR FileName = NULL;
		size_t FileNameSize = Info.EditorControl(ECTL_GETFILENAME, NULL);
		if (FileNameSize) {
			FileName = new wchar_t[FileNameSize];
			if (FileName) {
				Info.EditorControl(ECTL_GETFILENAME, FileName);
			}
		}
#endif
		if (Pass == 1 && *Opt.FileMasks) {
			if (ei.CurLine != startei.CurLine) {
#ifdef UNICODE
				if (FileName) {
					delete[] FileName;
				}
#endif
				return TRUE;
			}
			int Found = FALSE;
			TCHAR FileMask[MAX_PATH], *MaskPtr = Opt.FileMasks;
			while ((MaskPtr = GetCommaWord(MaskPtr, FileMask)) != NULL)
				if (Info.CmpName(FileMask,
#ifndef UNICODE
							ei.
#endif
							FileName,
							TRUE)) {
					Found = TRUE;
					break;
				}
			if (!Found) {
#ifdef UNICODE
				if (FileName) {
					delete[] FileName;
				}
#endif
				return TRUE;
			}
			MaskPtr = Opt.ExcludeFileMasks;
			while ((MaskPtr = GetCommaWord(MaskPtr, FileMask)) != NULL)
				if (Info.CmpName(FileMask,
#ifndef UNICODE
							ei.
#endif
							FileName,
							TRUE)) {
					Found = FALSE;
					break;
				}
			if (!Found) {
#ifdef UNICODE
				if (FileName) {
					delete[] FileName;
				}
#endif
				return TRUE;
			}
		}
#ifdef UNICODE
		if (FileName) {
			delete[] FileName;
		}
#endif

		struct EditorGetString egs;
		egs.StringNumber = ei.CurLine;
		Info.EditorControl(ECTL_GETSTRING, &egs);

		int TabPresent = _tmemchr(egs.StringText, _T('\t'), egs.StringLength) != NULL;
		int TabLength = egs.StringLength;
		if (TabPresent) {
			struct EditorConvertPos ecp;
			ecp.StringNumber = -1;
			ecp.SrcPos = egs.StringLength;
			Info.EditorControl(ECTL_REALTOTAB, &ecp);
			TabLength = ecp.DestPos;
		}

		if ((Pass != 1 || prevegs.StringLength != egs.StringLength) && TabLength >= Opt.RightMargin
				&& ei.CurPos >= egs.StringLength) {
			int SpacePos = -1;
			int I;
			for (I = egs.StringLength - 1; I > 0; I--)
				if (egs.StringText[I] == _T(' ') || egs.StringText[I] == _T('\t')) {
					SpacePos = I;
					int TabPos = I;
					if (TabPresent) {
						struct EditorConvertPos ecp;
						ecp.StringNumber = -1;
						ecp.SrcPos = I;
						Info.EditorControl(ECTL_REALTOTAB, &ecp);
						TabPos = ecp.DestPos;
					}
					if (TabPos < Opt.RightMargin)
						break;
				}

			if (SpacePos <= 0)
				break;

			int SpaceOnly = TRUE;
			for (I = 0; I < SpacePos; I++)
				if (egs.StringText[I] != _T(' ') && egs.StringText[I] != _T('\t')) {
					SpaceOnly = FALSE;
					break;
				}

			if (SpaceOnly)
				break;

			struct EditorSetPosition esp;
			memset(&esp, -1, sizeof(esp));
			esp.CurPos = SpacePos + 1;
			Info.EditorControl(ECTL_SETPOSITION, &esp);
			int Indent = TRUE;
			if (!Info.EditorControl(ECTL_INSERTSTRING, &Indent))
				break;
			if (ei.CurPos < SpacePos) {
				esp.CurLine = ei.CurLine;
				esp.CurPos = ei.CurPos;
				Info.EditorControl(ECTL_SETPOSITION, &esp);
			} else {
				egs.StringNumber = ei.CurLine + 1;
				Info.EditorControl(ECTL_GETSTRING, &egs);
				esp.CurLine = ei.CurLine + 1;
				esp.CurPos = egs.StringLength;
				Info.EditorControl(ECTL_SETPOSITION, &esp);
			}
			Info.EditorControl(ECTL_REDRAW, NULL);
		} else
			break;
	}
	return (TRUE);
}

SHAREDSYMBOL void WINAPI EXP_NAME(GetPluginInfo)(struct PluginInfo *Info)
{
	Info->StructSize = sizeof(*Info);
	Info->Flags = PF_EDITOR | PF_DISABLEPANELS;
	Info->DiskMenuStringsNumber = 0;
	static const TCHAR *PluginMenuStrings[1];
	PluginMenuStrings[0] = GetMsg(MAutoWrap);
	Info->PluginMenuStrings = PluginMenuStrings;
	Info->PluginMenuStringsNumber = ARRAYSIZE(PluginMenuStrings);
	Info->PluginConfigStringsNumber = 0;
}
