--import config.lua
--import utils.lua
--import polyUtil.lua
--import uiLib.lua

--import userConfig.lua

ModelVisualizer = {}

CAMERA_PERSPECTIVE = 1
CAMERA_ORTHO = 2

function start()
	ModelVisualizer.viewSize = 80
	ModelVisualizer.validModel = false
	ModelVisualizer.assetRot = 0.0
	ModelVisualizer.assetRotLastTime = 0.0
	ModelVisualizer.modelRot = eulerRot(0.0, 0.0, 0.0)

	-- mouse click-drag rotation
	ModelVisualizer.dragging = false
	ModelVisualizer.lastMousePos = vec2(0.0, 0.0)
	ModelVisualizer.lastInteractTime = 0.0
	ModelVisualizer.panelPos = vec2(0.0, 0.0)
	ModelVisualizer.time = 0.0
	ModelVisualizer.pageId = 1
	ModelVisualizer.bilinearInterp = false
	ModelVisualizer.ditherEnabled = false
	ModelVisualizer.shadingType = 2
	
	ModelVisualizer.cameraMat = mat4()
	ModelVisualizer.cameraMat.position = vec3(0.0, 0.0, 10.0)
	ModelVisualizer.cameraFocusDist = 2.0
	ModelVisualizer.camFov = degToRad(35.0)
	ModelVisualizer.cameraType = CAMERA_PERSPECTIVE

	ModelVisualizer.backgroundSprite = allocSprite()
	ModelVisualizer.backgroundSpriteData = {}
	ModelVisualizer.backgroundSpriteData.position = vec3(0.0, 0.0, UILib.uiDepth_views + 10)
	ModelVisualizer.backgroundSpriteData.size = vec2(ModelVisualizer.viewSize, ModelVisualizer.viewSize)
	ModelVisualizer.backgroundSpriteData.color = Config.uiColors.modelVisualizerBackground

	setSprite(ModelVisualizer.backgroundSprite, ModelVisualizer.backgroundSpriteData)

	ModelVisualizer.bgTextureSprite = allocSprite()
	ModelVisualizer.bgTextureSpriteData = {}
	ModelVisualizer.bgTextureSpriteData.position = vec3(0.0, 0.0, -10.0)
	ModelVisualizer.bgTextureSpriteData.size = vec2(512, 512)
	ModelVisualizer.bgTextureSpriteData.color = vec4(255, 255, 255, 255)
	ModelVisualizer.bgTextureSpriteData.textureId = TEXTURE_MAINBACKGROUND
	ModelVisualizer.bgTextureSpriteData.texturePos = vec2(0.0, 0.0)
	ModelVisualizer.bgTextureSpriteData.textureSize = vec2(512, 512)
	setSprite(ModelVisualizer.bgTextureSprite, ModelVisualizer.bgTextureSpriteData)

	ModelVisualizer.renderRegionPos = vec2(0.0, 0.0)
	ModelVisualizer.renderRegionSize = vec2(ModelVisualizer.viewSize, ModelVisualizer.viewSize)

	ModelVisualizer.meshes = {}

	local programId = getProgramId()
	invokeProgram(MAIN_PROGRAMID, "setViewTitle", programId, "viewer")
	
	_loadLogo()
end

function programActive(state)
	ModelVisualizer.bilinearInterp = invokeProgram(MAIN_PROGRAMID, "getProjectVar", "video_bilinearInterpEnabled")
	ModelVisualizer.ditherEnabled = invokeProgram(MAIN_PROGRAMID, "getProjectVar", "video_ditherEnabled")
end

function setViewPosition(x, y)
	ModelVisualizer.panelPos = vec2(x, y)

	ModelVisualizer.bgTextureSpriteData.position = vec3(-x, -y, -10.0)
	setSprite(ModelVisualizer.bgTextureSprite, ModelVisualizer.bgTextureSpriteData)
end

