#include "Edit.h"
#include <stdio.h>
#include <map>

// 在某字符后创建一个新的字符

Edit::Char* Edit::NewChar(Char* pc)
{
	// [pc] <-> [p] <-> [pc->next] 
	Char* p = new Char;
	p->prev = pc;
	p->next = pc->next;
	if (pc->next)
		pc->next->prev = p;
	pc->next = p;
	return p;
}

void Edit::DeleteChar(Char* pc)
{
	Char* prev = pc->prev;
	Char* next = pc->next;
	delete pc;
	if (prev)
		prev->next = next;
	if (next)
		next->prev = prev;
}

SIZE Edit::GetCharSize(TCHAR tch)
{
	return { textwidth(tch),textheight(tch) };
}

// 获取新输入字符所在行的 y 位置（返回是否需要新开一行）

bool Edit::GetLinePos(Char* pc, int* line_pos)
{
	*line_pos = pc->prev->rct.bottom;
	return (*line_pos) == 0 || pc->prev->tch == _T('\n');
}

Edit::Char* Edit::GetValidLeftChar(Char* pc)
{
	if (pc->prev)
		if (pc->prev->tch == _T('\n'))	// 不可聚焦到换行符
			return pc->prev->prev;		// 父亲是换行符的情况下，祖父一定非空
		else
			return pc->prev;
	else
		return pc;
}

Edit::Char* Edit::GetValidRightChar(Char* pc)
{
	if (pc->next)
		if (pc->next->tch == _T('\n'))	// 不可聚焦到换行符
			if (pc->next->next)			// 儿子是换行符的情况下，孙子按理来说一定是行占位符，但仍判定一次
				return pc->next->next;
			else
				return pc;
		else
			return pc->next;
	else
		return pc;
}

Edit::Char* Edit::GetValidUpOrDownChar(Char* pc, int x_pos, bool up)
{
	// 往某方向找到的第 n 个换行符
	Char* l1 = nullptr;
	Char* l2 = up ? &m_head : nullptr;	// 向上未找到时默认设为头结点

	// 找换行符
	Char* p = pc;
	while (p)
	{
		if (p->tch == _T('\n'))
		{
			if (l1)
			{
				l2 = p;
				break;
			}
			else
				l1 = p;
		}

		if (up)
			p = p->prev;
		else
			p = p->next;
	}

	// 存在上或下一行
	if (l1)
	{
		Char* pf;						// 搜索索引
		Char* best;						// 最佳匹配
		int best_distance = INT_MAX;	// 最佳匹配的距离

		if (up)	// 向上搜索时，设置上一行的开端为起点
			pf = (l2 == &m_head ? l2 : l2->next);
		else	// 向下搜索时，设置下一行的开端为起点（存在行占位符，不会出现空指针）
			pf = l1->next;
		best = pf;

		// 向上搜索遇到上一行结尾时停止
		// 向下搜索遇到下一行结尾时停止（或搜空）
		while (pf && ((up && pf != l1) || (!up && pf != l2)))
		{
			if (pf->tch != _T('\n'))
			{
				int distance = abs(pf->rct.right - x_pos);
				if (distance < best_distance)
				{
					best = pf;
					best_distance = distance;
				}
			}
			pf = pf->next;
		}

		return best;
	}
	else
	{
		return pc;
	}
}

Edit::Char* Edit::MoveFollowCharPos(Char* pc, int x, int y, bool is_inline, bool absolute)
{
	Char* p = pc;
	int x_offset = x;
	int y_offset = y;
	if (absolute)
	{
		x_offset = x - p->rct.left;
		y_offset = y - p->rct.bottom;
	}

	while (true)
	{
		p->rct.left += x_offset;
		p->rct.right += x_offset;
		p->rct.top += y_offset;
		p->rct.bottom += y_offset;

		// 单行模式下，移动完换行符就结束，若遇到空也停止
		if (p->next && (!is_inline || p->tch != _T('\n')))
			p = p->next;
		else
			break;
	}

	return p;
}

