local linkMat = mat4()
local panel
if ctx then
	panel = ctx.planPanel
end
local function invertColor(color)
	local backDark = mean(color) < 0.6 and 0.6 or -0.4
	return contrast(
		{
			r = mc(0, 1 - color.r + backDark, 1),
			g = mc(0, 1 - color.g + backDark, 1),
			b = mc(0, 1 - color.b + backDark, 1),
		},
		0
	)
end

local function cacheLinkPreview()
	editor.roomLinkPreview.canvas = gfx.copyCanvas(
		engine.canvas,
		{ w = editor.roomLinkPreview.w - 4, h = editor.roomLinkPreview.h - 4, filter = "linear" }
	)
end

local function linkCached(port)
	return port and port.linkID and room.links[port.linkID] and room.links[port.linkID].cached
end

local function drawRoomLink(room, port)
	if port.linkID and not port.link.cached and lfs.exists("cache/" .. port.linkID .. ".png") then
		port.link.cached = gfx.newImage("cache/" .. port.linkID .. ".png")
	end

	if
		time - editor.startTime > 1
		and port.linkID
		and ctx.roomZoom == 0
		and (prevRoom ~= ctx.viewport.key or prevRoomZoom ~= 0 or not port.link.cached or port.link.cacheTime)
	then
		--DRAW ROOM TO LINK>TIP>CANVAS
		--port.link.cached = gfx.copyCanvas(temp)
	end

	if port.link.cached then
		--DRAW CACHED TO SCREEN??
	end

	if
		port.preview
		or (ctx.roomZoom > 0 and not port.link.cached)
		or port.link.cacheTime
		or (ctx.roomZoom == 0 and prevRoomZoom ~= 0)
		or (prevRoom ~= ctx.viewport.key)
	then
		--DRAW ROOM TO LINK>TIP>CANVAS
	end
end

local function dragItem(item)
	if item.selected then
		if panel.dragging then
			panel.somethingDragging = true
			if item.isWindow then
				item.target.ox = ((mouse.x - item.startDiff.x - screenW / 2) / panel.transform.scale)
					- panel.transform.x
				item.target.oy = (
					(mouse.y - item.startDiff.y + panel.titleHeight - screenH / 2) / panel.transform.scale
				) - panel.transform.y
			else
				item.x = easeTo(item.x, mouse.x - item.w / 2, 2)
				item.y = easeTo(item.y, mouse.y - panel.row / 2 + item.dragY, 2)
			end
		end
	end
end

local function resizePlan()
	panel.docked = false
	panel.edge = -2
	panel.width = screenW --panel.edge-zero(tracer.x+tracer.width+10)
	panel.height = screenH
	panel.h = panel.height
	panel.x = 0 --panel.edge/2+zero(tracer.x+tracer.width+10)
	panel.y = panel.edge / 2

	local pscale = panel.transform.scale
	panel.titleHeight = panel.showTitles and 8 or 0
	panel.somethingHovered = false
	panel.somethingDragging = false
	panel.detailFade = easeTo(panel.detailFade, panel.transform.scale < 0.7 and 0 or 1, 8)
	panel.winR = panel.transform.scale * panel.winW
	panel.scaleSpeed = panel.sidebar.hovered and 0 or mouse.scroll.y
	panel.transform.scale = mc(
		0,
		mc(
			panel.min.scale,
			panel.transform.scale
				+ lume.sign(panel.scaleSpeed) * (panel.scaleSpeed ^ 2) * 0.01 * mc(0.5, panel.transform.scale, 1),
			panel.max.scale
		),
		15
	)
	panel.transform.scaleN = utils.map(panel.transform.scale, panel.min.scale, panel.max.scale, 0, 1)
	panel.shifted = false
	panel.resize = (round(pscale, 2) ~= round(panel.transform.scale, 2)) and 2 or panel.resize - 1
end

local function setTextSize(sidebar)
	panel.lineFontSize = sidebar and 10 or mf(mc(6, (5 * panel.transform.scale) ^ 1.1, 11))
	panel.charW = 10 --fonts.mplus.sizes[panel.lineFontSize] and fonts.mplus.sizes[panel.lineFontSize]:getWidth("i") or panel.charW
	panel.charH = 10 --fonts.mplus.sizes[panel.lineFontSize] and fonts.mplus.sizes[panel.lineFontSize]:getHeight() or panel.charH
end

local function dragDialogue()
	for l = 1, panel.lineCount, 1 do
		if panel.lines[l] then
			dragItem(panel.lines[l])
			if panel.lines[l].hovered then
				mouse_set(function()
					select(panel.lines[l])
				end)
			end
		end
	end
end

local function newSetFromRooms() end

local function contextMenu()
	if not (panel.somethingHovered or panel.somethingDragging) then
		local items = {}
		if #panel.selected > 0 and not lume.match(panel.selected, function(x)
			return x.isRoom == false
		end) then
			table.insert(items, {
				name = "New Set From Selected Rooms",
				func = function()
					newSetFromRooms()
				end,
				items = {
					{ name = "grid" },
					{ { name = "strip H" }, { name = "strip V" } },
				},
			})
		else
			table.insert(items, {
				name = "New Room",
				func = function()
					editor_newRoom()
				end,
			})
			table.insert(items, {
				name = "New Set",
				func = function()
					editor_newSet()
				end,
			})
		end
		mouse_setR(function()
			editor_openContextMenu({}, { dark = true, color = { r = 0, g = 0, b = 0, a = 1 } })
		end)
	end
end