function _loadLogo()
  if ModelVisualizer.logoModel ~= nil then
    return
  end

	ModelVisualizer.logoAnim = true

  ModelVisualizer.logoModel = loadModel("sys/assets/logo.gltf", -1)[1]

  local models = getModelChildren(ModelVisualizer.logoModel)
  ModelVisualizer.logoMesh = getModelMesh(models[1])

  setMeshMicroShader(ModelVisualizer.logoMesh, "useTexture", 0.0, MICROSHADER_UNIFORM)

	ModelVisualizer.validModel = false
end

function _updateLogo()
	-- animate camera
	local camMat = mat4()
	camMat.position = vec3(0.0, 0.1, 4.0)

	local focalPointMat = mat4()
	focalPointMat.position = vec3(0.0, 0.0, 0.0)
	focalPointMat.rotation = ModelVisualizer.modelRot * eulerRot(0.0, ModelVisualizer.assetRot, 0.0)
	
	ModelVisualizer.cameraMat = focalPointMat * camMat

	local deltaTime = getTime() - ModelVisualizer.assetRotLastTime
	if deltaTime > 0.0 then
		ModelVisualizer.assetRot = ModelVisualizer.assetRot + 1.0

		if ModelVisualizer.assetRot > 360.0 then
			ModelVisualizer.assetRot = ModelVisualizer.assetRot - 360.0
		end

		ModelVisualizer.assetRotLastTime = getTime()
	end

	setCamera(ModelVisualizer.cameraMat, ModelVisualizer.camFov)

	-- draw
	local lightDir = vec3Normalize(ModelVisualizer.cameraMat.position + vec3(1.0, 10.0, 0.0))
	local lightColor = vec4(255.0, 255.0, 255.0, 255.0)
	local ambientColor = vec4(150.0, 150.0, 150.0, 255.0)

	PolyUtil.meshLight(ModelVisualizer.logoMesh, lightDir, lightColor, ambientColor)

	PolyUtil.beginLighting()
	drawMesh(ModelVisualizer.logoMesh)
	PolyUtil.endLighting()
end

function showLogo()
  ModelVisualizer.logoAnim = true
  ModelVisualizer.validModel = false
end

function refreshModel(filename, pageId)
	ModelVisualizer.cameraType = Utils.getAppVar("cameraType") or CAMERA_PERSPECTIVE
	
	ModelVisualizer.shadingType = Utils.getAppVar("polyEditor_shadingType") or 2

	if filename == nil then
		return
	end

 	ModelVisualizer.logoAnim = false
	ModelVisualizer.validModel = false

	if ModelVisualizer.currentModel ~= nil then
		destroy(ModelVisualizer.currentModel)
	end

	ModelVisualizer.currentModel = nil
	ModelVisualizer.meshes = {}

	ModelVisualizer.currentModel = loadModel(filename .. ".gltf", 0)[1]

	_collectMeshes(ModelVisualizer.currentModel, pageId)

	ModelVisualizer.validModel = true
end

function _collectMeshes(model, pageId)
  local mesh = getModelMesh(model)
  
  if mesh ~= nil then
    table.insert(ModelVisualizer.meshes, mesh)

    setMeshTexture(mesh, pageId)
  end
  local children = getModelChildren(model)
  for i = 1, #children do
    _collectMeshes(children[i], pageId)
  end
end

function ModelVisualizer._updateInput()
	local mousePos = getMousePosition()

	local mouseInView = intersectRect(mousePos, 
		vec2(ModelVisualizer.backgroundSpriteData.position.x, ModelVisualizer.backgroundSpriteData.position.y), 
		ModelVisualizer.backgroundSpriteData.size)

	if getMouseButtonPressed(MOUSE_BUTTON_LEFT) == true and mouseInView == true then
		ModelVisualizer.dragging = true
		ModelVisualizer.lastMousePos = mousePos
	end

	-- end the drag whenever the button is no longer held; relying on the
	-- release edge can miss releases that land off this small panel, which
	-- would leave dragging stuck true and prevent auto-spin from resuming
	if getMouseButtonDown(MOUSE_BUTTON_LEFT) == false then
		ModelVisualizer.dragging = false
	end

	if ModelVisualizer.dragging == true then
		local delta = ModelVisualizer.lastMousePos - mousePos

		local speed = 1.0
		-- drag-x rotates around world Y, drag-y around world X (pre-multiply = world frame)
		ModelVisualizer.modelRot = eulerRot(0.0, delta.x * speed, 0.0) * ModelVisualizer.modelRot
		ModelVisualizer.modelRot = eulerRot(delta.y * speed, 0.0, 0.0) * ModelVisualizer.modelRot

		-- reset anchor each frame so the delta stays incremental
		ModelVisualizer.lastMousePos = mousePos

		ModelVisualizer.lastInteractTime = getTime()
	end
