-- -----------------------------------------------------------------------------
-- SimNetwork
-- scene.lua
-- 3D scene
-- -----------------------------------------------------------------------------

SimNetwork_SCENE = true
if not SimNetwork_GLOBAL	then dofile("global.lua") end
if not SimNetwork_TOOLBAR	then dofile("toolbar.lua") end

-- Define state of scenes
OBJECTS = {
	cable = E.create_object("data/cable.obj")
}

SCENE_STATE = {
	normal			= 0,	-- initial, nothing selected
	entity_selected		= 1,	-- entity selected
	cable_point_a		= 2,
	cable_point_b		= 3,
	router_drop		= 4,
}

Scene = {}
sceneMt = {__index = Scene}

function Scene:init()
	newScene = {
		rotationX = 0,		-- Current absolute rotaion about X and Y
		rotationY = 0,
		zoom = 1,		-- Current zoom level
		mouseRay = nil,		-- Mouse ray: used for mouse picking
		
		headLight = nil,
		scene3d = nil,		-- pivot for whole scene
		camera3d = nil,		-- root camera
		floor = nil,		-- ground		
		
		selectable = {}	,	-- selectable objects
		categorySelectable = 1,
		categoryFloor = 2,
		categoryMouseRay = 4,
		
		dropRouter = nil,
		routers = {},		-- routers
		cablePoints = {},	-- holds temporary points while cable
					-- being drawn
		selected = nil,		-- currently selected object
		
		cables = {},		-- laid cables
		-- current state of scene
		state = SCENE_STATE.normal
	}
	
	setmetatable(newScene, sceneMt)
	return newScene
end
--------------------------------------------------------------------------------
-- Function: Scene:mousePick()
-- Inputs:	a, b: picked entity and mouse ray in no particular order
--		p: point of picking
-- Returns: none
-- Called by Electro's do_contact() call back to report mouse picking event to
-- handle entity selection in 3D scene
function Scene:mousePick(a, b, p)
	if a == self.floor or b == self.floor then
		-- collision with floor
		if self.state == SCENE_STATE.normal then
			-- ignore
			
		elseif self.state == SCENE_STATE.entity_selected then
			-- deselect entity
			
		elseif self.state == SCENE_STATE.cable_point_a then
			-- position digTool over point A
			E.set_entity_position(digTool, p[1], 0, p[3])
			
			-- drawing a cable, first point, store point
			table.insert(self.cablePoints, p)
			self.state = SCENE_STATE.cable_point_b
			
		elseif self.state == SCENE_STATE.cable_point_b then
			-- position digTool over point A
			E.set_entity_position(digTool, p[1], 0, p[3])
			
			-- drawing cable, second point, store and draw
			table.insert(self.cablePoints, p)
			self:drawCable(self.cablePoints[1], self.cablePoints[2])
			self.cablePoints = {}
			self.state = SCENE_STATE.normal
		end
		
		--[[
		print("Floor collision")
		if isSelectedRouter() then
			print("Router selected")
			-- display router at point of collision
			E.set_entity_position(self.dropRouter, p[1], p[2], 0)
			E.set_entity_flags(self.dropRouter, E.entity_flag_hidden, false)
		end
		--]]
	else
		-- click based collision
		-- change transparency of selected element to 0.5
		if a == self.mouseRay then
			self.selected = b
		else
			self.selected = a
		end
		E.set_entity_alpha(self.selected, 0.5)
	end
	
end

--------------------------------------------------------------------------------
-- Function: Scene:drawCable()
-- Inputs:	pointA, pointB: source and destination
-- Returns:	none
-- Draws a cable between pointA and pointB on (X, Z) floor (will ignore Ys)
function Scene:drawCable(pointA, pointB)

	-- fix Y at 0
	pointA[2] = 0
	pointB[2] = 0
	
	local length = distance(pointA, pointB)
	local newCable = E.create_clone(OBJECTS.cable)
	
	-- calculate mid point of cable
	midX = (pointA[1] + pointB[1]) / 2
	midZ = (pointA[3] + pointB[3]) / 2
	
	-- calculate angle between X and object
	local degree = -1 * math.deg(math.atan((pointA[3] - pointB[3]) / (pointA[1] - pointB[1])))
	print("Cable ", pointA[1], pointA[3], pointB[1], pointB[3], "mid:", midX, midZ, " degree:", degree, "length:", length)
	
	-- render cable on scene
	E.set_entity_scale(newCable, 1, 1, length)
	E.set_entity_position(newCable, midX, 0.5, midZ)
	E.set_entity_rotation(newCable, 0, degree - 90, 0)
	E.parent_entity(newCable, self.headLight)
	
	-- add to list of laid cables
	table.insert(self.cables, newCable)
	
