#include "Canvas.h"
#include "SxLog.h"

static bool SxIsNoisyMsg(UINT m)
{
	return m == WM_MOUSEMOVE;
}

Canvas::Canvas()
	:Control(0, 0, 100, 100)
{
	this->id = "Canvas";
}

Canvas::Canvas(int x, int y, int width, int height)
	:Control(x, y, width, height)
{
	this->id = "Canvas";
}

void Canvas::setX(int x)
{
	this->x = x;
	for (auto& c : controls)
	{
		c->onWindowResize();
		c->setX(c->getLocalX() + this->x);
	}
	dirty = true;
}

void Canvas::setY(int y)
{
	this->y = y;
	for (auto& c : controls)
	{
		c->onWindowResize();
		c->setY(c->getLocalY() + this->y);
	}
	dirty = true;
}

void Canvas::clearAllControls()
{
	controls.clear();
}

void Canvas::draw()
{
	if (!dirty || !show)
	{
		for (auto& control : controls)
			if (auto c = dynamic_cast<Table*>(control.get()))
				c->draw();
		return;
	}
	saveStyle();
	setlinecolor(canvasBorderClor);//设置线色
	if (StellarX::FillMode::Null != canvasFillMode)
		setfillcolor(canvasBkClor);//设置填充色
	setfillstyle((int)canvasFillMode);//设置填充模式
	setlinestyle((int)canvasLineStyle, canvaslinewidth);

	// 在绘制画布之前，先恢复并更新背景快照：
	// 1. 如果已有快照，则先回贴旧快照以清除之前的内容。
	// 2. 当坐标或尺寸变化，或缓存图像无效时，丢弃旧快照并重新抓取新的背景。
	int margin = canvaslinewidth > 1 ? canvaslinewidth : 1;
	if (hasSnap)
	{
		// 恢复旧快照，清除上一次绘制
		restBackground();
		// 如果位置或尺寸变了，或没有有效缓存，则重新抓取
		if (!saveBkImage || saveBkX != this->x - margin || saveBkY != this->y - margin || saveWidth != this->width + margin * 2 || saveHeight != this->height + margin * 2)
		{
			discardBackground();
			saveBackground(this->x - margin, this->y - margin, this->width + margin * 2, this->height + margin * 2);
		}
	}
	else
	{
		// 首次绘制或没有快照时直接抓取背景
		saveBackground(this->x - margin, this->y - margin, this->width + margin * 2, this->height + margin * 2);
	}
	// 再次恢复最新快照，确保绘制区域干净
	restBackground();
	//根据画布形状绘制
	switch (shape)
	{
	case StellarX::ControlShape::RECTANGLE:
		fillrectangle(x, y, x + width, y + height);//有边框填充矩形
		break;
	case StellarX::ControlShape::B_RECTANGLE:
		solidrectangle(x, y, x + width, y + height);//无边框填充矩形
		break;
	case StellarX::ControlShape::ROUND_RECTANGLE:
		fillroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//有边框填充圆角矩形
		break;
	case StellarX::ControlShape::B_ROUND_RECTANGLE:
		solidroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//无边框填充圆角矩形
		break;
	}
	// 绘制所有子控件
	for (auto& control : controls)
	{
		control->setDirty(true);
		control->draw();
	}

	restoreStyle();
	dirty = false;	 //标记画布不需要重绘
}

bool Canvas::handleEvent(const ExMessage& msg)
{
	if (!show) return false;

	bool consumed = false;
	bool anyDirty = false;
	Control* firstConsumer = nullptr;

	for (auto it = controls.rbegin(); it != controls.rend(); ++it)
	{
		Control* c = it->get();
		bool cConsumed = c->handleEvent(msg);

		if (cConsumed && !firstConsumer) firstConsumer = c;
		consumed |= cConsumed;

		if (c->isDirty()) anyDirty = true;
	}

	if (firstConsumer && !SxIsNoisyMsg(msg.message))
	{
		SX_LOGD("Event") << SX_T("Canvas 消耗消息: ","Canvas consumed: msg=") << msg.message
			<< SX_T("子控件"," by child")<<" id=" << firstConsumer->getId();
	}

	if (anyDirty)
	{
		if (!SxIsNoisyMsg(msg.message))
			SX_LOGD("Dirty") << SX_T("Canvas检测有控件为脏状态 -> 请求重绘, ","Canvas anyDirty -> requestRepaint, ")<<"id = " << id;
		requestRepaint(parent);
	}

	return consumed;
}