void Edit::ConnectFollowChar(Char* pc)
{
	// 如果有后续字符
	if (pc->next)
	{
		// 标记此行的换行符（暂时设为 pc）
		Char* next_line = pc;

		// 同行右侧还有字符，且下一个字符未紧贴
		if (pc->tch != _T('\n') && (pc->next->rct.left != pc->rct.right || pc->next->rct.bottom != pc->rct.bottom))
			next_line = MoveFollowCharPos(pc->next, pc->rct.right, pc->rct.bottom, true, true);

		// 如果没有调整同行右侧字符，或者调整后返回的最后一个被调整的字符不是换行符，
		// 则需要重新搜索，确定该行是否有换行符
		while (next_line && next_line->tch != _T('\n'))
			next_line = next_line->next;

		// 如果 next_line 非空，说明得到换行符，此时需要检查下面的行是否对齐
		if (next_line)
		{
			// 检验下一行的行占位符的 x, y 轴是否对齐
			bool x_align = next_line->next->rct.left == 0;
			bool y_align = next_line->next->rct.bottom == next_line->rct.bottom + m_standard_char_height;

			// 若 x 轴已对齐，y 轴未对齐，则将后续所有字符 y 对齐即可
			if (x_align && !y_align)
				MoveFollowCharPos(next_line->next, 0, next_line->rct.bottom + m_standard_char_height, false, true);

			// x, y 轴都未对齐，说明是在行内插入了换行符，所以只需要将下一行 x, y 轴对齐，后续行 y 对齐即可
			else if (!x_align && !y_align)
			{
				Char* lp = MoveFollowCharPos(next_line->next, 0, next_line->rct.bottom + m_standard_char_height, true, true);
				if (lp->next)
				{
					MoveFollowCharPos(lp->next, 0, lp->rct.bottom + m_standard_char_height, false, true);
				}
			}
		}
	}
}

void Edit::Delete()
{
	// 若前面存在字符
	if (m_cursors.end.pos->prev || m_cursors.selecting)
	{
		// 如果正在选区
		if (m_cursors.selecting)
		{
			// 从近光标一直删到远光标（近光标不删）
			Char* pDel = m_cursors.pBehind->pos->next;
			Char* pEnd = m_cursors.pFront->pos->next;
			while (pDel && pDel != pEnd)
			{
				Char* p = pDel->next;
				DeleteChar(pDel);
				pDel = p;
			}

			// 恢复光标位置
			m_cursors.end.pos = m_cursors.pBehind->pos;

			// 重置选区状态
			ResetCursorSelState(&m_cursors);
			m_lbtn = false;
		}

		// 未选区，即为单删
		else
		{
			// 光标前移，删除后一个字符
			m_cursors.end.pos = m_cursors.end.pos->prev;
			DeleteChar(m_cursors.end.pos->next);
		}

		// 若聚焦到换行符，说明删除已跨行，可以删除换行符
		if (m_cursors.end.pos->tch == _T('\n'))
		{
			m_cursors.end.pos = m_cursors.end.pos->prev;	// 直接前移即可
			DeleteChar(m_cursors.end.pos->next);
		}

		// 清除断层
		ConnectFollowChar(m_cursors.end.pos);

		// 记录光标 x 位置
		UpdateCursorXPos(&m_cursors);
	}
}

void Edit::Insert(TCHAR tch)
{
	// 有选区时先删除内容
	if (m_cursors.selecting)
	{
		Delete();
	}

	// 插入新字符，并将光标设置到新字符位置
	Char* pnew = NewChar(m_cursors.end.pos);
	m_cursors.end.pos = pnew;

	// 预处理字符
	switch (tch)
	{
		// 换行
	case _T('\r'):
	{
		pnew->tch = _T('\n');	// 统一 '\r' 为 '\n'

		// 在换行符后插入行占位符，位置稍后调整
		Char* pNewLine = NewChar(pnew);

		// 将光标绑定到行占位符上
		m_cursors.end.pos = pNewLine;
		break;
	}

	// 普通字符
	default:
		pnew->tch = tch;
		break;
	}

	// 获取字符大小
	SIZE chSize = GetCharSize(pnew->tch);

	// 处理字符尺寸
	switch (pnew->tch)
	{
	case _T('\t'):
		chSize.cx = m_standard_char_width * 4;
		chSize.cy = m_standard_char_height;
		break;
	}

	int line_pos = 0;					// 当前行高
	int x_pos = pnew->prev->rct.right;	// 上一个字符的 x 坐标
	if (GetLinePos(pnew, &line_pos))	// 获取当前行高，判断是否需要换行
	{
		// 若需要换行，则增加当前行高，设置 x 偏移为 0
		line_pos += chSize.cy;
		x_pos = 0;
	}

	// 得到字符区域
	pnew->rct = { x_pos,line_pos - chSize.cy,x_pos + chSize.cx,line_pos };

	// 调整行占位符位置（临时位置）
	if (pnew->tch == _T('\n'))
	{
		pnew->next->rct = {
			pnew->rct.left,pnew->rct.top,
			pnew->rct.left,pnew->rct.bottom
		};
	}

	// 调整字符位置
	if (pnew->next)
	{
		ConnectFollowChar(pnew);
	}

	// 记录光标 x 位置
	UpdateCursorXPos(&m_cursors);
}