local function dialogueLines(window)
	bench.push("updateDialogueLines")

	local y = window.yoff or window.y
	local shift = panel.dragging and (panel.lineTarget == window)
	local lCount = 1

	local color = window.target.sky and window.target.sky.color or window.color
	local icolor = invertColor(color)

	for l, line in ipairs(window.lines) do
		if not (line.selected and panel.dragging) then
			if panel.loose > 10 then
				panel.loose = panel.loose - 0.05
				line.x = easeTo(line.x, window.x, 4)
				line.y = easeTo(line.y, y, 4)
			elseif not panel.panning and not panel.redraw and (panel.sidebar ~= window or window.active) then
				line.x = easeTo(line.x, window.x, 1.5)
				if shift and not panel.shifted then
					if mouse.y - panel.row / 2 < y + line.h / 2 then
						y = y + panel.lineBox.h + panel.row / 3
						panel.shifted = true
						panel.lineInsert = lCount
					elseif l == #window.lines then
						panel.lineInsert = #window.lines
					end
				end
				line.y = easeTo(line.y, (shift and not panel.shifted) and y - panel.row / 3 or y, 1.5)
			else
				line.x = window.x
				line.y = y
			end
		end

		line.col = ctx.dialogue[line.key].style and ctx.dialogue[line.key].style.backColor or color
		line.tcol = ctx.dialogue[line.key].style and ctx.dialogue[line.key].style.textColor or icolor
		line.window = window.key
		line.index = l

		if
			not line.w
			or mf(line.w) ~= mf(window.lineBox.w)
			or (window ~= panel.sidebar and panel.resize > 0)
			or (window == panel.sidebar and window.resize)
			or line.resize > 0
		then
			line.resize = line.resize and line.resize > 0 and line.resize - 1 or 0
			line.text, line.lines = lume.wordwrap(line.dialogue.languages[game.lang] or "", (line.w - 4) / panel.charW)
			line.text = lume.trim(line.text)
			line.w = window.lineBox.w
			line.h = panel.charH * line.lines + 4
			line.refreshText = true
		end

		line.invert = panel.sidebar ~= window
		line.full = true
		line.align = "left"
		line.alpha = panel.alpha ^ 2
		line.col.a = panel.sidebar == window and 0.8 or 1 --panel.detailFade
		line.infoTime = line.infoTime and easeTo(line.infoTime, line.hovered and 1 or 0, 4) or 0

		if not panel.sidebar.hovered or panel.sidebar == window then
			editor_updateButton(line)
		end

		line.cacheText = true

		if not line.selected then
			window.lineBox.h = window.lineBox.h + line.h + (window == panel.sidebar and 1 or 1)
		end

		if window ~= panel.sidebar and line.infoTime > 0.1 then
			line.sideAuth = line.sideAuth or {}
			line.sideAuth.x = line.x - (line.w / 3 + 2) * line.infoTime
			line.sideAuth.y = line.y
			line.sideAuth.w = line.w / 3
			line.sideAuth.h = mc(9, line.h, panel.charH * 2 + 4)
			line.sideAuth.text = line.dialogue.author
			line.sideAuth.align = "left"
			line.sideAuth.full = true
		end

		if not (line.selected and panel.dragging) then
			y = y + line.h + 1
			lCount = lCount + 1
		end

		if line.hovered then
			panel.somethingHovered = true
		end
		panel.lineCount = panel.lineCount + 1
		panel.lines[panel.lineCount] = window.lines[l]
	end

	--window.lineBox.h = window.lineBox.h+(panel.row*panel.transform.scale*0.2)
	bench.pop("updateDialogueLines")
end

local function sideLines()
	panel.sidebar = panel.sidebar or {}
	panel.sidebar.target = { dialogue = ctx.dialogue }
	panel.sidebar.lines = panel.sidebar.lines or {}
	panel.sidebar.lineBox = panel.sidebar.lineBox or { x = screenW, h = panel.sidebar.h }
	panel.sidebar.w = mc(200, screenW / 6, 350)
	panel.sidebar.h = panel.height - 32
	if editor.resized > 0 then
		panel.sidebar.x = panel.sidebar.active and screenW + 10 or panel.sidebar.x
	end
	panel.sidebar.x = panel.sidebar.x
			and easeTo(
				panel.sidebar.x,
				panel.x + panel.width - (panel.sidebar.active and panel.sidebar.w + 2 or -16),
				2
			)
		or screenW + 500
	panel.sidebar.y = panel.y + 16
	panel.sidebar.ox = panel.sidebar.x
	panel.sidebar.oy = panel.sidebar.y
	panel.sidebar.scrollSpeed = panel.sidebar.scrollSpeed
			and (panel.sidebar.hovered and panel.sidebar.scrollSpeed + mouse.scroll.y * 2)
		or 0
	panel.sidebar.scrollSpeed = panel.sidebar.scrollSpeed * 0.92
	panel.sidebar.scroll = panel.sidebar.scroll
			and mc(
				0,
				panel.sidebar.scroll
					- (hiSpec and panel.sidebar.scrollSpeed or (panel.sidebar.hovered and mouse.scroll.y * 6 or 0)),
				panel.sidebar.lineBox.h - screenH * 1.9,
				4
			)
		or 0
	panel.sidebar.yoff = 16 - panel.sidebar.scroll
	panel.sidebar.padding = 4
	panel.sidebar.lineBox = { x = panel.sidebar.x, y = panel.sidebar.y, w = panel.sidebar.w, h = panel.sidebar.h }
	panel.sidebar.color = { r = 0.1, g = 0.1, b = 0.1, a = 0.1 }
	panel.sidebar.authCol = { r = 0.1, g = 0.1, b = 0.1, a = 0.9 }
	panel.sidebar.sky = { color = panel.sidebar.color }
	panel.sidebar.sideAuths = panel.sidebar.sideAuths or {}
	panel.sidebar.hovered = mouse_in(panel.sidebar)

	if input_keyPressed("tab") then
		panel.sidebar.active = not panel.sidebar.active
		panel.sidebar.resize = true
	end
	if panel.sidebar.x < screenW - 10 and panel.sidebar.x > screenW - 100 then
		panel.sidebar.resize = true
	end
	if panel.sidebar.x < screenW then
		setTextSize(true)
		dialogueLines(panel.sidebar)

		if panel.sidebar.hovered then
			mouse_set(function() end)
		end
	end
	panel.sidebar.resize = false
end

local function preMouse()
	if panel.preDrag and math.dist(mouse.x, mouse.y, panel.preDrag.x, panel.preDrag.y) > 4 then
		panel.dragging = true
		panel.preDrag = nil
	end
	if panel.panning then
		mouse_setCursor(cursors.grab)
		mouse_pushRelease(function()
			panel.panning = false
		end)
		mouse_pushReleaseM(function()
			panel.panning = false
		end)
		panel.mouse = {
			x = (mouse.x - panel.startTransform.x) / panel.startTransform.scale,
			y = (mouse.y - panel.startTransform.y) / panel.startTransform.scale,
		}
		panel.transform.x = panel.startTransform.x + (panel.mouse.x - panel.startMouse.x)
		panel.transform.y = panel.startTransform.y + (panel.mouse.y - panel.startMouse.y)
	else
		panel.mouse = {
			x = (mouse.x - panel.transform.x) / panel.transform.scale,
			y = (mouse.y - panel.transform.y) / panel.transform.scale,
		}
		panel.moose = {
			x = ((mouse.x - screenW / 2) / panel.transform.scale) - panel.transform.x - panel.winW / 2,
			y = ((mouse.y + panel.titleHeight - screenH / 2) / panel.transform.scale)
				- panel.transform.y
				- panel.winH / 2,
		}
	end