void Canvas::addControl(std::unique_ptr<Control> control)
{

	//坐标转化
	control->setX(control->getLocalX() + this->x);
	control->setY(control->getLocalY() + this->y);
	control->setParent(this);
	SX_LOGI("Canvas")
		<< SX_T("添加子控件：父=Canvas 子id=", "addControl: parent=Canvas childId=")
		<< control->getId()
		<< SX_T(" 相对坐标=(", " local=(")
		<< control->getLocalX() << "," << control->getLocalY()
		<< SX_T(") 绝对坐标=(", ") abs=(")
		<< control->getX() << "," << control->getY()
		<< ")";


	controls.push_back(std::move(control));
	dirty = true;
}

void Canvas::setShape(StellarX::ControlShape shape)
{
	switch (shape)
	{
	case StellarX::ControlShape::RECTANGLE:
	case StellarX::ControlShape::B_RECTANGLE:
	case StellarX::ControlShape::ROUND_RECTANGLE:
	case StellarX::ControlShape::B_ROUND_RECTANGLE:
		this->shape = shape;
		dirty = true;
		break;
	case StellarX::ControlShape::CIRCLE:
	case StellarX::ControlShape::B_CIRCLE:
	case StellarX::ControlShape::ELLIPSE:
	case StellarX::ControlShape::B_ELLIPSE:
		this->shape = StellarX::ControlShape::RECTANGLE;
		dirty = true;
		break;
	}
}

void Canvas::setCanvasfillMode(StellarX::FillMode mode)
{
	this->canvasFillMode = mode;
	dirty = true;
}

void Canvas::setBorderColor(COLORREF color)
{
	this->canvasBorderClor = color;
	dirty = true;
}

void Canvas::setCanvasBkColor(COLORREF color)
{
	this->canvasBkClor = color;
	dirty = true;
}

void Canvas::setCanvasLineStyle(StellarX::LineStyle style)
{
	this->canvasLineStyle = style;
	dirty = true;
}

void Canvas::setLinewidth(int width)
{
	this->canvaslinewidth = width;
	dirty = true;
}

void Canvas::setIsVisible(bool visible)
{
	this->show = visible;
	dirty = true;
	for (auto& control : controls)
	{
		control->setIsVisible(visible);
	}
	if (!visible)
		this->updateBackground();
}

void Canvas::setDirty(bool dirty)
{
	this->dirty = dirty;
	for (auto& control : controls)
		control->setDirty(dirty);
}

