local _, addon = ...
local API = addon.API;
local ControlCenter = addon.ControlCenter;


local GetCursorPosition = GetCursorPosition;


local ArrowButtonMixin = {};
do
	function ArrowButtonMixin:OnLoad()
		self.OnLoad = nil;

		self:SetScript("OnMouseDown", self.OnMouseDown);
		self:SetScript("OnMouseUp", self.OnMouseUp);
		self:SetScript("OnEnable", self.OnEnable);
		self:SetScript("OnDisable", self.OnDisable);
		self:SetScript("OnEnter", self.OnEnter);
		self:SetScript("OnLeave", self.OnLeave);

		self.Highlight:SetAlpha(0.5);
	end

	function ArrowButtonMixin:OnMouseDown(button)
		if not self:IsEnabled() then return end;
		if button == "LeftButton" then
			self.Highlight:SetAlpha(1);
			self:GetParent().ScrollView:OnMouseWheel(self.delta);
			self:GetParent():StartPushingArrow(self.delta);
			addon.LandingPageUtil.PlayUISound("ScrollBarStep");
		end
	end

	function ArrowButtonMixin:OnMouseUp()
		self.Highlight:SetAlpha(0.5);
		self:GetParent():StopUpdating();
		self:GetParent().ScrollView:StopSteadyScroll();
	end

	function ArrowButtonMixin:OnEnable()
		self.Texture:SetVertexColor(1, 1, 1);
		self.Texture:SetDesaturated(false);
	end

	function ArrowButtonMixin:OnDisable()
		self.Texture:SetVertexColor(0.5, 0.5, 0.5);
		self.Texture:SetDesaturated(true);
	end

	function ArrowButtonMixin:OnEnter()
		self:GetParent():UpdateVisual();
	end

	function ArrowButtonMixin:OnLeave()
		self:GetParent():UpdateVisual();
	end
end


local ThumbButtonMixin = {};
do
	function ThumbButtonMixin:OnLoad()
		self.OnLoad = nil;

		self:SetScript("OnMouseDown", self.OnMouseDown);
		self:SetScript("OnMouseUp", self.OnMouseUp);
		self:SetScript("OnEnter", self.OnEnter);
		self:SetScript("OnLeave", self.OnLeave);
		self:SetScript("OnHide", self.OnHide);

		self.HighlightTop:SetAlpha(0.2);
		self.HighlightMiddle:SetAlpha(0.2);
		self.HighlightBottom:SetAlpha(0.2);
	end

	function ThumbButtonMixin:OnMouseDown(button)
		if button == "LeftButton" then
			self.HighlightTop:SetAlpha(0.5);
			self.HighlightMiddle:SetAlpha(0.5);
			self.HighlightBottom:SetAlpha(0.5);
			self:GetParent():StartDraggingThumb();
			self:LockHighlight();
			addon.LandingPageUtil.PlayUISound("ScrollBarThumbDown");
		end
	end

	function ThumbButtonMixin:OnMouseUp(button)
		if button == "LeftButton" then
			self.HighlightTop:SetAlpha(0.2);
			self.HighlightMiddle:SetAlpha(0.2);
			self.HighlightBottom:SetAlpha(0.2);
			self:UnlockHighlight();
			self:GetParent():StopUpdating();
			self:GetParent():UpdateVisual();
		end
	end

	local function Thumb_Expand_OnUpdate(self, elapsed)
		self.capSize = self.capSize + 128 * elapsed;
		if self.capSize >= 16 then
			self.capSize = 16;
			self:SetScript("OnUpdate", nil);
		end
		self.Top:SetSize(self.capSize, self.capSize);
		self.Bottom:SetSize(self.capSize, self.capSize);
	end

	local function Thumb_Shrink_OnUpdate(self, elapsed)
		self.capSize = self.capSize - 128 * elapsed;
		if self.capSize <= 16 then
			self.capSize = 16;
			self:SetScript("OnUpdate", nil);
		end
		self.Top:SetSize(self.capSize, self.capSize);
		self.Bottom:SetSize(self.capSize, self.capSize);
	end

	function ThumbButtonMixin:Expand()
		self.expanded = true;
		self.Top:SetTexCoord(0/512, 32/512, 264/512, 296/512);
		self.Middle:SetTexCoord(0/512, 32/512, 296/512, 360/512);
		self.Bottom:SetTexCoord(0/512, 32/512, 360/512, 392/512);
		self.HighlightTop:SetTexCoord(0/512, 32/512, 264/512, 296/512);
		self.HighlightMiddle:SetTexCoord(0/512, 32/512, 296/512, 360/512);
		self.HighlightBottom:SetTexCoord(0/512, 32/512, 360/512, 392/512);

		self.capSize = self.Top:GetWidth() * 0.5;
		self:SetScript("OnUpdate", Thumb_Expand_OnUpdate);
		Thumb_Expand_OnUpdate(self, 0);
	end

	function ThumbButtonMixin:Shrink()
		self.Top:SetTexCoord(0/512, 32/512, 132/512, 164/512);
		self.Middle:SetTexCoord(0/512, 32/512, 164/512, 228/512);
		self.Bottom:SetTexCoord(0/512, 32/512, 228/512, 260/512);
		self.HighlightTop:SetTexCoord(0/512, 32/512, 132/512, 164/512);
		self.HighlightMiddle:SetTexCoord(0/512, 32/512, 164/512, 228/512);
		self.HighlightBottom:SetTexCoord(0/512, 32/512, 228/512, 260/512);

		self.capSize = self.Top:GetWidth() * 2;
		self:SetScript("OnUpdate", Thumb_Shrink_OnUpdate);
		Thumb_Shrink_OnUpdate(self, 0);
	end

	function ThumbButtonMixin:OnEnter()
		self:GetParent():UpdateVisual();
	end

	function ThumbButtonMixin:OnLeave()
		self:GetParent():UpdateVisual();
	end

	function ThumbButtonMixin:OnHide()
		self:OnMouseUp("LeftButton");
	end