end

local function dropDialogue()
	if panel.selected[1] and panel.selected[1].isLine and panel.dragging and panel.lineTarget then
		local drop = {}
		local k, v
		for l, line in ipairs(panel.selected) do
			local win = panel.windows[line.window] or panel.sidebar
			lume.remove(win.lines, line)
			v, k = lume.match(win.target.dialogue, function(x)
				return x.key == line.dialogue.key
			end)
			line.drop = v
			if win ~= panel.sidebar then
				lume.remove(win.target.dialogue, v)
			end
		end
		for l, line in lume.ripairs(panel.selected) do
			if #panel.lineTarget.lines > 0 then
				table.insert(panel.lineTarget.lines, panel.lineInsert, line)
				table.insert(panel.lineTarget.target.dialogue, panel.lineInsert, line.drop)
			else
				table.insert(panel.lineTarget.lines, 1, line)
				panel.lineTarget.target.dialogue = panel.lineTarget.target.dialogue or {}
				table.insert(panel.lineTarget.target.dialogue, 1, line.drop)
			end

			line.window = panel.lineTarg
			line.resize = 2
		end
	end
end

local function startPan()
	panel.startTransform = lume.clone(panel.transform)
	panel.startMouse = lume.clone(panel.mouse)
	panel.panning = true
end

local function deselect()
	for i, v in ipairs(panel.selected) do
		v.selected = false
	end
	panel.selected = {}
	panel.selectedConnection = nil
end

local function select(item)
	panel.lineTarget = nil
	local i, v = lume.find(panel.selected, item)
	local sameType = panel.selected[1]
		and ((item.isWindow and panel.selected[1].isWindow) or (item.isLine and panel.selected[1].isLine))
	if shiftDown and sameType then
		if v then
			table.remove(panel.selected, i)
			item.selected = false
		else
			table.insert(panel.selected, item)
			item.selected = true
			item.dragOrder = #panel.selected
		end
	else
		if item.isWindow then
			panel.dragging = true
		else
			panel.preDrag = { x = mouse.x, y = mouse.y }
		end
		if not item.selected then
			deselect()
			panel.selected = { item }
			item.selected = true
			item.dragOrder = #panel.selected
		end
	end
	panel.startMouse = lume.clone(panel.mouse)
	local dragY = 0
	for i, item in pairs(panel.selected) do
		if item.selected then
			if item.isWindow then
				item.startDiff = { x = mouse.x - item.x, y = mouse.y - item.y }
			else
				item.dragY = dragY
				dragY = dragY + item.h
			end
		end
	end
	panel.lineBox = { x = item.x, y = mouse.y, w = item.w, h = dragY }
end

local function postMouse()
	if not panel.panning then
		if input_keyDown("space") then
			mouse_setCursor(cursors.hand)
			mouse_set(startPan)
		elseif panel.somethingHovered == false then
			mouse_setCursor(defaultCursor)
			mouse_setM(startPan)
			mouse_pushClick(function()
				deselect()
			end)
		end
	end

	mouse_pushRelease(function()
		dropDialogue()
		panel.lineTarget = nil
		panel.preDrag = nil
		panel.dragging = false
		panel.loose = 1
	end)
end

local function close()
	panel.open = false
end

