function editor.playLevel()
	editingLevel				= false
	editor.wasEditingCode 		= editor.editingCode and scriptsPanel.key or ""
	editor.editingCode 			= false
	editor.curoom = lume.find(currentRooms,currentRoom)
	editor.resolution = game.resolution 

	preCam = {angle=lume.clone(cam.angle)}
	
	game.setResolution(game.playRes)

	editor.dockPanel 			(scriptsPanel)
	game.unPause 				()
	editor.storeSelected 		()
	editor.save 				()
	editor.togglePanel 			(pathsPanel,true)
	pathsPanel.wallCutting = false
	scene.destroy 			() 
	game.reloadLevelData 		()
	scene.load 				()

	-- collectgarbage()
end

function editor.exitEditor()
	namer:warning("Exit editor?",function()
		effects.fade(game.exit)
	end)
end

function editor.resumeEdit()
	editor.refreshSelection = true
	delayedCall(function() editor.refreshSelection = false end,0.1)
	
	game.setResolution(editor.resolution)
	game.unPause 						()
	lmouse.setVisible 	(true)
	editingLevel 						= true
	scene.destroy 				()
	game.reloadLevelData 		()
	scene.load 		 				()
	editor.restoreSelected 	()	
	editor.restorePathPanel ()

	cam.angle = lume.clone(preCam.angle)

	if editor.wasEditingCode and editor.wasEditingCode ~= "" then 
		editor.openScript(editor.wasEditingCode)
	end
end

function editor.storeSelected()
	selectedIDs={}
	if not selectedEntities then return end
	for i,e in ipairs(selectedEntities) do 
		table.insert(selectedIDs, e.uid) 
	end
	
end

function editor.restoreSelected(ids)
	lume.clear(selectedEntities)
	for i,d in ipairs( ids or selectedIDs or {}) do
		table.insert(selectedEntities, scene.getEntityByID(d))
	end
end


function editor.start()
	game.state  			= "Edit"
	editState 			= "Normal"
	layer 				= 2
	editor.saveStep 			= 30
	editingLevel 		= true
	editor.startTime 	= time
	lmouse.setVisible( true )


	if config.inRoom then 
		-- editor.setRoom(game.getRoom(config.inRoom.key))
		-- waitFor(function()
		-- 	if currentRoom.viewport then 
		-- 		game.enterRoom()
		-- 		roomZoom=1
		-- 		return true
		-- 	end
		-- end)
	end
end

function editor.setState(state)

end



function editor.update()
	bench.push("editor.update")

	if editor.intersect  and editor.debug.trace_mouse_intersect then 
		trace("intersect",editor.intersect.x,editor.intersect.y,editor.intersect.z)
	end

	editor.saveStep 		=  editingLevel and editor.autoSaveEnabled and editor.saveStep+1 or editor.saveStep 
	editor.buttonsClipped = 0
	editor.buttonsDrawn = 0
	editor.resized 				= editor.resized>0 and editor.resized-1 or 0
	editor.refresh 				= false
	editor.nearestPlane 	= 9999999
	editor.pointing 			= false
	--mouse.locked 					= false
	mouse.z 							= 0
	editor.updateLinks 		= false
	
	lmouse.setCursor 	(defaultCursor) 
	editor.traceEditorHelp()
	wobbleRoom 						= ""


	if not editor.tempCam and editor.preTempCam then 
		editor.resetCamera(editor.preTempCam.index,true)
		currentRoom.camera = editor.preTempCam 
	elseif editor.tempCam then 
		editor.resetCamera(editor.tempCam,true)
		editor.tempCam = nil
	end
	
	gfx.fontSize(12)

	roomZoom 							= level.isAnim and 0 or easeTo(roomZoom,roomsView and 1 or 0, roomsView and 4 or  8,3)
	editor.wasRoomView 		= roomsView 
	editor.gridFade 							= easeTo(editor.gridFade,editor.gridEnabled and 1 or 0, editor.gridEnabled and 3 or  2, 1)
	scene.setView 			(currentRoom)
	editor.animHovered 	 	= {key="",object=""}

	game.switchSet()
	game.switchRoom()

	if editingLevel then
		editor.click()
	end

	if  room and cam then   
		editor.transformEntities(currentRoom)
		editor.handleOtherMouseStates()
		editor.editorKeys()
		editor.autoSave()
		editor.saveSize()
		editor.dropNewPathPoint()
		editor.mouseUpdates()

		if (editState == "Normal" or editState == "Transforming" or (not levelClicked and animationPanel.open)) and (not roomsView or roomHovered) and stageHovered then 
			editor.readyToClick = true
		
		else
			editor.readyToClick = false
		end

		if 	editState == "Panning" 			then 
			editor.panLevel() 
		end
		editor.select() 
	end

	gfx.setShader 		()
	gfx.setDepthMode	()


	editor.updatePanels()

	if 	#contextMenu.items>0 	then 
		editor.updateContextMenu() 
	else
		if editor.colorTip.open then
			unlockMouse() 
			editor.updateColorTip()
			lockMouse()
		end
		if editState=="Context Menu" then
			editState="Normal"
		end
	end

	editor.toolTip()
	namer:update()

	bench.pop("editor.update")