void Edit::ResetCursorSelState(CursorGroup* pcg)
{
	pcg->selecting = false;
	pcg->pBehind = &pcg->end;
	pcg->pFront = &pcg->end;
}

void Edit::SortCursors(CursorGroup* pcg)
{
	// 从 begin 向下搜，如果搜到，就说明在前的（远离头结点的）是 end
	Char* p = pcg->begin.pos;
	while (p)
	{
		if (p == pcg->end.pos)
		{
			pcg->pFront = &pcg->end;
			pcg->pBehind = &pcg->begin;
			break;
		}
		p = p->next;
	}

	// 没搜到，说明在前的是 begin
	if (!p)
	{
		pcg->pFront = &pcg->begin;
		pcg->pBehind = &pcg->end;
	}
}

void Edit::UpdateCursorXPos(CursorGroup* pcg)
{
	pcg->x_pos = m_cursors.end.pos->rct.right;
}

Edit::Char* Edit::GetMouseChar(int x, int y)
{
	int line_num = y / m_standard_char_height + 1;	// 理论上鼠标所在行
	Char* begin = &m_head;		// 行头
	Char* end = nullptr;		// 行尾
	Char* p = &m_head;			// 搜索索引
	int sum = 0;				// 行计数
	while (sum != line_num)		// 搜行
	{
		if (p->tch == _T('\n'))	// 遇到换行，做标记
		{
			if (end)			// 第一个换行无需标记 begin
				begin = end;	// 之后，每次遇到一个新的换行，begin 接替 end，end 设为新的换行
			end = p;
			sum++;
		}
		if (!p->next)			// 搜到底了，行数不够
		{
			if (end)			// 如果在第一行内，不改变 begin，否则 begin 接替 end
				begin = end;
			end = p;			// end 设为底部，完成搜行
			break;
		}
		else
			p = p->next;
	}
	if (begin != &m_head)		// 将 begin 从换行符移动到行占位符
		begin = begin->next;

	Char* pf = begin;				// 搜索索引
	Char* best = begin;				// 最佳匹配
	int best_distance = INT_MAX;	// 最佳匹配的距离
	while (pf && pf != end->next)	// 从 begin 搜到 end
	{
		if (pf->tch != _T('\n'))
		{
			int distance = abs(pf->rct.right - x);
			if (distance < best_distance)
			{
				best = pf;
				best_distance = distance;
			}
		}
		pf = pf->next;
	}

	return best;
}

bool Edit::IsSeparation(Char* pc)
{
	switch (pc->tch)
	{
	case _T(' '):	case _T('('):	case _T(')'):	case _T(';'):
	case _T('['):	case _T(']'):	case _T('{'):	case _T('}'):
	case _T('='):	case _T('+'):	case _T('-'):	case _T('~'):
	case _T('%'):	case _T('^'):	case _T('&'):	case _T('*'):
	case _T(':'):	case _T('/'):	case _T('<'):	case _T('>'):
	case _T('.'):	case _T(','):	case _T('|'):	case _T('\\'):
	case _T('?'):	case _T('#'):	case _T('@'):	case _T('$'):
	case _T('"'):	case _T('\''):
	case _T('\n'):	case _T('\0'):	case _T('\t'):
		return true;
		break;
	default:
		return false;
		break;
	}
}