local function windows()
	setTextSize(false)

	local lineTarget = false
	local i = 0
	for w, window in pairs(panel.windows) do
		i = i + 1
		local y = 0
		bench.push("1")

		window.x, window.y =
			panel.transform.scale * (window.target.ox + panel.transform.x) + screenW / 2,
			panel.transform.scale * (window.target.oy + panel.transform.y) - panel.titleHeight + screenH / 2
		window.w, window.h =
			panel.transform.scale * panel.winW + 1, panel.transform.scale * (panel.winH + panel.titleHeight)
		window.cx, window.cy = window.x + window.w / 2, window.y + window.h / 2

		dragItem(window)

		window.hovered = mouse_in(window)

		if window.connecting then
			mouse_pushReleaseM(function()
				window.connecting = false
				panel.somethingConnecting = false
			end)
		end

		if window.hovered then
			panel.somethingHovered = true
			if panel.somethingConnecting and panel.connection[3] ~= window then
				panel.connection[2] = { x = window.cx, y = window.cy }
				mouse_pushReleaseM(function()
					if
						not lume.any(ctx.level.connections, function(x)
							return (x[1] == window.key and x[2] == panel.connection[3].key)
								or (x[2] == window.key and x[1] == panel.connection[3].key)
						end)
					then
						ctx.level.connections[uuid()] = {
							{ type = "manual", room = window.key },
							{ type = "manual", room = panel.connection[3].key },
						}
						panel.connectionFade = 0
					end
				end)
			end

			mouse_setR(function()
				local items = {}

				table.insert(items, {
					{
						name = "Enter Room",
						func = function()
							close()
							game_exitAllRooms()
							engine_waitFor(function()
								if ctx.roomZoom < 0.1 then
									game_enterRoom(window.target, nil, nil, nil, true)
									return true
								end
							end)
						end,
					},
					editor_contextRooms(),
				})
				editor_openContextMenu(items)
			end, true)

			mouse_set(function()
				select(window)
			end)

			mouse_pushClickM(function()
				panel.connection = { { x = window.cx, y = window.cy }, { x = mouse.x, y = mouse.y }, window }
				window.connecting = true
				panel.somethingConnecting = true
			end)
		end
		bench.pop("1")

		bench.push("2")
		y = window.y + window.h + 3

		window.addLine.x = window.x + panel.winR + 2
		window.addLine.w = panel.row * mc(0.5, panel.transform.scale / 2, 1) + 3
		window.addLine.h = panel.row * mc(0.5, panel.transform.scale / 2, 1)
		window.addLine.y = window.y + window.h - window.addLine.h + 1
		window.addLine.float = panel.transform.scale < 1
		window.addLine.color = { r = 1, g = 1, b = 1, a = 0.8 }

		window.lineBox = window.lineBox or { x = 0, y = 0, w = 0, h = 0 }
		window.lineBox.x = window.x
		window.lineBox.y = y
		window.lineBox.w = mc(150, window.w * 3, 500)
		window.lineBox.h = 0

		window.yoff = y
		bench.pop("2")

		bench.push("3")
		dialogueLines(window)
		bench.pop("3")

		bench.push("4")
		window.lineBox.h = window.lineBox.h + panel.row / 2
		if panel.dragging then
			if bboxCollide2(window.lineBox, panel.lineBox) then
				lineTarget = true
				panel.lineTarget = window
			end
		end
		bench.pop("4")
	end

	if not lineTarget then
		panel.lineTarget = nil
	end

	if panel.selected[1] and panel.selected[1].isLine then
		local dragY = 0
		for l, line in ipairs(panel.selected) do
			line.dragY = dragY
			dragY = dragY + line.h
		end
	end

	if panel.selected[1] then
		panel.lineBox.x = panel.selected[1].x
		panel.lineBox.y = panel.selected[1].y
		panel.lineBox.w = panel.selected[1].w
	end

	bench.push("5")
	for w, window in pairs(panel.windows) do
		if window.isSet then
			local p1, p2, hovered, angle, offset
			local offsets = {}
			for v, view in pairs(window.target.views) do
				offsets[view.key] = offsets[view.key]
						and lume.sign(offsets[view.key]) * -(math.abs(offsets[view.key]) + ((offsets[view.key] % 2 == 1 or offsets[view.key] == 0) and 10 or 0))
					or 0
				offset = offsets[view.key] * panel.transform.scale
				p1 = window
				p2 = panel.windows[view.key]
				angle = math.angle(p1.cx, p1.cy, p2.cx, p2.cy) + pi / 2
				p1, p2 =
					{ p1.cx + math.cos(angle) * offset, p1.cy + math.sin(angle) * offset },
					{ p2.cx + math.cos(angle) * offset, p2.cy + math.sin(angle) * offset }
				hovered = false

				if
					not (panel.somethingHovered or panel.somethingDragging or panel.somethingConnecting)
					and distPointToLineSegment({ mouse.x, mouse.y }, p1, p2) < 5 * panel.transform.scale
				then
					panel.somethingHovered = true
					hovered = true
					mouse_setR(function()
						panel.selectedConnection = c
						editor_openContextMenu({
							{ name = "Remove Room from Set" },
						})
					end)
				end
				local highlight = hovered or (panel.selectedConnection and panel.selectedConnection == c)
				table.insert(
					panel.connections,
					{ p1, p2, (highlight and 8 or 4) * panel.transform.scale, { 0, 0, 0, highlight and 1 or 0.7 } }
				)
			end
		end
	end
	bench.pop("5")

	for w, window in pairs(panel.windows) do
		local target = window.target

		if window.redraw or panel.redraw then
			shaders.alpha:send    ("alpha",1)
			window.redraw = false
			target.preview =  newCanvas(panel.winW,panel.winH)
			if window.isSet then
				target.preview:renderTo(function() 
					gfx.clear(target.sky.color.r,target.sky.color.g,target.sky.color.b,1) 
				end)
				target.temp = newCanvas(panel.winW,panel.winH)
				for v,view in ipairs(target.views) do
					print("UPCONTENTS")
					local mesh  = view_updateViewport(view,true)
					if mesh then
						mesh = mesh.viewMesh
						local room = view.room
						local preTex = gfx.getTexture(mesh)
						gfx.setColor(1,1,1,1)
						print("DRCONTENTS")
						-- TODO fix plan pages
						-- view_drawViewContents({
						-- 	room      = room,
						-- 	camera    = room.cameras[view.cameraID],
						-- 	viewport  = room.viewports[view.viewportID],
						-- 	canvas    = target.temp,
						-- 	nolinks   = true ,
						-- 	release   = true,
						-- 	preview   = true ,
						-- 	bounds    = {
						-- 		w       = panel.winW,
						-- 		h       = panel.winH,
						-- 	}
						-- })
						-- gfx.setTexture(mesh,target.temp)
						gfx.push("all")
						target.preview:renderTo(function()
							gfx.setColor(1,1,1,1)
							-- gfx.scale(panel.winW/2,panel.winH/2)
							gfx.draw(mesh)
						end)
						gfx.pop()
						gfx.setTexture(mesh,preTex)
					end
				end
				shaders.alpha:send    ("alpha",panel.alpha)
			else
				print("DRCOROOOM")
				view_drawViewContents({
					room    = target,
					camera  = target.cameras[1],
					canvas  = target.preview,
					nolinks = true ,
					release = true ,
					preview = true ,
					bounds  = {
						w       = panel.winW,
						h       = panel.winH,
					}
				})
			end
		end
	end
end

local function connections()
	local side, d, d2, col = nil, panel.transform.scale * 64, 999, {}
	for c, connection in pairs(ctx.level.connections) do
		local p1 = panel.windows[connection[1].room]
		local p2 = panel.windows[connection[2].room]
		local hovered = false
		local spatial = connection[1].type ~= "manual"
		local sideline
		d2 = d / math.dist(p1.cx, p1.cy, p2.cx, p2.cy)
		if
			not (
				panel.somethingHovered
				or panel.somethingDragging
				or panel.somethingConnecting
				or panel.selectedConnection
			)
			and distPointToLineSegment({ mouse.x, mouse.y }, { p1.cx, p1.cy }, { p2.cx, p2.cy })
				< 4 * panel.transform.scale
		then
			hovered = true
			panel.somethingHovered = true
			if connection[1].type ~= "manual" then
				side = math.dist(mouse.x, mouse.y, p1.cx, p1.cy) < d and p1
					or (math.dist(mouse.x, mouse.y, p2.cx, p2.cy) < d and p2 or nil)
				if side then
					col = side.target.sky.color
					sideline = {
						{
							lerp(p1.cx, p2.cx, side == p1 and d2 or 1 - d2),
							lerp(p1.cy, p2.cy, side == p1 and d2 or 1 - d2),
						},
						{ side.cx, side.cy },
						15,
						{ col.r, col.g, col.b, 1 },
					}
				end
			end
			mouse_setR(function()
				panel.selectedConnection = c
				editor_openContextMenu({
					{
						name = "Remove Connection",
						func = function()
							ctx.level.connections[c] = nil
						end,
					},
					{ name = "Insert New Room" },
				})
			end)
		end
		local highlight = hovered or (panel.selectedConnection and panel.selectedConnection == c)
		table.insert(panel.connections, {
			{ p1.cx, p1.cy },
			{ p2.cx, p2.cy },
			panel.transform.scale * (spatial and 1.3 or 0.8) * (highlight and 5 or 2),
			{ 1, 1, 1, (spatial and 1 or (highlight and 0.5 or 0.2)) },
		})
		if sideline then
			table.insert(panel.connections, sideline)
		end
	end
end