end

function ModelVisualizer._updateCamera()
	local bb = computeWorldBoundingBox(ModelVisualizer.currentModel)
	local minPoint = bb.min
	local maxPoint = bb.max

	local size = vec3Length(maxPoint - minPoint)

	local perspDist = size * 1.5

	ModelVisualizer.cameraFocusDist = 1.0 / (perspDist * math.tan(ModelVisualizer.camFov * 0.5))

	-- animate camera rotating it around model
	local camMat = mat4()
	camMat.position = vec3(0.0, 0.0, perspDist)

	local focalPointMat = mat4()
	focalPointMat.position = vec3(
		lerp(minPoint.x, maxPoint.x, 0.5), 
		lerp(minPoint.y, maxPoint.y, 0.5), 
		lerp(minPoint.z, maxPoint.z, 0.5))

	 focalPointMat.rotation = ModelVisualizer.modelRot

	 ModelVisualizer.cameraMat = focalPointMat * camMat

	 if ModelVisualizer.dragging == false then
	 	local deltaTime = getTime() - ModelVisualizer.assetRotLastTime
	 	if deltaTime > 0.05 then
	 		ModelVisualizer.modelRot = eulerRot(0.0, 1.0, 0.0) * ModelVisualizer.modelRot

	 		ModelVisualizer.assetRotLastTime = getTime()
	 	end
	 end
end

function ModelVisualizer._drawModel()
	if ModelVisualizer.bilinearInterp == true then
		setBilinearInterpEnabled(true)
	end

	if ModelVisualizer.cameraType == CAMERA_ORTHO then
		setCameraOrtho(ModelVisualizer.cameraMat, ModelVisualizer.cameraFocusDist)
	else
		setCamera(ModelVisualizer.cameraMat, ModelVisualizer.camFov)
	end

	local lightDir = vec3Normalize(ModelVisualizer.cameraMat.position + vec3(1.0, 10.0, 0.0))
	local lightColor = vec4(255.0, 255.0, 255.0, 255.0)
	local ambientColor = vec4(100.0, 100.0, 100.0, 255.0)

	local lit = true
	if ModelVisualizer.shadingType == 4 then
		lit = false
	end

	if lit then
		PolyUtil.beginLighting()
	end

	for i = 1, MESH_LAYERS do
		local mesh = ModelVisualizer.meshes[i]
		if mesh ~= nil then
			PolyUtil.meshLight(mesh, lightDir, lightColor, ambientColor)

			setMeshMicroShader(mesh, "useTexture", 1.0, MICROSHADER_UNIFORM)

			drawMesh(mesh)
		end
	end

	if lit then
		PolyUtil.endLighting()
	end

	if ModelVisualizer.bilinearInterp == true then
		setBilinearInterpEnabled(false)
	end
end

function update()
	startRenderToSprite(ModelVisualizer.backgroundSprite, ModelVisualizer.renderRegionPos, ModelVisualizer.renderRegionSize)

	setDepthEnabled(false)
	drawSprite2D(ModelVisualizer.bgTextureSprite)
	setDepthEnabled(true)

	if ModelVisualizer.logoAnim == true then
		ModelVisualizer._updateInput()

		_updateLogo()
	end

	if ModelVisualizer.validModel == true then
		ModelVisualizer._updateInput()

		ModelVisualizer._updateCamera()

		ModelVisualizer._drawModel()
	end
	
	if ModelVisualizer.ditherEnabled == true then
		setDitherEnabled(true)
	end

	drawSprite2D(ModelVisualizer.backgroundSprite)

	if ModelVisualizer.ditherEnabled == true then
		setDitherEnabled(false)
	end
end
