#include <farplug-wide.h>
#include "DrawLng.hpp"
#include "DrawLine.hpp"

static TCHAR BoxChar[] =
#ifndef UNICODE
	{		// OEM
		0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1,
		0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0,
		0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA};
#else
	{		// UNICODE
		0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c,
		0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a,
		0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559,
		0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c};
#endif

static short BoxLeft[] = {0, 1, 2, 1, 1, 2, 2, 0, 2, 2, 1, 2, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 2, 2, 0, 2, 2,
		2, 1, 2, 1, 0, 0, 0, 0, 1, 2, 1, 0};
static short BoxUp[] = {1, 1, 1, 2, 0, 0, 2, 2, 0, 2, 2, 1, 0, 1, 1, 0, 1, 0, 1, 1, 2, 2, 0, 2, 0, 2, 0, 2, 1,
		2, 0, 0, 2, 1, 0, 0, 2, 1, 1, 0};
static short BoxRight[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2,
		2, 1, 2, 1, 1, 2, 2, 1, 1, 2, 0, 1};
static short BoxDown[] = {1, 1, 1, 2, 2, 1, 2, 2, 2, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 0, 2, 0, 2, 2, 0, 2,
		0, 0, 1, 2, 0, 0, 1, 2, 2, 1, 0, 1};

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

SHAREDSYMBOL void WINAPI EXP_NAME(SetStartupInfo)(const struct PluginStartupInfo *Info)
{
	::Info = *Info;
}