local function drawLines(sidebar)
	bench.push("drawOverviewLines")
	setTextSize(not sidebar)
	panel.lineFont = gfx.setFont(fonts.interfont, sidebar and panel.lineFontSize or 10)
	local line
	for l = 1, panel.lineCount, 1 do
		line = panel.lines[l]
		if line then
			if (panel.windows[line.window] ~= nil) == sidebar then
				if line.infoTime > 0.1 then
					editor_queueButton(line.sideAuth)
				end

				if line.selected then
					gfx.setLineWidth(2)
					gfx.setColor(1, 1, 1, 0.7)
					gfx.rectangle("line", line.x, line.y, line.w - 1, line.h - 1, 3)
					gfx.printf(line.dragOrder, line.x - panel.row, line.y + 3, panel.row)
				end

				line.a = (sidebar and panel.dragging) and 1 or nil
				line.harsh = mean(line.tcol) < 0.5
				line.invert = sidebar
				gfx.setColor(1, 1, 1, 0.7)
				editor_queueButton(line)

				if not sidebar and l > 1 then
					if
						(not panel.sidebar.prevAuth or line.author ~= panel.sidebar.prevAuth)
						and line.author ~= ""
						and panel.sidebar.hovered
					then
						panel.sidebar.sideAuths[line.author] = panel.sidebar.sideAuths[line.author] or {}
						panel.sidebar.sideAuths[line.author].x = line.x - line.w / 2
						panel.sidebar.sideAuths[line.author].y = line.y
						panel.sidebar.sideAuths[line.author].w = line.w / 2
						panel.sidebar.sideAuths[line.author].h = panel.row
						if editor_buttonVisible(panel.sidebar.sideAuths[line.author]) then
							panel.sidebar.sideAuths[line.author].color = panel.sidebar.authCol
							--panel.sidebar.sideAuths[line.author].blur     = true
							panel.sidebar.sideAuths[line.author].full = true
							panel.sidebar.sideAuths[line.author].align = "left"
							panel.sidebar.sideAuths[line.author].noClick = true
							panel.sidebar.sideAuths[line.author].invert = true
							panel.sidebar.sideAuths[line.author].cacheText = true
							panel.sidebar.sideAuths[line.author].text = panel.sidebar.sideAuths[line.author].text
								or panel.sidebar.sideAuths[line.author].text
								or ellipses(line.author, line.w / 2 - 20)
							editor_queueButton(panel.sidebar.sideAuths[line.author])
						end
					end
					panel.sidebar.prevAuth = line.author
				end
			end
		end
	end
	bench.pop("drawOverviewLines")
end

local function draw()

	gfx.setColor(0.11, 0.11, 0.23, 1)
	gfx.rectangle("fill", 0, 0, panel.width, panel.height)
	-- gfx.setScissor(panel.x + 2, panel.y, panel.width, panel.height)

	for _, c in ipairs(panel.connections) do
		gfx.setLineWidth(c[3])
		gfx.setColor(c[4])
		gfx.line(c[1][1], c[1][2], c[2][1], c[2][2])
		-- trace(c[1][1], c[1][2], c[2][1], c[2][2])
	end
	panel.connections = {}

	panel.connectionFade = easeTo(panel.connectionFade, panel.somethingConnecting and 1 or 0, 4)
	if panel.connectionFade > 0 then
		gfx.setColor(1, 1, 1, 0.4 * panel.connectionFade)
		gfx.setLineWidth(1 + panel.connectionFade * 5)
		gfx.line(panel.connection[1].x, panel.connection[1].y, panel.connection[2].x, panel.connection[2].y)
	end
	panel.connection[2] = panel.somethingConnecting and { x = mouse.x, y = mouse.y } or panel.connection[2]

	for w, window in pairs(panel.windows) do
		local target = window.target
		shaders.alpha:send("alpha", panel.alpha)
		if window.isSet then
			gfx.setColor(0, 0, 0, 0.8)
			gfx.setLineWidth(12 * panel.transform.scale)
			gfx.rectangle("line", window.x - 2, window.y - 2, window.w + 2, window.h + 2, 3)
		end
		if window.selected then
			gfx.setColor(1, 1, 1, 0.7)
			gfx.setLineWidth(8)
			gfx.rectangle("line", window.x - 2, window.y - 2, window.w + 2, window.h + 2, 3)
		end
		gfx.setColor(target.sky.color.r, target.sky.color.g, target.sky.color.b, window.hovered and 1 or 0.8)
		gfx.rectangle("fill", window.x - 2, window.y - 2, window.w + 2, window.h + 2, 3)

		if panel.showTitles then
			gfx.setColor(colorTable(invertColor(rgb(target.sky.color))))
			gfx.setFont(fonts.inconsolata, mc(6, (5 * panel.transform.scale) ^ 1.2, 20))
			gfx.printf(
				target.name,
				window.x,
				window.y + panel.titleHeight * 0.5 * panel.transform.scale - gfx.currentFontHeight / 1.5,
				panel.winW * panel.transform.scale,
				"center"
			)
		end

		gfx.setColor(1, 1, 1, 1)

	
		if target.preview then
			gfx.setColor(1, 1, 1, 1)
			gfx.draw(target.preview,window.x-1,window.y+(panel.titleHeight-1)*panel.transform.scale,0,panel.transform.scale,panel.transform.scale)
		end

		if window.hovered then
			if not window.selected then
				gfx.setColor(0, 0, 0, 0.7)
				gfx.setLineWidth(5)
				gfx.rectangle("line", window.x - 4, window.y - 4, window.w + 6, window.h + 6, 3)
			end
		end

		gfx.setFont(fonts.inconsolata, mc(4, 6 * panel.transform.scale, 30))
		editor_queueButton(window.addLine)
		gfx.setFont(fonts.inconsolata, mc(4, 6 * panel.transform.scale, 30) * 2)
		--gfx.print(round(window.target.ox,0)..","..round(window.target.oy,0),window.x,window.y-25)

		if panel.lineTarget == window then
			gfx.setColor(0, 0, 0, 0.8)
			gfx.rectangle(
				"fill",
				window.lineBox.x - 6,
				window.lineBox.y - 6 - panel.row / 6,
				window.lineBox.w + 6,
				window.lineBox.h + panel.lineBox.h,
				3
			)
		end
	end

	drawLines(true)

	if panel.sidebar.x then 
		editor_drawContextBack(panel.sidebar)
	end

	-- gfx.intersectScissor(0,panel.sidebar.y+4,screenWidth,panel.sidebar.h-8)

	drawLines(false)

	-- gfx.setScissor()

end

local function bounds(room)
	panel.min.x, panel.max.x = math.min(room.x, panel.min.x), math.max(room.x, panel.max.x)
	panel.min.y, panel.max.y = math.min(room.y, panel.min.y), math.max(room.y, panel.max.y)