end

function editor.setRoom(room)
	currentRoom  		= room
	editor.currentRoom  = room
end


function editor.toggleRooms()
	roomsView = not roomsView
end

function editor.copyEntities()
	editor.pasteboard={} 
	for i,entity in lume.ripairs(selectedEntities) do
		if not entity.simple then 
			table.insert(editor.pasteboard, deepcopy(entity))
		end
	end
end

function editor.pasteEntities()
	editor.pushUndoState("Paste")

	lume.clear(selectedEntities) 

	--table.sort(editor.pasteboard, function(a, b) return a.z > b.z end)

	for i,v in ipairs(editor.pasteboard) do 
		local pasted 	= deepcopy(v)
		pasted.x		=	pasted.x+30
		pasted.y		=	pasted.y+30
		pasted.z		=	pasted.z+30
		pasted.uid 		= 	uuid()
		table.insert(selectedEntities, scene.newEntity(scene.returnObject(pasted)))
	end
	log("pasted",#selectedEntities)
end

-------------------------------------------

function editor.setPlayer()
	if #selectedEntities == 1 then 
		namer:warning("Set Player",function() room.player = selectedEntities[1].uid end) 
	end
	if #selectedEntities == 0 and room.player~="" and player~=nil then
		cam.x = player.ox + (sidebar.visible and sidebar.width-80 or 0)
		cam.y = player.oy
	end
end

function editor.renameInstance(entity)
	namer:typeString("Rename Instance",entity.name,{x=entity.render.x,y=entity.render.y},
		function(x) 
			for i,e in ipairs(room.entities) do 
				if e.name == x then 
					namer:alert("Name Taken")
					return
				end
			end
			entity.name = x
		end
	)
end



function editor.drawGrid()

	if not (roomZoom<1 
		 and editor.gridFade>0.05 
		 and room.key == currentRoom.key 
		 and not editor.drawingLink) 
	then return end
	bench.push("drawGrid")

	local gridAlpha = editor.gridFade*0.3*(1-roomZoom)

	gfx.setColor(1,1,1,1) 
	editor.gridMat = mat4.rotate(mat4(), mat4(), pi/2, axes.x)
	shaders.forwardRender:send("MVP",MVPmat:transpose(MVPmat:mul(gfx.camera.vp,editor.gridMat)))
	shaders.forwardRender:send("modelMatrix",editor.gridMat)

	gfx.setWireframe( true ) 
	-- shaders.forwardRender:send("frame",gridAlpha)
	shaders.forwardRender:send("drawShadowed",false)
	shaders.forwardRender:send("fadePoint",{-cam.x,-cam.y,-cam.z})
	shaders.forwardRender:send("fadeDist",600+math.abs(zcam.pos.y)*2)
		gfx.draw(editor.debugGrid) 
	shaders.forwardRender:send("fadeDist",0)
	shaders.forwardRender:send("frame",0)
	shaders.forwardRender:send("drawShadowed",drawShadowed)
	gfx.setWireframe( false)
	gfx.setColor(1,1,1,1)

	bench.pop("drawGrid")
end

function editor.createGrid()
	local scale = 1000
	local gap = 50
	local verts = {}
	for i=0,(scale/gap)*2,1 do 	
		table.insert(verts,{-scale+i*gap, -scale,0,1, 0,0 ,1,1,1,0.1})
		table.insert(verts,{-scale+i*gap,  scale,0,1, 0,0 ,1,1,1,0.1})
		table.insert(verts,{-scale+i*gap, -scale,0,1, 0,0 ,1,1,1,0.1})
	end
	for i=0,(scale/gap)*2,1 do 	
		table.insert(verts,{scale, -scale+i*gap,0,1, 0,0 ,1,1,1,0.1})
		table.insert(verts,{-scale,-scale+i*gap,0,1, 0,0 ,1,1,1,0.1})
		table.insert(verts,{scale, -scale+i*gap,0,1, 0,0 ,1,1,1,0.1})
	end

	editor.grid = gfx.newMesh(zformat, verts, "triangles")

	scale = 1000
	gap = 50
	verts = {}
	for i=0,(scale/gap)*2,1 do 	
		for j=0,(scale/gap)*2,1 do 	
			table.insert(verts,{-scale +i*gap, 			-scale+j*gap,			0,1, 0,0 ,1,1,1,0.4})
			table.insert(verts,{-scale +(i+1)*gap, 	-scale+j*gap,			0,1, 0,0 ,1,1,1,0.4})
			table.insert(verts,{-scale +i*gap, 			-scale+j*gap,			0,1, 0,0 ,1,1,1,0.4})

			table.insert(verts,{-scale +i*gap, 			-scale+j*gap,			0,1, 0,0 ,1,1,1,0.4})
			table.insert(verts,{-scale +i*gap, 			-scale+(j+1)*gap,	0,1, 0,0 ,1,1,1,0.4})
			table.insert(verts,{-scale +i*gap, 			-scale+j*gap,			0,1, 0,0 ,1,1,1,0.4})
		end
	end
	
	editor.debugGrid = gfx.newMesh(zformat, verts, "triangles")
end

function editor.scrollZoom()
	if stageHovered  then 
		if not(mouse.down.L or mouse.down.R) then 
			--if keyboard:modsTouched() then
			--	mouseScroll = {x=0,y=0}
			--end

			if cam.locked  then 
				if keyDown("space") then
					cam.offset.y		= cam.offset.y+mouseScroll.y*5
					cam.offset.x		= cam.offset.x-mouseScroll.x*5
				else
					cam.soom 	= cam.soom+mouseScroll.y* 0.003
				end
			else
				if hiSpec or level.isAnim then 
					cam.soom 	= cam.soom-mouseScroll.y* 0.003
				else 
					if keyDown("space") then
						cam.y		= cam.y-mouseScroll.y*5
						cam.x		= cam.x+mouseScroll.x*5
					elseif cmdDown  then
						--cam.focalL	= mc(0,cam.focalL-mouseScroll.y*(0.005+0.02*cam.focalL),1)
						cam.soom 	= cam.soom+mouseScroll.y* 0.003
					elseif  altDown  then
						cam.tilt	= mc(0,cam.focalL>0 and cam.tilt+mouseScroll.y*0.01 or 0,1)
						--cam.focalL 	= cam.tilt>cam.focalL and cam.tilt or cam.focalL
					elseif  cam.focalL>0  then
							cam.x = cam.x + math.sin(-cam.angle.x) * mouseScroll.y * 5
							cam.z = cam.z + math.cos(-cam.angle.x) * mouseScroll.y * 5
							cam.x = cam.x + math.cos(cam.angle.x) * -mouseScroll.x * 5
							cam.z = cam.z + math.sin(cam.angle.x) * -mouseScroll.x * 5
					end
				end
			end
		end
	end
end


function editor.panLevel()
	lmouse.setCursor(cursors.grab)
	--trace("cam.zoom(pan)     "..cam.zoom,tostring(cam==room.cam))
	if not cam.locked then 
		if editor.panStarted then 
			editor.panStarted=false
			cam.stored 		= {x=cam.x,y=cam.y,z=cam.z,soom=cam.soom,b=b or 1}
			
		end
		local soom = (cam.soom-0.78)^0.95

		if not shiftDown then 
			if level.isAnim then 
				animationPanel.panned = true
				cam.x = cam.stored.x - (mouseStored.x-mouse.x)*editor.panMult.x
				cam.y = cam.stored.y - (mouseStored.y-mouse.y)*editor.panMult.y
			else
				cam.x = cam.stored.x - math.cos(cam.angle.x) * (mouseStored.x-mouse.x) * 1 *soom 
				cam.z = cam.stored.z - math.sin(cam.angle.x) * (mouseStored.x-mouse.x) * 1 *soom 
				cam.y = cam.stored.y - (mouseStored.y-mouse.y) * 1 *soom
			end
		else
			if level.isAnim then 
				cam.soom=mc(0.8,cam.stored.soom+(mouseStored.y-mouse.y)/2000,1.22)
			else
				cam.x = cam.stored.x - math.cos(cam.angle.x) * (mouseStored.x-mouse.x) * 1 - math.sin(-cam.angle.x) * (mouseStored.y-mouse.y) * 1
				cam.z = cam.stored.z - math.sin(cam.angle.x) * (mouseStored.x-mouse.x) * 1 - math.cos(-cam.angle.x) * (mouseStored.y-mouse.y) * 1
			end
		end
	end
end


function editor.mouseUpdates()
	if (not roomsView or currentRoom == roomHovered) and  not scriptsPanel.focused then 
		if (not roomsView or roomHovered) and stageHovered and not mouse.locked and not mouseIn(scriptsPanel.border) then 
			editor.clickStage()
		end
	end

	mouse.pushRelease(editor.release )
	mouse.pushReleaseR(editor.release )

	editor.scrollZoom()
	editor.storeMouse()
	editor.previewDrop()
end

function editor.previewDrop()

	if not contextMenu.open then 
		editor.dropPoint = editor.intersect and vec3(editor.intersect) or editor.dropPoint
		if editor.intersectTri then 
			editor.intersectNormal = cpml.mesh.normal(editor.intersectTri)
		end
		mouseLocalDrop 	= (editor.intersect) and {x=editor.intersect.x,y=editor.intersect.y,z=editor.intersect.z} or vec3Table(vec3(getCameraPlaneCoords())) or {x=0,y=0,z=0}

		mouseLocalDrop.dropped=true
	end


	if type(mouseLocalDrop)~="table" then return end

	if #galleryPanel.selected == 0 then return end
	-- for i,item in ipairs(galleryPanel.selected) do 
	-- 	if stageHovered and droppingItem and objects[item.key]~=nil then 
	-- 		local frame = objects[item.key].animations[ objects[item.key].default].layers[1].frames[1]
	-- 		local dim  	= (frame.w>frame.h) and frame.w*cam.zoom or frame.h*cam.zoom
	-- 		dropScale 	= easeTo(dropScale,mouseIn(sidebar) and 1 or dim/thumbSize,8)
	-- 			gfx.setBlendMode("alpha","premultiplied")
	-- 			gfx.setColor(1,1,1,1)
	-- 			--gfx.draw(getImageThumb(item.key), mouse.x-dropScale*thumbSize*0.5, mouse.y-dropScale*thumbSize*0.5,0, dropScale,dropScale)
	-- 	end
	-- end
end

function editor.storeMouse()
	mousePrev 	= mouseLocal or {x=mouse.x,y=mouse.y}
	mouseMoved = (savedMouse.x~=mouse.x or savedMouse.y~=mouse.y or mouseScroll.x~=0
		or mouseScroll.y~=0)
	savedMouse 	= { 
		x=mouse.x, 
		y=mouse.y 
	}
end

function editor.handleOtherMouseStates()
	if 	editState == "Resizing Panel" 	then 	
		editor.resizePanel() 
	end 

	if editState == "Normal" then 
		
	end
end

function editor.contextColorPicker(target)
	editor.pickingColor = true
	contextMenu.colorTarget = target
	editor.storedMouse = vec2(mouse)
end



function editor.toggleSky()
	skyBacking = not skyBacking
end

function editor.toggleLines()
	renderLines = renderLines<lineRenderModes and lineRenderModes or 1
end


function editor.loadSet(key)
	loadSet(key)
	editor.editingSet 	= key
	editor.currentRooms = currentSet.rooms	
end


function editor.save()
	local saveLevel  	= editor.preSave()
	loader:saveDef 	 	("objects",	objects)
	loader:saveDef 	 	("dialogue",	dialogue)
	loader:saveDef 	 	("tags",		tagNames)
	loader:saveDef   	("scripts",	scripts)
	local directory  	= PROJ_DIR.."levels/"..level.key..".lua"
	local packed = Tserial.pack(saveLevel,nil,true)
	loader:writeFile 	(directory,"return "..packed)
	levels[level.key] 	= deepcopy(saveLevel)
	editor.savePreviews()
	saveConfig 			()
	editor.saveStep 			= 30
	log 				("Level Saved")
end

function editor.stripObjects(objects)
	local t = {}
	for o,object in pairs(objects) do
		t[o]={}
		for k,v in pairs(object) do
			if not multicheckOR(k,{"animations","image","path"}) then 
				t[o][k] = deepcopy(v)
			end
		end
	end
	return t
end

function editor.savePreviews()
	local img = LOVR and "Image" or "Texture"
	-- for s,set in pairs(level.sets) do 
	-- 	if set.preview and tostring(set.preview)~=img then 
	-- 		set.preview:newImageData():encode("png", "setCache/"..set.key..".png")
	-- 	end
	-- end
	-- for r,room in pairs(level.rooms) do
	-- 	if room.preview and tostring(room.preview)~=img then 
	-- 		room.preview:newImageData():encode("png", "previewCache/"..room.key..".png")
	-- 	end
	-- 	for l,link in pairs(room.links) do
	-- 		if link.cached and tostring(link.cached)~=img then 
	-- 			link.cached:newImageData():encode("png", "linkCache/"..link.linkID..".png")
	-- 		end
	-- 	end
	-- end
end

function editor.preSave(ents)

	local raw = deepcopy(level)
	-- raw.sky=deepcopy(level.sky)print("sky")
	-- raw.connections = deepcopy(level.connections)print("connections")
	-- raw.overview = deepcopy(level.overview)print("overview")
	-- raw.sets = {}
	-- for s,set in pairs(level.sets) do 
	-- 	raw.sets[s] = {}
	-- 	raw.sets[s].sky=shallowcopy(sky) print("sky")
	-- 	raw.sets[s].ox=shallowcopy(ox) print("ox")
	-- 	raw.sets[s].y=shallowcopy(y) print("y")
	-- 	raw.sets[s].rooms={}
	-- 	for r,room in pairs(set.rooms) do 
	-- 		raw.sets[s].rooms[r] = {}
	-- 		local rroom =raw.sets[s].rooms[r]
	-- 		rroom.i=room.i print("i")
	-- 		rroom.viewpoly=room.viewpoly print("viewpoly")
	-- 		rroom.cameraID=room.cameraID print("cameraID")
	-- 		rroom.prevpoly=room.prevpoly print("prevpoly")
	-- 		rroom.aspectRatio=room.aspectRatio print("aspectRatio")
	-- 		rroom.depth=room.depth print("depth")
	-- 		rroom.bounds=room.bounds print("bounds")
	-- 		rroom.viewportID=room.viewportID print("viewportID")
	-- 		rroom.viewVerts=room.viewVerts print("viewVerts")
	-- 		rroom.numVerts=room.numVerts print("numVerts")
	-- 		rroom.key=room.key print("key")
	-- 		rroom.changed=room.changed print("changed")
	-- 	end
	-- 	raw.sets[s].oy=shallowcopy(oy) print("oy")
	-- 	raw.sets[s].x=shallowcopy(x) print("x")
	-- 	raw.sets[s].name=shallowcopy(name) print("name")
	-- 	raw.sets[s].key=shallowcopy(key) print("key")
	-- 	raw.sets[s].dateCreated=shallowcopy(dateCreated) print("dateCreated")
	-- end
	-- raw.variables = deepcopy(level.variables)print("variables")
	-- raw.key = deepcopy(level.key)print("key")
	-- raw.images = deepcopy(level.images)print("images")
	-- raw.rooms={}
	-- for r,room in pairs(level.rooms) do 
	-- 	raw.rooms[r]={}
	-- 	for k,v in pairs(room) do 
	-- 		print(k)
	-- 		raw.rooms[r][k]=deepcopy(v)
	-- 	end
	-- end
	-- raw.name = deepcopy(level.name)print("name")
	-- raw.defaultSet = deepcopy(level.defaultSet)print("defaultSet")

	for r,room in pairs(level.rooms) do 
		raw.rooms[r]  					= deepcopy(room)
		local tempObjects={}
		local tempEntities = {}
		for i,entity in ipairs(room.entities) do
			local obj = scene.pushEntity(tempObjects,entity) 
			if obj and obj.uid then 
				tempEntities[obj.uid] = obj
			end
		end
		if room.dialogue then 
			local lines = {}
			for l,line in ipairs(room.dialogue) do
				lines[l]=line.key
			end
			raw.rooms[r].dialogue  = lines
		end
		raw.rooms[r].objects  	= tempObjects
		if ents then 
			raw.rooms[r].entitiesByID = tempEntities
		end
	end
	for s,set in pairs(raw.sets) do 
		if set.dialogue then 
			local lines = {}
			for l,line in ipairs(set.dialogue) do
				lines[l]=line.key
			end
			raw.sets[s].dialogue  = lines
		end
		for r,room in ipairs(set.views) do 
			for p,prop in ipairs({"camera","room","viewport","viewPolys"}) do 
				raw.sets[s].views[r][prop]=nil
			end
		end
	end
	return raw
end

function editor.autoSave()
	editor.saveTime 	= mf((editor.autoSaveTime*11)+1)*5*3600 
	if(editor.saveStep > editor.saveTime) then 
		if level.isAnim then 
			editor.saveStep 			= 30
			editor.saveGraphics(animationPanel) 
		else 
			editor.save() 
		end
	end 
end

function editor.showInFinder(dir)
	local spaced 	= dir:gsub("%s+", "\\ ")
	if os.name()=="Windows" then 
		local thing = "explorer /select, \""..dir.."\""
		os.execute 		(thing)
	else
		os.execute 		("open -R file://"..spaced)
	end
end

function editor.dropFile(file,copy)
	if file~=nil then
		 fileDropped = true
	end
	if  fileDropped and editingLevel then 
		local filename 		= file:getFilename()
		local basename 		= basename(filename)
		local extension 	= file:getFilename():sub(-3,-1)
		local name  			= uuid()
		local destFolder 	= "Game"
		local destTags 		= {}
		local sourcePath  = "\""..file:getFilename().."\""

		if not stageHovered and galleryPanel.hovered then 
			destTags = copy and objects[copy].tags or galleryPanel.selectedTags
		end

		print(extension)	
		if extension=="mp3" then 
			local to = " \""..PROJ_DIR.."audio".. "/"..basename.."\""
			if animationPanel.open then 
				mkdir(LINK_DIR.."/audio")
				print(os.copy().." "..sourcePath.." "..to)
				os.execute 	(os.copy().." "..sourcePath.." "..to)
				animationPanel.animation.audio = basename
			end
		end

		if extension=="png" or extension=="obj" then

			local from  					= copy and " \""..PROJ_DIR..copy..".png\"" or sourcePath
			local to 

			if extension=="png" then 
				to    					= " \""..PROJ_DIR.."images".. "/"..name..".png\""
			elseif extension=="obj" then 
				to    					= " \""..PROJ_DIR.."models".. "/"..name..".obj\""
			end

			log 				(os.copy().." "..from.." "..to)
			os.execute 	(os.copy().." "..from.." "..to)


			local addedImg 			= imageLoader:loadSymbol(name, destFolder, true)
			objects[addedImg.name].tags 	= destTags
			tagImage 						(objects[addedImg.name])

			if stageHovered then
				log(editor.newEntity(name,mouseLocalDrop).uid)
			end
			
			fileDropped=false
			return name
		end
	end
end