void Canvas::onWindowResize()
{
	// 首先处理自身的快照等逻辑
	Control::onWindowResize();

	// 记录父容器原始尺寸（用于计算子控件的右/下边距）
	int origParentW = this->localWidth;
	int origParentH = this->localHeight;

	// 当前容器的新尺寸
	int finalW = this->width;
	int finalH = this->height;

	// 当前容器的新坐标（全局坐标）
	int parentX = this->x;
	int parentY = this->y;

	// 调整每个子控件在 AnchorToEdges 模式下的位置与尺寸
	for (auto& ch : controls)
	{
		// Only adjust when using anchor-to-edges layout
		if (ch->getLayoutMode() == StellarX::LayoutMode::AnchorToEdges)
		{
			// Determine whether this child is a Table; tables keep their height constant
			bool isTable = (dynamic_cast<Table*>(ch.get()) != nullptr);

			// Unpack anchors
			auto a1 = ch->getAnchor_1();
			auto a2 = ch->getAnchor_2();

			bool anchorLeft = (a1 == StellarX::Anchor::Left || a2 == StellarX::Anchor::Left);
			bool anchorRight = (a1 == StellarX::Anchor::Right || a2 == StellarX::Anchor::Right);
			bool anchorTop = (a1 == StellarX::Anchor::Top || a2 == StellarX::Anchor::Top);
			bool anchorBottom = (a1 == StellarX::Anchor::Bottom || a2 == StellarX::Anchor::Bottom);

			// If it's a table, treat as anchored left and right horizontally and anchored top vertically by default.
			if (isTable)
			{
				anchorLeft = true;
				anchorRight = true;
				// If no explicit vertical anchor was provided, default to top.
				if (!(anchorTop || anchorBottom))
				{
					anchorTop = true;
				}
			}

			// Compute new X and width
			int newX = ch->getX();
			int newWidth = ch->getWidth();
			if (anchorLeft && anchorRight)
			{
				// Scale horizontally relative to parent's size.
				if (origParentW > 0)
				{
					// Maintain proportional position and size based on original local values.
					double scaleW = static_cast<double>(finalW) / static_cast<double>(origParentW);
					newX = parentX + static_cast<int>(ch->getLocalX() * scaleW + 0.5);
					newWidth = static_cast<int>(ch->getLocalWidth() * scaleW + 0.5);
				}
				else
				{
					// Fallback: keep original
					newX = parentX + ch->getLocalX();
					newWidth = ch->getLocalWidth();
				}
			}
			else if (anchorLeft && !anchorRight)
			{
				// Only left anchored: keep original width and left margin.
				newWidth = ch->getLocalWidth();
				newX = parentX + ch->getLocalX();
			}
			else if (!anchorLeft && anchorRight)
			{
				// Only right anchored: keep original width and right margin.
				newWidth = ch->getLocalWidth();
				int origRightDist = origParentW - (ch->getLocalX() + ch->getLocalWidth());
				newX = parentX + finalW - origRightDist - newWidth;
			}
			else
			{
				// No horizontal anchor: position relative to parent's left and width unchanged.
				newWidth = ch->getLocalWidth();
				newX = parentX + ch->getLocalX();
			}
			ch->setX(newX);
			ch->setWidth(newWidth);

			// Compute new Y and height
			int newY = ch->getY();
			int newHeight = ch->getHeight();
			if (isTable)
			{
				// Table: Height remains constant; adjust Y based on anchors.
				newHeight = ch->getLocalHeight();
				if (anchorTop && anchorBottom)
				{
					// If both top and bottom anchored, scale Y but keep height.
					if (origParentH > 0)
					{
						double scaleH = static_cast<double>(finalH) / static_cast<double>(origParentH);
						newY = parentY + static_cast<int>(ch->getLocalY() * scaleH + 0.5);
					}
					else
					{
						newY = parentY + ch->getLocalY();
					}
				}
				else if (anchorTop && !anchorBottom)
				{
					// Top anchored only
					newY = parentY + ch->getLocalY();
				}
				else if (!anchorTop && anchorBottom)
				{
					// Bottom anchored only
					int origBottomDist = origParentH - (ch->getLocalY() + ch->getLocalHeight());
					newY = parentY + finalH - origBottomDist - newHeight;
				}
				else
				{
					// No vertical anchor: default to top
					newY = parentY + ch->getLocalY();
				}
			}
			else
			{
				if (anchorTop && anchorBottom)
				{
					// Scale vertically relative to parent's size.
					if (origParentH > 0)
					{
						double scaleH = static_cast<double>(finalH) / static_cast<double>(origParentH);
						newY = parentY + static_cast<int>(ch->getLocalY() * scaleH + 0.5);
						newHeight = static_cast<int>(ch->getLocalHeight() * scaleH + 0.5);
					}
					else
					{
						newY = parentY + ch->getLocalY();
						newHeight = ch->getLocalHeight();
					}
				}
				else if (anchorTop && !anchorBottom)
				{
					// Top anchored only: keep height constant
					newHeight = ch->getLocalHeight();
					newY = parentY + ch->getLocalY();
				}
				else if (!anchorTop && anchorBottom)
				{
					// Bottom anchored only: keep height and adjust Y relative to bottom
					newHeight = ch->getLocalHeight();
					int origBottomDist = origParentH - (ch->getLocalY() + ch->getLocalHeight());
					newY = parentY + finalH - origBottomDist - newHeight;
				}
				else
				{
					// No vertical anchor: position relative to parent's top, height constant.
					newHeight = ch->getLocalHeight();
					newY = parentY + ch->getLocalY();
				}
			}
			ch->setY(newY);
			ch->setHeight(newHeight);
		}
		// Always forward the window resize event to the child (recursively).
		ch->onWindowResize();
	}
}

void Canvas::requestRepaint(Control* parent)
{
	if (this == parent)
	{
		if (!show)
			return;

		// 关键护栏：
		// - Canvas 自己是脏的 / 没有快照 / 缓存图为空
		//   => 禁止局部重绘，直接升级为一次完整 draw（先把 dirty 置真，避免 draw() 早退）
		if (dirty || !hasSnap || !saveBkImage)
		{
			SX_LOGD("Dirty")
				<< SX_T("Canvas 局部重绘降级为全量重绘: id=", "Canvas partial->full draw: id=")
				<< id
				<< " dirty=" << (dirty ? 1 : 0)
				<< " hasSnap=" << (hasSnap ? 1 : 0);

			this->dirty = true;
			this->draw();
			return;
		}

		SX_LOGD("Dirty") << SX_T("Canvas 请求局部重绘：id=", "Canvas::requestRepaint(partial): id=") << id;

		for (auto& control : controls)
			if (control->isDirty() && control->IsVisible())
				control->draw();

		return;
	}

	SX_LOGD("Dirty") << SX_T("Canvas 请求根级重绘：id=", "Canvas::requestRepaint(root): id=") << id;
	onRequestRepaintAsRoot();
}