void Edit::GetWordFromChar(Char* pc, Char*& word_begin, Char*& word_end)
{
	Char* begin = pc;
	Char* end = pc;

	// 自身是分隔符，无法找单词
	if (IsSeparation(pc))
	{
		word_begin = pc;
		word_end = pc;
		return;
	}

	// 向前找头
	while (begin && begin != &m_head)
	{
		if (IsSeparation(begin))
			break;
		begin = begin->prev;
	}
	if (begin->next)
		begin = begin->next;

	// 向后找尾
	while (end && end != &m_head)
	{
		if (IsSeparation(end))
		{
			if (end->prev != &m_head)
				end = end->prev;
			break;
		}
		if (end->next)
			end = end->next;
		else
			break;
	}

	word_begin = begin;
	word_end = end;
}

void Edit::HighLight()
{
	COLORREF my_blue = RGB(56, 134, 213);
	COLORREF deep_blue = RGB(67, 182, 243);

	std::map<std::wstring, COLORREF> key_map = {
		{L"int", my_blue},
		{L"char", my_blue},
		{L"float", my_blue},
		{L"double", my_blue},
		{L"bool", my_blue},
		{L"void", my_blue},
		{L"auto", my_blue},
		{L"true", deep_blue},
		{L"false", deep_blue},
		{L"return", RED},
		{L"if", RED},
		{L"else", RED},
		{L"while", RED},
		{L"for", RED},
	};

	// 清空着色
	Char* pa = m_head.next;
	while (pa)
	{
		pa->text_color = BLACK;
		pa = pa->next;
	}

	Char* begin = nullptr;
	Char* end = nullptr;
	Char* p = &m_head;
	while (p && p->next)
	{
		p = p->next;

		if (IsSeparation(p))
			continue;

		GetWordFromChar(p, begin, end);

		// 匹配关键字
		std::wstring word = GetText(begin, end);
		for (auto key : key_map)
		{
			if (key.first == word)
			{
				// 高亮关键字
				Char* p = begin;
				while (p != end->next)
				{
					p->text_color = key.second;
					p = p->next;
				}
				break;
			}
		}

		p = end;
	}
}

void Edit::Render()
{
	setbkcolor(WHITE);
	cleardevice();

	//system("cls");
	int i = 0;

	Char* pc = &m_head;		// 当前字符
	bool inSel = false;		// 是否在选区中
	while (pc)
	{
		//printf("[%d] '%lc' %s\n", i++, pc->tch, pc == m_cursors.end.pos ? "<<" : "");

		// 判断选区状态
		if (m_cursors.selecting)
		{
			if (pc == m_cursors.pBehind->pos->next)
			{
				inSel = true;
			}
			if (pc == m_cursors.pFront->pos->next)
			{
				inSel = false;
			}
		}

		// 设置颜色

		if (inSel)
		{
			//settextcolor(WHITE);
			//setbkcolor(BLUE);
			setbkcolor(RGB(157, 213, 234));
		}
		else
		{
			settextcolor(BLACK);
			setbkcolor(WHITE);
		}

		switch (pc->tch)
		{
		case _T('\n'):
		case _T('\t'):
			break;

		default:
			settextcolor(pc->text_color);
			outtextxy(pc->rct.left, pc->rct.top, pc->tch);
			break;
		}

		pc = pc->next;
	}

	// 绘制光标
	RECT rctCursor = m_cursors.end.pos->rct;
	if (rctCursor.right == 0)	// 光标碰壁，需要外移
		rctCursor.right = m_cursors.width;

	if (m_cursors.show)
	{
		setfillcolor(RGB(150, 150, 150));
		solidrectangle(rctCursor.right - m_cursors.width, rctCursor.top, rctCursor.right, rctCursor.bottom);
	}

	FlushBatchDraw();
}

Edit::Edit()
{
	m_cursors.timer = clock();								// 计时
	m_cursors.end.pos = &m_head;							// 光标位置
	m_head.rct.bottom = m_standard_char_height;				// 设置一下头结点的高度，用于显示光标
}

