local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Packages = Rojo.Packages

local Roact = require(Packages.Roact)

local PatchTree = require(Plugin.PatchTree)
local PatchSet = require(Plugin.PatchSet)

local Theme = require(Plugin.App.Theme)
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
local VirtualScroller = require(Plugin.App.Components.VirtualScroller)

local e = Roact.createElement

local DomLabel = require(script.DomLabel)

local PatchVisualizer = Roact.Component:extend("PatchVisualizer")

function PatchVisualizer:init()
	self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))

	self.updateEvent = Instance.new("BindableEvent")
end

function PatchVisualizer:willUnmount()
	self.updateEvent:Destroy()
end

function PatchVisualizer:shouldUpdate(nextProps)
	if self.props.patchTree ~= nextProps.patchTree then
		return true
	end

	local currentPatch, nextPatch = self.props.patch, nextProps.patch
	if currentPatch ~= nil or nextPatch ~= nil then
		return not PatchSet.isEqual(currentPatch, nextPatch)
	end

	return false
end

function PatchVisualizer:render()
	local patchTree = self.props.patchTree
	if patchTree == nil and self.props.patch ~= nil then
		patchTree = PatchTree.build(
			self.props.patch,
			self.props.instanceMap,
			self.props.changeListHeaders or { "Property", "Current", "Incoming" }
		)
		if self.props.unappliedPatch then
			patchTree =
				PatchTree.updateMetadata(patchTree, self.props.patch, self.props.instanceMap, self.props.unappliedPatch)
		end
	end

	-- Recusively draw tree
	local scrollElements, elementHeights = {}, {}

	if patchTree then
		local function drawNode(node, depth)
			local elementHeight, setElementHeight = Roact.createBinding(30)
			table.insert(elementHeights, elementHeight)
			table.insert(
				scrollElements,
				e(DomLabel, {
					updateEvent = self.updateEvent,
					elementHeight = elementHeight,
					setElementHeight = setElementHeight,
					patchType = node.patchType,
					className = node.className,
					isWarning = node.isWarning,
					instance = node.instance,
					name = node.name,
					hint = node.hint,
					changeList = node.changeList,
					depth = depth,
					transparency = self.props.transparency,
					showSourceDiff = self.props.showSourceDiff,
				})
			)
		end

		patchTree:forEach(function(node, depth)
			drawNode(node, depth)
		end)
	end

	return Theme.with(function(theme)
		return e(BorderedContainer, {
			transparency = self.props.transparency,
			size = self.props.size,
			position = self.props.position,
			layoutOrder = self.props.layoutOrder,
		}, {
			CleanMerge = e("TextLabel", {
				Visible = #scrollElements == 0,
				Text = "No changes to sync, project is up to date.",
				Font = Enum.Font.GothamMedium,
				TextSize = 15,
				TextColor3 = theme.Settings.Setting.DescriptionColor,
				TextWrapped = true,
				Size = UDim2.new(1, 0, 1, 0),
				BackgroundTransparency = 1,
			}),

			VirtualScroller = e(VirtualScroller, {
				size = UDim2.new(1, 0, 1, 0),
				transparency = self.props.transparency,
				count = #scrollElements,
				updateEvent = self.updateEvent.Event,
				render = function(i)
					return scrollElements[i]
				end,
				getHeightBinding = function(i)
					return elementHeights[i]
				end,
			}),
		})
	end)
end

return PatchVisualizer