end

--------------------------------------------------------------------------------
-- Function: Scene:enableMouseRay()
-- Inputs:	category: geometry category to assign to mouse ray
-- Returns:	none
-- Enables mouse ray, this function is called whenever we want to detect
-- coll ision between mouse pointer and any other entity in 3D scene. Note that
-- collisions are reported to Scene:mousePick
function Scene:enableMouseRay(category)
	local mouseV = {E.get_camera_vector(gameScene.camera3d, input.MouseX, input.MouseY)}
	local cameraP = {E.get_entity_position(gameScene.camera3d)}
				
	if debug then
		-- print debugging information
		print("Mouse 3D: ", unpack{mouseV})
		print("Cam 3D: ", unpack{cameraP})
	end
				
	-- Enable the mouse ray.
	E.set_entity_geom_type(
		self.mouseRay, 
		E.geom_type_ray, 
		cameraP[1], cameraP[2], cameraP[3],
		unpack(v_mult(mouseV, 100)))
				
	-- enable mouse ray collision
	E.set_entity_geom_attr(gameScene.mouseRay, E.geom_attr_callback, category)
end

function Scene:disableMouseRay()
	E.set_entity_geom_attr(self.mouseRay, E.geom_attr_callback, 0)
	E.set_entity_geom_type(
		self.mouseRay,
		E.geom_type_ray,
		0, 0, 0, 0, 0, 0)
end
--------------------------------------------------------------------------------
-- Function: Scene:scroll()
-- Inputs:	transV: translation vector to apply to camera
-- Returns:	none
-- Scrolls camera on X and Z planes, and zooms/out by translating about Y.
function Scene:scroll(transV)
	E.move_entity(self.camera3d, transV[1], 0, transV[2])
end

--------------------------------------------------------------------------------
-- Function: Scene:rotate()
-- Inputs:	x, y: relative rotational degree about X and Y respectively
-- Returns:	none
-- Rotates the whole 3D scene about X and Y
function Scene:rotate(x, y)
	
	self.rotationX = self.rotationX + x
	self.rotationY = self.rotationY + y
	
	-- restrict rotation about Y
	if self.rotationY > 360 or self.rotationY < -360 then
		-- keep angle in (-360, 360)
		local discard = math.floor(self.rotationY/360)*360
		self.rotationY = self.rotationY - discard
	end
		
	-- restrict rotation about X
	if self.rotationX > 89 then 
		self.rotationX = 89
	elseif self.rotationX < 10 then 
		self.rotationX = 10 
	end
	
	E.set_entity_rotation(self.scene3d, self.rotationX, self.rotationY, 0)
	self:updateSceneGeom()
end

function Scene:changeZoom(level)

	self.zoom = self.zoom + level
	if self.zoom > 0.125 then

		E.set_entity_scale(self.scene3d, self.zoom, self.zoom, self.zoom)
		self:updateSceneGeom()
	end
end

function Scene:addSceneGeom(theEntity, geomType, geomData, geomCategory)
	table.insert(self.selectable, {
		entity = theEntity,
		geom_type = geomType,
		geom_data = geomData,
		geom_category = geomCategory
	})
	
	E.set_entity_geom_attr(entity, E.geom_attr_category, geomCategory)
	
end

function Scene:updateSceneGeom()
	for i,t in self.selectable do
		E.set_entity_geom_type(t.entity, t.geom_type, unpack(v_mult(t.geom_data, self.zoom)))
	end
end