void Edit::UpdateMessage(bool isNewMsg, ExMessage msg)
{
	clock_t now_time = clock();
	bool do_render = false;					// 是否渲染

	// 处理光标
	clock_t cursor_duration = now_time - m_cursors.timer;
	if (m_cursors.show && cursor_duration >= m_cursors.show_duration)
	{
		m_cursors.timer = now_time;
		m_cursors.show = false;
		do_render = true;
	}
	else if (!m_cursors.show && cursor_duration >= m_cursors.hide_duration)
	{
		m_cursors.timer = now_time;
		m_cursors.show = true;
		do_render = true;
	}

	// 有新消息
	if (isNewMsg)
	{
		switch (msg.message)
		{
			// 字符消息		
		case WM_CHAR:
		{
			// 退格
			if (msg.ch == _T('\b'))
			{
				Delete();
			}

			// 其他字符
			else
			{
				Insert(msg.ch);
			}

			HighLight();

			do_render = true;

			m_cursors.show = true;
			m_cursors.timer = now_time;
			break;
		}

		// 按键消息
		case WM_KEYDOWN:
		{
			// 是否移动了光标
			bool moved = false;

			// 键码
			switch (msg.vkcode)
			{
			case VK_SHIFT:
				m_cursors.selecting = true;
				m_cursors.begin = m_cursors.end;
				m_shift = true;
				break;

			case VK_UP:
				m_cursors.end.pos = GetValidUpOrDownChar(m_cursors.end.pos, m_cursors.x_pos, true);
				moved = true;
				break;

			case VK_DOWN:
				m_cursors.end.pos = GetValidUpOrDownChar(m_cursors.end.pos, m_cursors.x_pos, false);
				moved = true;
				break;

			case VK_LEFT:
				if (m_cursors.selecting && !m_shift)
					m_cursors.end.pos = m_cursors.pBehind->pos;
				else
					m_cursors.end.pos = GetValidLeftChar(m_cursors.end.pos);
				UpdateCursorXPos(&m_cursors);
				moved = true;
				break;

			case VK_RIGHT:
				if (m_cursors.selecting && !m_shift)
					m_cursors.end.pos = m_cursors.pFront->pos;
				else
					m_cursors.end.pos = GetValidRightChar(m_cursors.end.pos);
				UpdateCursorXPos(&m_cursors);
				moved = true;
				break;
			}

			// 选区状态下，需要对光标排序
			if ((moved && m_cursors.selecting) || msg.vkcode == VK_SHIFT)
			{
				// 未按下 shift 时移动光标，取消选择
				if (!m_shift)
					ResetCursorSelState(&m_cursors);
				else
					SortCursors(&m_cursors);
			}

			m_cursors.show = true;
			m_cursors.timer = now_time;
			do_render = true;

			break;
		}

		case WM_KEYUP:
		{
			switch (msg.vkcode)
			{
			case VK_SHIFT:
				m_shift = false;
				break;
			}

			break;
		}

		case WM_LBUTTONDOWN:
		{
			m_lbtn = true;

			ResetCursorSelState(&m_cursors);
			m_cursors.end.pos = GetMouseChar(msg.x, msg.y);
			m_cursors.selecting = true;
			m_cursors.begin = m_cursors.end;
			UpdateCursorXPos(&m_cursors);
			SortCursors(&m_cursors);

			m_cursors.show = true;
			m_cursors.timer = now_time;
			do_render = true;
			break;
		}

		case WM_MOUSEMOVE:
			if (m_lbtn)
			{
				m_cursors.end.pos = GetMouseChar(msg.x, msg.y);
				UpdateCursorXPos(&m_cursors);
				SortCursors(&m_cursors);

				m_cursors.show = true;
				m_cursors.timer = now_time;
				do_render = true;
			}
			break;

		case WM_LBUTTONUP:
			m_lbtn = false;
			break;

		case WM_LBUTTONDBLCLK:
		{
			Char* begin, * end;
			GetWordFromChar(m_cursors.end.pos, begin, end);

			// 近光标需要提前选择一个字符
			if (begin->prev)
				begin = begin->prev;

			ResetCursorSelState(&m_cursors);
			m_cursors.selecting = true;
			m_cursors.begin.pos = begin;
			m_cursors.end.pos = end;
			m_cursors.pBehind = &m_cursors.begin;
			m_cursors.pFront = &m_cursors.end;
			UpdateCursorXPos(&m_cursors);

			m_cursors.show = true;
			m_cursors.timer = now_time;
			do_render = true;
			break;
		}

		}
	}

	if (do_render)
		Render();
}

std::wstring Edit::GetText(Char* begin, Char* end)
{
	std::wstring wstr;
	Char* p = begin ? begin : m_head.next;
	while (p)
	{
		if (end && p == end->next)
			break;
		if (p->tch)	// 排除行占位符
			wstr.push_back(p->tch);
		p = p->next;
	}
	return wstr;
}