end


local ScrollBarMixin = {};
do
	function ScrollBarMixin:OnLoad()
		self.OnLoad = nil;

		Mixin(self.UpArrow, ArrowButtonMixin);
		Mixin(self.DownArrow, ArrowButtonMixin);
		Mixin(self.Thumb, ThumbButtonMixin);
		self.UpArrow:OnLoad();
		self.UpArrow.delta = 1;
		self.DownArrow:OnLoad();
		self.DownArrow.delta = -1;
		self.Thumb:OnLoad();

		self.textureObjects = {};

		local function AddTexture(obj)
			self.textureObjects[obj] = true;
		end

		local function SetTexCoord(obj, x1, x2, y1, y2)
			AddTexture(obj);
			obj:SetTexCoord(x1/512, x2/512, y1/512, y2/512);
		end

		SetTexCoord(self.Rail.Top, 0, 32, 0, 32);
		SetTexCoord(self.Rail.Middle, 0, 32, 32, 96);
		SetTexCoord(self.Rail.Bottom, 0, 32, 96, 128);

		SetTexCoord(self.Thumb.Top, 0, 32, 132, 164);
		SetTexCoord(self.Thumb.HighlightTop, 0, 32, 132, 164);
		SetTexCoord(self.Thumb.Middle, 0, 32, 164, 228);
		SetTexCoord(self.Thumb.HighlightMiddle, 0, 32, 164, 228);
		SetTexCoord(self.Thumb.Bottom, 0, 32, 228, 260);
		SetTexCoord(self.Thumb.HighlightBottom, 0, 32, 228, 260);

		SetTexCoord(self.UpArrow.Texture, 0, 32, 396, 428);
		SetTexCoord(self.UpArrow.Highlight, 0, 32, 396, 428);
		SetTexCoord(self.DownArrow.Texture, 0, 32, 428, 460);
		SetTexCoord(self.DownArrow.Highlight, 0, 32, 428, 460);


		self.Rail:SetScript("OnMouseDown", function(_, button)
			if button == "LeftButton" then
				self:ScrollToMouseDownPosition();
				addon.LandingPageUtil.PlayUISound("ScrollBarThumbDown");
			end
		end);

		self.Rail:SetScript("OnEnter", function()
			self:UpdateVisual();
		end);

		self.Rail:SetScript("OnLeave", function()
			self:UpdateVisual();
		end);
	end

	function ScrollBarMixin:SetTexure(texture)
		for obj in pairs(self.textureObjects) do
			obj:SetTexture(texture);
		end
	end

	function ScrollBarMixin:ShowArrowButtons(showArrows)
		if showArrows == self.showArrows then return end;
		self.showArrows = showArrows;

		self.UpArrow:SetShown(showArrows);
		self.DownArrow:SetShown(showArrows);

		local offsetY;
		if showArrows then
			offsetY = 16;
		else
			offsetY = 0;
		end

		self.Rail:ClearAllPoints();
		self.Rail:SetPoint("TOP", self, "TOP", 0, -offsetY);
		self.Rail:SetPoint("BOTTOM", self, "BOTTOM", 0, offsetY);
	end

	function ScrollBarMixin:SetVisibleExtentPercentage(ratio)
		local height = API.Round(ratio * self.Rail:GetHeight());
		if height < 32 then
			height = 32;
		end
		self.Thumb:SetHeight(height);
	end

	function ScrollBarMixin:UpdateVisibleExtentPercentage()
		local range = self.ScrollView:GetScrollRange();
		local viewHeight = self.ScrollView:GetHeight();
		local ratio = viewHeight / (viewHeight + range);
		if ratio > 0.95 then
			ratio = 0.95;
		end
		self:SetVisibleExtentPercentage(ratio);
		self:UpdateThumbRange();
		self:SetValueByRatio(self.ratio or 0);
	end

	function ScrollBarMixin:OnSizeChanged()
		self:UpdateVisibleExtentPercentage();
	end

	function ScrollBarMixin:SetValueByRatio(ratio)
		if ratio < 0.001 then
			ratio = 0;
			self.isTop = true;
			self.isBottom = false;
		elseif ratio > 0.999 then
			ratio = 1;
			self.isTop = false;
			self.isBottom = true;
		else
			self.isTop = false;
			self.isBottom = false;
		end

		if self.isTop then
			self.UpArrow:Disable();
		else
			self.UpArrow:Enable();
		end

		if self.isBottom then
			self.DownArrow:Disable();
		else
			self.DownArrow:Enable();
		end

		self.ratio = ratio;
		self.Thumb:SetPoint("TOP", self.Rail, "TOP", 0, -ratio * self.thumbRange);
	end

	function ScrollBarMixin:UpdateThumbRange()
		local railLength = self.Rail:GetHeight();
		local range = API.Round(railLength - self.Thumb:GetHeight());
		self.thumbRange = range;
		if range > 0 then
			self.ratioPerUnit = 1 / range;
		else
			self.ratioPerUnit = 1;
		end
	end

	function ScrollBarMixin:SetScrollable(scrollable)
		if scrollable then
			self.Thumb:Show();
			self.UpArrow:Show();
			self.DownArrow:Show();
			self.isTop = self.ScrollView:IsAtTop();
			self.isBottom = self.ScrollView:IsAtBottom();
			self:SetAlpha(1);
		else
			self.Thumb:Hide();
			self.UpArrow:Hide();
			self.DownArrow:Hide();
			self.isTop = true;
			self.isBottom = true;
			self:SetAlpha(0.5);
		end
		self.scrollable = scrollable;
		self.UpArrow:SetEnabled(not self.isTop);
		self.DownArrow:SetEnabled(not self.isBottom);
		self:UpdateThumbRange();
	end

	function ScrollBarMixin:UpdateVisual()
		if self.Rail:IsMouseMotionFocus() or self.Thumb:IsMouseMotionFocus() or self:IsDraggingThumb() then
			if not self.expanded then
				self.expanded = true;
				self.Thumb:Expand();
			end
		else
			if self.expanded then
				self.expanded = nil;
				self.Thumb:Shrink();
			end
		end
	end

	function ScrollBarMixin:StartDraggingThumb()
		self:Snapshot();
		self:UpdateThumbRange();
		self.t = 0;
		self.isDraggingThumb = true;
		self:SetScript("OnUpdate", self.OnUpdate_ThumbDragged);
	end

	function ScrollBarMixin:IsDraggingThumb()
		return self.isDraggingThumb
	end

	function ScrollBarMixin:OnUpdate_ThumbDragged(elapsed)
		self.x, self.y = GetCursorPosition();
		self.x = self.x / self.scale;
		self.y = self.y / self.scale;
		self.dx = self.x - self.x0;
		self.dy = self.y - self.y0;
		self:SetValueByRatio(self.fromRatio - self.dy * self.ratioPerUnit);
		self.ScrollView:SnapToRatio(self.ratio);
	end

	function ScrollBarMixin:Snapshot()
		self.x0, self.y0 = GetCursorPosition();
		self.scale = self:GetEffectiveScale();
		self.x0 = self.x0 / self.scale;
		self.y0 = self.y0 / self.scale;
		self.fromRatio = self.ratio;
	end

	function ScrollBarMixin:StartPushingArrow(delta)
		self:Snapshot();
		self:UpdateThumbRange();
		self.t = 0;
		self.delta = delta or -1;
		self:SetScript("OnUpdate", self.OnUpdate_ArrowPushed);
	end

	function ScrollBarMixin:OnUpdate_ArrowPushed(elapsed)
		self.t = self.t + elapsed;
		if self.t > 0.3 then
			self.t = 0;
			self.ScrollView:SteadyScroll(-self.delta);
		end
	end

	function ScrollBarMixin:StopUpdating()
		self:SetScript("OnUpdate", nil);
		self.t = nil;
		self.x, self.y = nil, nil;
		self.x0, self.y0 = nil, nil;
		self.dx, self.dy = nil, nil;
		self.scale = nil;
		self.isDraggingThumb = nil;
	end

	function ScrollBarMixin:ScrollToMouseDownPosition()
		local x, y = GetCursorPosition();
		local scale = self:GetEffectiveScale();
		x, y = x/scale, y/scale;

		local top = self.Rail:GetTop();
		local bottom = self.Rail:GetBottom();

		local ratio;
		if (top - y) < 4 then
			ratio = 0;
		elseif (y - bottom) < 4 then
			ratio = 1;
		else
			ratio = (y - top)/(bottom - top);
		end

		self.ScrollView:ScrollToRatio(ratio);
	end

	function ScrollBarMixin:GetScrollView()
		return self.ScrollView
	end
end


local DebugScrollView = {};
do
	function DebugScrollView:ScrollToRatio()

	end

	function DebugScrollView:SnapToRatio()

	end

	function DebugScrollView:OnMouseWheel()

	end

	function DebugScrollView:IsAtTop()

	end

	function DebugScrollView:IsAtBottom()

	end

	function DebugScrollView:SteadyScroll()

	end

	function DebugScrollView:StopSteadyScroll()

	end
end


local function CreateScrollBarWithDynamicSize(parent)
	local f = CreateFrame("Frame", nil, parent, "PlumberScrollBarWithDynamicSizeTemplate");

	Mixin(f, ScrollBarMixin);
	f:OnLoad();
	f:SetTexure("Interface/AddOns/Plumber/Art/ControlCenter/SettingsPanelWidget.png");
	f:ShowArrowButtons(true);
	f:UpdateThumbRange();
	f:SetValueByRatio(0);

	f.ScrollView = DebugScrollView;

	return f
end
ControlCenter.CreateScrollBarWithDynamicSize = CreateScrollBarWithDynamicSize;