SHAREDSYMBOL HANDLE WINAPI EXP_NAME(OpenPlugin)(int OpenFrom, INT_PTR Item)
{
	static int Reenter = FALSE;

	if (Reenter)
		return (INVALID_HANDLE_VALUE);
	Reenter = TRUE;

	int LineWidth = 1, KeyCode /*I*/;
	BOOL Done = FALSE;
	INPUT_RECORD rec;
	/*COORD MousePos={-1,-1};*/

	SetTitle(LineWidth, (LineWidth == 1) ? MTitleSingle : MTitleDouble);

	while (!Done) {
		Info.EditorControl(ECTL_READINPUT, &rec);

		if ((rec.EventType & (~0x8000)) != KEY_EVENT || !rec.Event.KeyEvent.bKeyDown) {
#if 0
      if(rec.EventType==MOUSE_EVENT)
      {
        if((rec.Event.MouseEvent.dwControlKeyState&SHIFT_PRESSED) &&
         (rec.Event.MouseEvent.dwButtonState&(FROM_LEFT_1ST_BUTTON_PRESSED|RIGHTMOST_BUTTON_PRESSED)))
        {
          int DX=MousePos.X - rec.Event.MouseEvent.dwMousePosition.X;
          int DY=MousePos.Y - rec.Event.MouseEvent.dwMousePosition.Y;
          int KeyCodeX=!DX?VK_NUMPAD0:(DX<0?VK_NUMPAD6:VK_NUMPAD4);
          int KeyCodeY=!DY?VK_NUMPAD0:(DY<0?VK_NUMPAD2:VK_NUMPAD8);

          if(DX < 0) DX=-DX;
          if(DY < 0) DY=-DY;

          // Ctrl
          if(rec.Event.MouseEvent.dwControlKeyState&(LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
          {
            if(DX < DY)
              DX=0;
            else if(DX > DY)
              DY=0;
          }

          if(DX && !DY)
          {
            for(I=0; I < DX; ++I)
              ProcessShiftKey(KeyCodeX,
                (rec.Event.MouseEvent.dwButtonState&FROM_LEFT_1ST_BUTTON_PRESSED)?1:2);
          }
          else if(!DX && DY)
          {
            for(I=0; I < DY; ++I)
              ProcessShiftKey(KeyCodeY,
                (rec.Event.MouseEvent.dwButtonState&FROM_LEFT_1ST_BUTTON_PRESSED)?1:2);
          }
          else if(DX && DY)
          {
            for(I=0;; ++I)
            {
              ProcessShiftKey(KeyCodeX,
                (rec.Event.MouseEvent.dwButtonState&FROM_LEFT_1ST_BUTTON_PRESSED)?1:2);
              ProcessShiftKey(KeyCodeY,
                (rec.Event.MouseEvent.dwButtonState&FROM_LEFT_1ST_BUTTON_PRESSED)?1:2);
              DX--;
              DY--;
              if(DX < 0 && DY < 0) //??
                break;
            }
          }
        }
        MousePos=rec.Event.MouseEvent.dwMousePosition;
        Info.EditorControl(ECTL_PROCESSINPUT,&rec);
        continue;
      }
      else
#endif
			{
				Info.EditorControl(ECTL_PROCESSINPUT, &rec);
				continue;
			}
		} else
			KeyCode = rec.Event.KeyEvent.wVirtualKeyCode;

		switch (KeyCode) {
			case VK_ESCAPE:
			case VK_F10:
				if ((rec.Event.KeyEvent.dwControlKeyState
							& (SHIFT_PRESSED | LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | LEFT_ALT_PRESSED
									| RIGHT_ALT_PRESSED))
						== 0)
					Done = TRUE;
				break;

			case VK_F1:
				Info.ShowHelp(Info.ModuleName, NULL, 0);
				break;

			case VK_F2:
				if ((rec.Event.KeyEvent.dwControlKeyState
							& (SHIFT_PRESSED | LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | LEFT_ALT_PRESSED
									| RIGHT_ALT_PRESSED))
						== 0) {
					LineWidth = 3 - LineWidth;
					SetTitle(LineWidth, ((LineWidth == 1) ? MTitleSingle : MTitleDouble));
				}
				break;

			default:
				if ((KeyCode >= VK_PRIOR && KeyCode <= VK_DOWN)
						|| (KeyCode >= VK_NUMPAD0 && KeyCode <= VK_NUMPAD9)) {
					if (rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
						ProcessShiftKey(KeyCode, LineWidth);
					else
						Info.EditorControl(ECTL_PROCESSINPUT, &rec);
				} else {
					if (KeyCode < VK_F3 || KeyCode > VK_F12)
						Info.EditorControl(ECTL_PROCESSINPUT, &rec);
					continue;
				}
				break;
		}
	}
	Info.EditorControl(ECTL_SETTITLE, NULL);
	Info.EditorControl(ECTL_SETKEYBAR, NULL);
	Reenter = FALSE;
	return (INVALID_HANDLE_VALUE);
}

void SetTitle(int LineWidth, int IDTitle)
{
	int I;
	struct KeyBarTitles Kbt;
	for (I = 0; I < 12; ++I) {
		Kbt.Titles[I] = (TCHAR *)TEXT("");
		Kbt.CtrlTitles[I] = (TCHAR *)TEXT("");
		Kbt.AltTitles[I] = (TCHAR *)TEXT("");
		Kbt.ShiftTitles[I] = (TCHAR *)TEXT("");
		Kbt.CtrlShiftTitles[I] = (TCHAR *)TEXT("");
		Kbt.AltShiftTitles[I] = (TCHAR *)TEXT("");
		Kbt.CtrlAltTitles[I] = (TCHAR *)TEXT("");
	}
	Kbt.Titles[1 - 1] = (TCHAR *)GetMsg(MHelp);
	Kbt.Titles[2 - 1] = (TCHAR *)GetMsg((LineWidth == 1) ? MDouble : MSingle);
	Kbt.Titles[10 - 1] = (TCHAR *)GetMsg(MQuit);
	Info.EditorControl(ECTL_SETKEYBAR, &Kbt);
	Info.EditorControl(ECTL_SETTITLE, (void *)GetMsg(IDTitle));
}

void ProcessShiftKey(int KeyCode, int LineWidth)
{
	EditorInfo ei;
	Info.EditorControl(ECTL_GETINFO, &ei);

	struct EditorSetPosition esp;
	esp.CurLine = ei.CurLine;
	esp.CurPos = ei.CurTabPos;
	esp.CurTabPos = -1;
	esp.TopScreenLine = -1;
	esp.LeftPos = -1;
	esp.Overtype = -1;

	if (ei.CurLine > 0) {
		int StringNumber = ei.CurLine - 1;
		Info.EditorControl(ECTL_EXPANDTABS, &StringNumber);
	}
	Info.EditorControl(ECTL_EXPANDTABS, &ei.CurLine);
	if (ei.CurLine >= ei.TotalLines - 1) {
		struct EditorGetString egs;
		egs.StringNumber = ei.CurLine;
		Info.EditorControl(ECTL_GETSTRING, &egs);
		struct EditorSetPosition esp;
		esp.CurLine = ei.CurLine;
		esp.CurPos = egs.StringLength;
		esp.CurTabPos = -1;
		esp.TopScreenLine = -1;
		esp.LeftPos = -1;
		esp.Overtype = -1;
		Info.EditorControl(ECTL_SETPOSITION, &esp);
		Info.EditorControl(ECTL_INSERTSTRING, NULL);
		esp.CurLine = ei.CurLine;
		esp.CurPos = ei.CurTabPos;
		Info.EditorControl(ECTL_SETPOSITION, &esp);
	}

	if (ei.CurLine < ei.TotalLines - 1) {
		int StringNumber = ei.CurLine + 1;
		Info.EditorControl(ECTL_EXPANDTABS, &StringNumber);
	}
	Info.EditorControl(ECTL_GETINFO, &ei);

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

	int StringLength = egs.StringLength > ei.CurPos ? egs.StringLength : ei.CurPos + 1;
	TCHAR *NewString = (TCHAR *)malloc(StringLength * sizeof(TCHAR));
	if (StringLength > egs.StringLength)
		_tmemset(NewString + egs.StringLength, L' ', StringLength - egs.StringLength);
	_tmemcpy(NewString, egs.StringText, egs.StringLength);

	int LeftLine, UpLine, RightLine, DownLine;
	GetEnvType(NewString, StringLength, &ei, LeftLine, UpLine, RightLine, DownLine);

	switch (KeyCode) {
		case VK_UP:
		case VK_NUMPAD8:
			UpLine = LineWidth;
			if (LeftLine == 0 && RightLine == 0)
				DownLine = UpLine;
			if (esp.CurLine > 0)
				esp.CurLine--;
			break;
		case VK_DOWN:
		case VK_NUMPAD2:
			DownLine = LineWidth;
			if (LeftLine == 0 && RightLine == 0)
				UpLine = DownLine;
			esp.CurLine++;
			break;
		case VK_LEFT:
		case VK_NUMPAD4:
			LeftLine = LineWidth;
			if (UpLine == 0 && DownLine == 0)
				RightLine = LeftLine;
			if (esp.CurPos > 0)
				esp.CurPos--;
			break;
		case VK_RIGHT:
		case VK_NUMPAD6:
			RightLine = LineWidth;
			if (UpLine == 0 && DownLine == 0)
				LeftLine = RightLine;
			esp.CurPos++;
			break;
	}

	if (LeftLine != 0 && RightLine != 0 && LeftLine != RightLine)
		LeftLine = RightLine = LineWidth;

	if (UpLine != 0 && DownLine != 0 && UpLine != DownLine)
		UpLine = DownLine = LineWidth;

	for (size_t I = 0; I < sizeof(BoxChar) / sizeof(TCHAR); I++)
		if (LeftLine == BoxLeft[I] && UpLine == BoxUp[I] && RightLine == BoxRight[I]
				&& DownLine == BoxDown[I]) {
			NewString[ei.CurPos] = BoxChar[I];

#ifndef UNICODE
			struct EditorConvertText ect;
			ect.Text = NewString + ei.CurPos;
			ect.TextLength = 1;
			Info.EditorControl(ECTL_OEMTOEDITOR, &ect);
#endif

			struct EditorSetString ess;
			ess.StringNumber = egs.StringNumber;
			ess.StringText = NewString;
			ess.StringEOL = (TCHAR *)egs.StringEOL;
			ess.StringLength = StringLength;

			Info.EditorControl(ECTL_SETSTRING, &ess);
			Info.EditorControl(ECTL_SETPOSITION, &esp);
			Info.EditorControl(ECTL_REDRAW, NULL);
			break;
		}
	free(NewString);
}

void GetEnvType(TCHAR *NewString, int StringLength, struct EditorInfo *ei, int &LeftLine, int &UpLine,
		int &RightLine, int &DownLine)
{
	TCHAR OldChar[3];

	OldChar[0] = ei->CurPos > 0 ? NewString[ei->CurPos - 1] : L' ';
	OldChar[1] = NewString[ei->CurPos];
	OldChar[2] = ei->CurPos < StringLength - 1 ? NewString[ei->CurPos + 1] : L' ';

#ifndef UNICODE
	struct EditorConvertText ect;
	ect.Text = OldChar;
	ect.TextLength = sizeof(OldChar) / sizeof(TCHAR);
	Info.EditorControl(ECTL_EDITORTOOEM, &ect);
#endif

	TCHAR LeftChar = OldChar[0];
	TCHAR RightChar = OldChar[2];
	TCHAR UpChar = L' ';
	TCHAR DownChar = L' ';

	if (ei->CurLine > 0) {
		struct EditorGetString UpStr;
		UpStr.StringNumber = ei->CurLine - 1;
		Info.EditorControl(ECTL_GETSTRING, &UpStr);
		if (ei->CurPos < UpStr.StringLength)
			UpChar = UpStr.StringText[ei->CurPos];
#ifndef UNICODE
		struct EditorConvertText ect;
		ect.Text = &UpChar;
		ect.TextLength = 1;
		Info.EditorControl(ECTL_EDITORTOOEM, &ect);
#endif
	}
	if (ei->CurLine < ei->TotalLines - 1) {
		struct EditorGetString DownStr;
		DownStr.StringNumber = ei->CurLine + 1;
		Info.EditorControl(ECTL_GETSTRING, &DownStr);
		if (ei->CurPos < DownStr.StringLength)
			DownChar = DownStr.StringText[ei->CurPos];
#ifndef UNICODE
		struct EditorConvertText ect;
		ect.Text = &DownChar;
		ect.TextLength = 1;
		Info.EditorControl(ECTL_EDITORTOOEM, &ect);
#endif
	}
	LeftLine = UpLine = RightLine = DownLine = 0;
	for (size_t I = 0; I < sizeof(BoxChar) / sizeof(TCHAR); I++) {
		if (LeftChar == BoxChar[I])
			LeftLine = BoxRight[I];
		if (UpChar == BoxChar[I])
			UpLine = BoxDown[I];
		if (RightChar == BoxChar[I])
			RightLine = BoxLeft[I];
		if (DownChar == BoxChar[I])
			DownLine = BoxUp[I];
	}
}

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(MDrawLines);
	Info->PluginMenuStrings = PluginMenuStrings;
	Info->PluginMenuStringsNumber = sizeof(PluginMenuStrings) / sizeof(PluginMenuStrings[0]);
	Info->PluginConfigStringsNumber = 0;
}

const TCHAR *GetMsg(int MsgId)
{
	return (Info.GetMsg(Info.ModuleNumber, MsgId));
}