function Scene:loadDemoScene()
	-- create entities
	self.scene3d = E.create_pivot()
	self.camera3d = E.create_camera(E.camera_type_perspective)
	
	--E.set_camera_range(camera3d, 1, 100)
	local lightL = E.create_light(E.light_type_positional)
	local light3d  = E.create_light(E.light_type_positional)
	local lightR = E.create_light(E.light_type_positional)
	self.headLight = E.create_light(E.light_type_positional)	

	local building1 = E.create_object("data/building1.obj")
	local building2 = E.create_object("data/building1.obj")
	local dig = E.create_object("data/dig.obj")
	digTool = E.create_object("data/dig.obj")
	self.floor = E.create_object("data/floor.obj")
	self.dropRouter = E.create_object("data/router.obj")
	
	E.set_entity_scale(self.floor, 1.3, 1, 1.3)
	-- Create SKY 
	local sky = E.create_object("data/sky.obj")

	local envmap = E.create_image("data/sky_nx.png",
                            "data/sky_px.png",
                            "data/sky_ny.png",
                            "data/sky_py.png",
                            "data/sky_nz.png",
                            "data/sky_pz.png")

	-- scale sky
	E.set_entity_scale(sky, 200, 200, 200) 

	-- bind entities
	local buildings = E.create_pivot()
	E.parent_entity(building1, buildings)
	E.parent_entity(building2, buildings)
	E.parent_entity(dig, buildings)
	E.parent_entity(digTool, buildings)
	E.parent_entity(self.dropRouter, buildings)
	E.set_entity_flags(self.dropRouter, E.entity_flag_hidden, true)

	-- group all 3D entities under scene3d
	E.parent_entity(lightL, self.camera3d)
	E.parent_entity(lightR, lightL)
	E.parent_entity(light3d, lightR)
	E.parent_entity(self.scene3d, light3d)
	E.parent_entity(sky,self.floor)

	E.parent_entity(buildings, self.headLight)
	E.parent_entity(self.floor, self.headLight)
	E.parent_entity(self.headLight, self.scene3d)
	E.set_light_color(self.headLight, 1.0, 1.0, 1.0)
	
	-- position elements               
	E.set_entity_position(self.camera3d, 0, 20, 100)
	E.set_entity_position(light3d, 0, 30, 30)
	E.set_entity_position(lightL, -10, 0, 3)
	E.set_entity_position(lightR, 10, 0, 3)
	E.set_entity_position(self.headLight, 0, 20, 100)
	
	E.set_entity_position(building1, 20, 0, 0)
	E.set_entity_position(building2, -30, 0, 16)
	E.set_entity_position(dig,18,0,0)
	E.set_entity_position(digTool,0,0,0)
	
	E.set_brush_image(E.get_mesh(sky, 0), envmap)
	E.set_brush_flags(E.get_mesh(sky, 0), E.brush_flag_unlit,     true)
	E.set_brush_flags(E.get_mesh(sky, 0), E.brush_flag_sky_map_0, true)

	local dig2 = E.create_clone(dig)
	E.parent_entity(dig2,buildings)
	E.set_entity_position(dig2,17,0,0) 
	                  
	local dig3 = E.create_clone(dig)
	E.parent_entity(dig3,buildings)
	E.set_entity_position(dig3,16,0,0) 

	-- Register selectable objects
	local bounds = {2, 2 * 3.2, 2}
	self:addSceneGeom(building1, 
			E.geom_type_box,
			bounds,
			self.categorySelectable)
	
	self:addSceneGeom(building2,
			E.geom_type_box,
			bounds,
			self.categorySelectable)
	
			self:addSceneGeom(self.floor,
			E.geom_type_box,
			{200, 1, 200},
			self.categoryFloor)
	-- update geometry
	self:updateSceneGeom()
	
	-- E.set_entity_geom_attr(floor, E.geom_attr_category, categoryWorld)
			
	-- create mouse ray for mouse picking
	self.mouseRay = E.create_pivot()
	E.parent_entity(self.mouseRay, self.camera3d)
	E.set_entity_geom_attr(self.mouseRay, E.geom_attr_category, self.categoryMouseRay)

	-- turn on geometry visibility, useful for making sure bounding geometry
	-- is appropriate for objects
	--[[
	E.set_entity_flags(floor, E.entity_flag_visible_geom, true)
	E.set_entity_flags(building1, E.entity_flag_visible_geom, true)
	E.set_entity_flags(building2, E.entity_flag_visible_geom, true)
	--]]
end