end

local function newLine(key, target)
	if not ctx.dialogue[key] then
		print(inspect(key))
		return
	end
	table.insert(panel.lines, {
		key = key,
		dialogue = ctx.dialogue[key],
		author = ctx.dialogue[key].author,
		hovered = false,
		noClick = true,
		isLine = true,
		col = ctx.dialogue[key].style and ctx.dialogue[key].style.backColor or colors.white,
		tcol = ctx.dialogue[key].style and ctx.dialogue[key].style.textColor
			or lume.merge(invertColor(colors.white), { a = 1 }),
		releaseR = function()
			editDialogue(key)
		end,
		w = panel.winW,
		h = panel.row,
		x = 0,
		y = 0,
	})
	table.insert(target.lines, panel.lines[#panel.lines])
end

local function addDialogue(target, window)
	namer_typeString("new dialogue", "Untitled", { x = mouse.x, y = mouse.y }, function(x)
		local key = uuid()
		ctx.dialogue[key] = {
			key = key,
			speaker = "",
			author = "",
			languages = {
				en = x,
			},
			-- style=effects.defaultCardStyle()
		}
		table.insert(target.dialogue, { key = key })
		if window then
			newLine(key, window)
		end
	end)
end

local function editDialogue(key)
	namer_typeString("new dialogue", ctx.dialogue[key].languages[game.lang], { x = mouse.x, y = mouse.y }, function(x)
		ctx.dialogue[key].languages[game.lang] = x
	end)
end

local function addDialogueLine(target)
	addDialogue(target.target, target)
end

local function cancelLink()
	editor.roomLinkPreview.active = false
end

local function removeLink(link)
	ctx.level.connections[link.connection] = nil
	ctx.rooms[link.linkRoom].links[link.connection] = nil
	ctx.rooms[link.room].links[link.connection] = nil
end

local function update()
	panel.color = { r = 0.2, g = 0.2, b = 0.2, a = 0.5 }
	panel.visible = panel.alpha > 0.05

	panel.alpha = easeTo(panel.alpha, panel.open and 1 or 0, 4)

	panel.lineCount = 0

	if panel.alpha == 0 and #panel.lines > 0 then
		panel.lines = {}
		panel.windows = {}
		panel.sidebar = {}
		collectgarbage()
	end
	if not panel.open then
		return 0
	end

	panel.height = 32


	
	resizePlan()
	preMouse()
	windows()
	contextMenu()
	connections()
	sideLines()
	dragDialogue()
	postMouse()

	ctx.level.overview = { x = panel.transform.x, y = panel.transform.y, scale = panel.transform.scale }

	panel.redraw = false
end

function plan_setPanel(p)
	panel = p
end

function plan_newRoom(room)
	panel.windows[room.key] = {
		key = room.key,
		target = room,
		isRoom = true,
		lines = {},
		lineBox = { x = 0, y = 0, w = 0, h = 0 },
		redraw = false,
		addLine = {
			x = 0,
			y = 0,
			w = panel.row,
			h = panel.row,
			text = "+",
			color = { a = 0.6 },
			textColor = { r = 0, b = 0, g = 0, a = 1 },
			tip = "Add Dialogue",
		},
		w = editor.roomPreview.w + 1,
		h = editor.roomPreview.h,
		hovered = false,
		isWindow = true,
	}
	panel.windows[room.key].addLine.func = function()
		addDialogueLine(panel.windows[room.key])
	end
end

function plan_open()
	if panel.full then
	else
		panel.padding = 4
		panel.width = screenW
		panel.height = screenH
		resizePlan()
		panel.backTex = newCanvas(screenW, screenH)
		editor_drawContextBack(panel)
		-- panel.redraw = true
		ctx.level.overview = ctx.level.overview or lume.clone(panel.transform)
		panel.transform = ctx.level.overview
		mouse_setRelativeMode(false)
		panel.windows = {}
		panel.sidebar = { x = screenW + 50, active = false, resize = true, lines = {} }
		panel.resize = 4
		for l, line in pairs(ctx.dialogue) do
			newLine(line.key, panel.sidebar)
		end
		panel.sidebar.lines = sortBy(panel.sidebar.lines, "author")
		sideLines()
		dialogueLines(panel.sidebar)

		for r, room in pairs(ctx.rooms) do
			plan_newRoom(room)
			for l, line in ipairs(room.dialogue) do
				newLine(line.key, panel.windows[r])
			end
		end

		for s, set in pairs(ctx.level.pages) do
			panel.windows[s] = {
				key = s,
				target = set,
				isSet = true,
				lines = {},
				redraw = false,
				addLine = {
					x = 0,
					y = 0,
					w = panel.row,
					h = panel.row,
					text = "+",
					color = { a = 0.6 },
					textColor = { r = 0, b = 0, g = 0, a = 1 },
					tip = "Add Dialogue",
				},
				w = editor.roomPreview.w + 1,
				h = editor.roomPreview.h,
				hovered = false,
				isWindow = true,
			}
		end
	end
end

function plan_startLink(point)
	editor.roomLink = { cameraID = ctx.viewport.cameraID, point = point, room = ctx.viewport.room }
	cacheLinkPreview()
	paths_closePanel()
	editor_tempTip("Started Link")
	editor.roomLinkPreview.active = true
end

function plan_stopLink(point)
	ctx.level.connections[uuid()] =
		{ lume.clone(editor.roomLink.point), lume.merge(lume.clone(point), { cameraID = ctx.viewport.cameraID }) }
	editor_tempTip("Linked Paths")
	editor.roomLink = false
	editor.setUpdateLinks = true
	editor.roomLinkPreview.active = false
end

function plan_drawLinks(view)
	if editor.debug.no_links then
		return
	end

	if view and not view.nolinks then
		bench.push("prepareLinks")

		view.room.links = view.room.links or { "nocopy" }
		for k, v in pairs(ctx.level.connections) do
			local c = v[1].room == view.room.key and v[1] or (v[2].room == view.room.key and v[2] or nil)
			local c2 = c == v[1] and v[2] or v[1]
			if c then
				if not view.room.links[k] then
					local link = {
						room = c2.room,
						cameraID = c2.cameraID,
						inRoom = true,
						connection = k,
						linkRoom = view.room.key,
					}
					if c.type == "edge" then
						local pts = view.room[c.mode][c.id].points
						local pt = pts[c.index]
						local ptn = pts[c.index == #pts and 1 or c.index + 1]
						if c.mode == "floors" then
							link.x, link.y, link.z = ((vec3(pt) + vec3(ptn)) / 2):unpack()
						end
					end
					view.room.links[k] = link
				end
			end
		end

		bench.pop("prepareLinks")

		--trace("links",lume.count(view.room.links)-1)

		if view ~= ctx.viewport or not ctx.editing or animatePanel.open or lume.count(view.room.links) == 0 then
			return
		end
		bench.push("drawLinks")
		local i = 1
		for l, link in pairs(view.room.links) do
			if type(link) == "table" then
				link.linkID = l
				link.i = i / 100 --lume.count(view.room.links)
				plan_drawLink(link, link.room, nil)
				i = i + 1
			end
		end
		bench.pop("drawLinks")
	end
end

function plan_drawLink(link, r, t)
	--TODO links
	if true then
		return
	end
    
	bench.push("link")

	if not link or not ctx.editing then
		bench.pop("link")
		return
	end

	local d = link.room
	local size = 500
	local size2 = d and size * 0.5 or 250

	link.zoom = link.zoom or 0.25
	link.tip = link.tip or {}

	if not t then
		bench.push("1")
		link.tip.w = (0.4 - ((link.zoom * 4) ^ 1.2) / 4) * size2
		link.tip.h = (0.4 - ((link.zoom * 4) ^ 1.2) / 4) * size2 * 0.8
		link.tip.x = 0 --link.tip.w/2
		link.tip.y = 0 --link.tip.h/2
		link.tip.ta = 1 - (link.zoomNorm or 0)
		link.tip.free = true
		link.tip.matrix = mat4()
		if d then
			link.tip.matrix = mat4()
			link.tip.matrix:translate(link.tip.matrix, vec3(link.x, link.y, link.z))
			local look = mat4.look_at(
				mat4(),
				vec3(link.x, link.y, link.z),
				-ctx.viewport.camera.pos,
				vec3(0, 1, 0)
			)
			mat4.invert(link.tip.matrix, mat4.mul(link.tip.matrix, look, link.tip.matrix))
			link.tip.nocopy = true
		else
			bench.pop("link")
			return
		end
		shaders.forwardRender:send("wobble", 0)
		shaders.forwardRender:send("MVP", mat4.mul(mat4(), ctx.viewport.camera.vp, linkMat:transpose(link.tip.matrix)))
		bench.pop("1")
	end

	local text = "" --d and (game.resolution<1 and "" or ellipses(level.rooms[link[2].room].name,(t and t.w or link.tip.w)-18)) or ""
	if t then
		t.text = text
		t.free = true
	else
		link.tip.text = text
	end
	local todraw = t or link.tip
	todraw.noshadow = true
	bench.push("2")
	local tip = editor_drawTip(todraw)
	bench.pop("2")
	local tp = { x = tip.x + 2, y = tip.y + 2, w = tip.w - 4, h = tip.h - 4 }
	link.hovered = (t or editor.readyToClick)
		and (t and mouse_in(tip) or round(pickerColor and pickerColor.g or 0) == link.i)
	local wasHovered = link.tip.hovered
	if not t then
		link.tip.hovered = link.hovered
	end

	link.cacheTime = (link.tip.hovered and not wasHovered)
	link.zoom = easeTo(link.zoom, (editor.linkSelected == link or link.tip.hovered) and 0.15 or 0.25, 4)
	mult = (t and t.stackPos) and 0.3 or (t and size or size2) / 800

	link.zoomNorm = (link.zoom - 0.15) * 10
	local border = 4
	local border2 = border * (t and (link.hovered and 0.5 or 1) or link.zoomNorm)
	local add = (game.resolution < 1 or t or (size2 and size2 < 300)) and 14 or ((link.zoom - 0.15) * 140)
	local s = { tip.x + border, tip.y + border * 4.5 - add, tip.w - border * 2, tip.h - border * 5.5 + add }
	s = { s[1] - border2 / 2, s[2] - border2 / 2, s[3] + border2, s[4] + border2 }

	local preview = {
		s[1],
		s[2],
		s[1] + s[3],
		s[2],
		s[1] + s[3],
		s[2] + s[4],
		s[1],
		s[2] + s[4],
	}

	bench.push("3")
	link.tip.mesh = link.tip.mesh
		or gfx.newMesh(zformat, {
			{ tp.x, tp.y, 0, 1, 1, 0 },
			{ tp.x + tp.w, tp.y, 0, 1, 0, 0 },
			{ tp.x + tp.w, tp.y + tp.h, 0, 1, 0, 1 },
			{ tp.x, tp.y + tp.h, 0, 1, 1, 1 },
		}, "fan")
	gfx.setTexture(link.tip.mesh, link.tip.canvas or load_imageTex())

	gfx.setColor(1, 1, 1, 1)
	gfx.draw(link.tip.mesh)
	bench.pop("3")

	bench.push("4")

	--local dest      = d and level.rooms[d.room][d.mode][d.path] or nil

	local pos
	if dest then
		local destCam = ctx.rooms[d.room].cameras[1]
		local coords = lume.clone(dest.points[d.side == "source" and 1 or #dest.points])
		local offset = destCam.locked and { offset = coords } or coords
		local prepos = lume.merge(destCam, offset)
		pos = lume.merge(prepos, { zoom = 0.2 * mult, center = { x = s[3] / 2, y = s[4] / 2 } })
	end

	--if editor.roomLink.room == r  then
	--  pos = editor.roomLink.camera
	--end

	bench.pop("4")

	bench.push("5")
	mouse_pushDown(function()
		editor.linkSelected = nil
	end)

	if link.hovered then
		editor.linkHovered = true
		editor.state = "Transforming"
		mouse_setCursor(cursors.hand)
		if t and t.stackPos then
			if t.stackPos < #ctx.roomStack then
				mouse_set(function()
					game_enterStackRoom(t.stackPos)
				end)
			end
		else
			mouse_setR(function()
				editor.linkSelected = link

				editor_openContextMenu(link.inRoom and {
					{
						name = "Remove Link",
						update = function()
							link.zoom = 0.15
						end,
						func = function()
							removeLink(link)
						end,
						col = { r = 0.5, g = 0.3, b = 0.2, a = 0.4 },
					},
				} or { { name = "Cancel Link", func = cancelLink, col = { r = 0.6, g = 0.3, b = 0.2, a = 0.4 } } })
			end, true)

			mouse_set(function()
				local rm = r and r.key or (r or d.room)
				if ctx.viewport.key ~= rm then
					link.hovered = false
					link.tip.hovered = false
					if ctx.pageView then
						game_enterRoom()
						ctx.roomZoom = -0.5
						game_enterRoom()
						delayedCall(function()
							game_enterRoom(ctx.rooms[rm], prepos, link, link.linkID)
						end, 0.05)
					else
						game_enterRoom(ctx.rooms[rm], pos, link, link.linkID)
					end
				end
			end)
		end
	end

	bench.pop("5")

	bench.push("7")
  
	if t then
		gfx.setColor(1, 1, 1, 1)
		if t.canvas then
			gfx.draw(
				t.canvas,
				tip.x + mu(2 * game.resolution),
				tip.y + mu(2 * game.resolution),
				0,
				game.resolution,
				game.resolution
			)
		elseif link.tip.canvas then
			gfx.draw(
				link.tip.canvas,
				tip.x + mu(2 * game.resolution),
				tip.y + mu(2 * game.resolution),
				0,
				(tp.w / link.tip.canvasSize.w) * game.resolution,
				(tp.h / link.tip.canvasSize.h) * game.resolution
			)
		end
		gfx.setColor(1, 1, 1, link.hovered and 0.3 or 0)
		gfx.setLineWidth(1)
		-- gfx.polygon("line", preview)
    
	else
		if not link.tip.canvas or link.cacheTime then
			link.tip.canvasSize = { w = tp.w * editor.linkRes, h = tp.h * editor.linkRes }
			link.tip.canvas = newCanvas(link.tip.canvasSize.w, link.tip.canvasSize.h)
			-- pushDraw
			--  local room = level.rooms[link.room]
			--  if room then
			--    drawRoom({
			--      room    = level.rooms[link.room],
			--      wobble  = {speed = 0,dist  = 0},
			--      camera  = camera_setup(room.cameras[link.cameraID or 1]),
			--      canvas  = link.tip.canvas,
			--      bounds  = {x=0,y=0,w=link.tip.canvasSize.w,h=link.tip.canvasSize.h},
			--      polys   = {preview},
			--      linkID  = link.linkID,
			--      link    = link,
			--      nolinks = true
			--    })
			--  end
		end
	end
	bench.pop("7")

	if t and t.canvas then
	end
	bench.pop("link")
end

function plan_drawLinkPreview()
	editor.roomLinkPreview.x = easeTo(
		editor.roomLinkPreview.x,
		editor.roomLinkPreview.active and engine.trace.x + engine.trace.width + 73 or -150,
		4
	)
	if editor.roomLink and editor.roomLinkPreview.active then
		plan_drawLink({}, editor.roomLink.room, editor.roomLinkPreview)
	elseif editor.roomLinkPreview.x < -100 then
		editor.roomLink = nil
	end
end

function plan_drawRoomStack()
	if animatePanel.open then
		return
	end

	local newStack = lume.concat({ editor.setPreview }, { editor.stackStart or {} }, ctx.roomStack)
	local right = 0
  trace("drawlinks",#newStack)
	for i, p in ipairs(newStack) do
		local x = i * (editor.roomPreview.w + 5)
			- editor.roomPreview.w * 0.5
			+ math.zero(
				math.max(engine.trace.x + engine.trace.width, editor.roomLinkPreview.x + editor.roomLinkPreview.w * 0.5)
			)

		local t = {
			x = x,
			w = editor.roomPreview.w,
			h = editor.roomPreview.h,
			y = editor.roomLinkPreview.y - (editor.roomPreview.h ^ 0.5 * 0.5),
			notri = true,
			noshadow = true,
			free = true,
			scale = 1,
			stackPos = i - 2,
		}

		if i == 1 then
			if not editor.setPreview and not ctx.pageView then
				-- editor.setPreview = gfx.copyCanvas(
				-- 	ctx.page.preview,
				-- 	{ w = editor.roomPreview.w * 0.945, h = editor.roomPreview.h * 0.935, y = 0 }
				-- )
			end
			if editor.setPreview then
				t.canvas = editor.setPreview
				plan_drawLink({}, room, t)
        trace("drawlink")
			end
		elseif i == 2 then
			if #ctx.roomStack > 0 then
				-- plan_cacheStackStart()
			else
				editor.stackStart = nil
			end
			if editor.stackStart then
				t.canvas = editor.stackStart
				plan_drawLink({}, room, t)
			end
		elseif i < #newStack then
			if p[3].tip and p[3].tip.canvas then
				plan_drawLink(p[3], room, t)
			end
		end
		right = x > right and x or right
	end
	editor.stackEnd = easeTo(editor.stackEnd, right - editor.stackPreview.w / 2, 4)
end

function plan_cacheStackStart()
	editor.stackStart = editor.stackStart
		or gfx.copyCanvas(
			engine.canvas,
			{ w = editor.roomPreview.w - 4, h = editor.roomPreview.h - 4, filter = "linear" }
		)
end

function plan_cacheRoomPreview(room)
	editor.roomPreviews[room.key] = gfx.copyCanvas(
		engine.canvas,
		{ w = editor.roomPreview.w * 2 - 1, h = editor.roomPreview.h * 2 - 2, filter = "linear" }
	)
	room.preview = editor.roomPreviews[room.key]
end

function plan_cacheSetPreview()
	editor.setPreviews[ctx.page.key] = gfx.copyCanvas(
		engine.canvas,
		{ w = editor.roomPreview.w * 2 - 1, h = editor.roomPreview.h * 2 - 2, filter = "linear" }
	)
	ctx.page.preview = editor.setPreviews[ctx.page.key]
end

function plan_define()
	return {
		update = update,
		draw = draw,
		name = "",
		open = false,
		docked = false,
		drag = false,
		panning = false,
		showTitles = false,
		winW = 70,
		winH = 48,
		titleHeight = 8,
		detailFade = 1,
		loose = 0,
		x = 0,
		y = 0,
		width = screenW,
		height = screenH,
		connectionFade = 0,
		connection = { { x = 0, y = 0 }, { x = 0, y = 0 }, {} },
		sidebar = { x = screenW + 500, y = 0, w = 350, h = 0, scroll = 0 },
		connections = {},
		selected = {},
		lines = {},
		charW = 10,
		charH = 10,
		lineCount = 1,
		lineBox = { x = 0, y = 0, w = 0, h = 0 },
		row = 20,
		resize = 2,
		transform = {
			x = 0,
			y = 0,
			scale = 1,
		},
		full = false,
		level = {},
		windows = {},
		edge = 32,
		alpha = 0,
		min = {
			x = 0,
			y = 0,
			scale = 0.5,
		},
		max = {
			x = 0,
			y = 0,
			scale = 4,
		},
	}
end
