require 'luagl'
require 'luaglut'
require 'memarray'
require 'config'

local res_w = 0
local res_h = 0
local res_override = 0

local quit = false
local fps = 60 
local fps_str = ''
local msec = 1000 / fps
local frames = 0
local cur_fps = 0
local start_time
local fps_time
local elapsed_time
local millis = 0
local row
local order
local ppm, width, height
local debugmode = 0
local sync_set = 0
local draw_mode = 'solid'
local fps_mode  = 'fixed'
local cross_size = 0
local debug_pause = 1
local seeked = 0
local song_time_end = 0

local effects = {}
local timeline = {}

local spectrum = {}

local mouse_x = 0
local mouse_y = 0

local mouse_over_slider = 0

local slider_x = 10
local slider_y = 10
local slider_w = 20
local slider_h = 10
local slider_bar_pos = 0

local function glutBitmapString(font, str)                                                                                                   
        for i = 1, string.len(str) do
                glutBitmapCharacter(font, string.byte(str, i))
        end
end


function engine_load_effects()
	local fxlist = config_effects()
	for _, effect in pairs(fxlist) do
		--print("Loading effect: ".. effect.name)
		res, errmsg = loadfile(effect.file)
		if errmsg == nil then
			res()
			effect.load = loadstring("return "..effect.name.."_load()")
			effect.unload = loadstring("return "..effect.name.."_unload()")
			effect.init = loadstring("return "..effect.name.."_init")()
			effect.deinit = loadstring("return "..effect.name.."_deinit()")
			effect.paint = loadstring("return "..effect.name.."_paint")()
			effect.load()
		else
			--print("engine_load_effects: " .. errmsg)
			return false
		end
	end
	
	effects = fxlist
	
	return engine_make_timeline()
end

function engine_get_effect(name) 
	for _, effect in pairs(effects) do
		if effect.name == name then
			return effect
		end
	end
	return false
end

function engine_make_timeline()
	temptimeline = {}
	temptimeline.current = nil
	temptimeline.current_start = nil
	temptimeline.current_end = nil
	temptimeline.effects = {}
	
	for _, timing in pairs(config_timing()) do
		-- temptimeline.effects[] = engine_get_effect(timing.effect) 
		
		table.insert(temptimeline.effects, #temptimeline.effects + 1, timing)
		
	end

	table.sort(temptimeline.effects, function(a,b) return tonumber(a.start) <= tonumber(b.start) end)
	
	
	for _, timing in pairs(temptimeline.effects) do
		--print(timing.start .. ": " ..timing.effect)
	end
	

	
	timeline = temptimeline
	
	
	
	engine_next_effect()
	
	return true
end

function engine_spectrum() 
	
		temp = song_spectrum()
		for i = 0, 511 do
			spectrum[i] = 0.001 + (temp[i] * 0.7)
		end
	
	return spectrum;
end

function engine_next_effect()
	local selected = nil
	local selected_start = 0
	local selected_end = nil

	for _, timing in pairs(timeline.effects) do
		if millis >= timing.start then 
			selected = timing.effect
			selected_start = timing.start
			selected_params = timing.params
		else
			if selected_end == nil then
				selected_end = timing.start
				break
			end
		end
		
	end
	
	selected = engine_get_effect(selected)
	
	--print(millis .. " switch to " .. selected.name)
	
	if selected_end == nil then
		selected_end = 999999
	end
	
	last = timeline.current
	
	timeline.current_start = millis
	timeline.current_end = selected_end
	timeline.current_params = selected_params
	timeline.current = selected
	
	if timeline.current == last then
		--print(millis .. " same effect, not calling init")
	else
		timeline.current.init(millis)
	end
	
end

function engine_main() 

	res_override, res_w, res_h = demo_getresolution()
	if res_override == 1 then
		print('-- Window size: ' .. res_w .. ' x ' .. res_h .. ' --')
	end
	
	for i = 0, 511 do
		spectrum[i] = 0.5
	end

	glutInit(arg)
	if res_override == 1 then
		glutInitWindowSize(res_w, res_h)
	end
	glutInitDisplayMode(GLUT_RGB + GLUT_DOUBLE + GLUT_DEPTH + GLUT_MULTISAMPLE)
	if arg then title = arg[0] else title = "glut" end
	window = glutCreateWindow(title)
	if res_override == 0 then
		glutFullScreen()
	end
	
	
	glutSetCursor(GLUT_CURSOR_NONE)
	glutDisplayFunc(display_func)
	glutKeyboardFunc(keyboard_func)
	glutReshapeFunc(resize_func)
	glutTimerFunc(msec, timer_func, 0)
	
	-- rodent stuff
	glutMouseFunc(mouse_func)
	glutMotionFunc(mouse_motion)
	glutPassiveMotionFunc(mouse_motion)
	
	
	start_time = glutGet(GLUT_ELAPSED_TIME)
	fps_time = start_time
	
	print('-- Trilobit ScriPtable Lua Interactive MultiplatForm (SPLIF) version 0.1 --')
	print('-- Running on Lua: "' .. _VERSION .. '" LuaGL: "' .. luagl.VERSION .. '" LuaGLUT: "' .. luaglut.VERSION .. '" FMOD Ex: "' .. song_getfmodversion() .. '" --')
	print('Starting up...')
	
	song_time_end = song_getlength()

	if engine_load_effects() then
		song_play()
		glutMainLoop()
	end
	
	
end

function load_texture(fname)
   local f, width, height, depth, data

   f = assert(io.open(fname, 'rb'))
   assert(f:read('*l') == 'P6')
   width = f:read('*n')
   height = f:read('*n')
   depth = f:read('*l')
   depth = f:read('*l')
   data = f:read('*a')
   f:close()

   local ppm = memarray('uchar', width * height * 3)
   for y = 1, height do
      local dst_idx = (height - y) * width * 3
      local src_idx = (y - 1) * width * 3
      for x = 0, width - 1 do
         ppm[dst_idx + 3*x + 0] = string.byte(data, src_idx + 3*x + 1)
         ppm[dst_idx + 3*x + 1] = string.byte(data, src_idx + 3*x + 2)
         ppm[dst_idx + 3*x + 2] = string.byte(data, src_idx + 3*x + 3)
      end
   end

   return ppm, width, height
end

function debug()
	glDisable(GL_BLEND)
	glDisable(GL_TEXTURE_2D)
	glMatrixMode(GL_PROJECTION)
	glPushMatrix()
	glLoadIdentity()
	glOrtho(0, glutGet(GLUT_WINDOW_WIDTH), 0, glutGet(GLUT_WINDOW_HEIGHT), -3, 3)
	if debugmode == 1 then
		if sync_set > 0 then
			sync_str = '[sync #' .. sync_set .. ' set]'
		else
			sync_str = ''
		end
		fps_str = 'effect: '.. timeline.current.name ..' millis: [' .. millis .. '] order: [' .. order .. '] row: [' .. row .. '] fps: [' .. cur_fps .. '] ' .. sync_str
		glColor3d(1,1,1)
		glRasterPos2d(2,glutGet(GLUT_WINDOW_HEIGHT)-16)
		glutBitmapString(GLUT_BITMAP_8_BY_13, fps_str) 
		glColor3d(0.2,0.2,0.2)
		glRasterPos2d(2,glutGet(GLUT_WINDOW_HEIGHT)-30)
		glutBitmapString(GLUT_BITMAP_8_BY_13, fps_str )
		
		-- susiruma cursori, tein itse, säästin
		glColor3d(1,1,0.2)
		y1=glutGet(GLUT_WINDOW_HEIGHT)-mouse_y;
		glBegin(GL_TRIANGLES)
		glVertex3d(mouse_x, y1,0)
		glVertex3d(mouse_x+10, y1,0)
		glVertex3d(mouse_x, y1-10,0)
		glEnd()
		
		draw_song_slider()
	end
	glPopMatrix()
end

function draw_song_slider() 
	local song_duration = song_getlength()
	
	
	
	
	
	slider_bar_pos = slider_w * (millis/song_duration)
	
	
	slider_x = 10
	slider_y = 10
	slider_w = glutGet(GLUT_WINDOW_WIDTH)-20
	slider_h = 10
	
	if mouse_over_slider == 1 then
		glColor3d(0.9,0.9,0.9)
	else
		glColor3d(0.5,0.5,0.5)
	end
	
	glBegin(GL_QUADS)
	
	glVertex3d(slider_x, slider_y,-0.1)
	glVertex3d(slider_x+slider_w, slider_y,-0.1)
	glVertex3d(slider_x+slider_w, slider_y+slider_h,-0.1)
	glVertex3d(slider_x, slider_y+slider_h,-0.1)
	
	glEnd();
	
	glColor3d(0,0,0)
	
	glBegin(GL_QUADS)
	
	glVertex3d(slider_x+slider_bar_pos, slider_y,0)
	glVertex3d(slider_x+slider_bar_pos+5, slider_y,0)
	glVertex3d(slider_x+slider_bar_pos+5, slider_y+slider_h,0)
	glVertex3d(slider_x+slider_bar_pos, slider_y+slider_h,0)
	
	glEnd();
	
end

function display_func()
	if quit then return end

	elapsed_time = glutGet(GLUT_ELAPSED_TIME) - fps_time
	millis = song_getmillis()
	row = song_getrow()
	order = song_getorder()
	
	if seeked == 1 then
		--print("seek check")
		engine_next_effect()
		seeked = false
	end

	if millis >= timeline.current_end then
		engine_next_effect()
	end
	
	timeline.current.paint(millis, timeline.current_params)
	
	debug()

	glutSwapBuffers()

	if (millis >= song_time_end) then
                quit = true
                glutDestroyWindow(window)
                os.exit(0)
	end

	frames = frames + 1
	if elapsed_time > 1000 then
		cur_fps = frames * 1000 / elapsed_time
		fps_time = fps_time + elapsed_time
		frames = 0
	end
end


function resize_func(w, h)
	local ratio = w / h
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	if w < h then
		glViewport(0,0,w,w)
		ratio = w/w
	else
		local center_x = 0
		if w > 1600 then 
			center_x = (w / 2) - 800
			w = 1600
		end
		if h > 1200 then h = 1200
		end
		
		ratio = w / h
		glViewport(center_x,0,w,h)
	end
	gluPerspective(45,ratio,1,10000)
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()
	glEnable(GL_DEPTH_TEST)
	glEnable(GL_NORMALIZE)
end

function timer_func()
	if quit then return end

	glutSetWindow(window)
	glutTimerFunc(msec, timer_func, 0)
	glutPostRedisplay()
end

function keyboard_func(key,x,y)
	if key == 27 then
		quit = true
		glutDestroyWindow(window)
		os.exit(0)
	end
	if key == 32 then
		debugmode = -debugmode
	end

	
	if debugmode == 1 then
	
		if key == 114 then
			--print("reload")
			song_play()
			millis = song_getmillis()
			row = song_getrow()
			order = song_getorder()
			engine_load_effects()
		end 
		-- syncpoints
		if key >= 49 and key <= 57 then
			--print('sync #' .. key-48 .. ', millis: ' .. millis .. ' order: ' .. order .. ' row: ' .. row)
			sync_set = key-48
		end

		-- song seeking
		if key == 97 then
			seeked = 1
			song_seek(0)
		end
		if key == 100 then
			seeked = 1
			song_seek(1)
		end
		if key == 119 then
			seeked = 1
			song_seek(2)
		end
		if key == 115 then
			seeked = 1
			song_seek(3)
		end
		
		if key == 122 then
			song_seek(4)
		end
		if key == 120 then
			song_seek(5)
		end
		if key == 112 then
			if debug_pause == 1 then 
				song_pause(0)
			else 
				song_pause(1)
			end
			debug_pause = -debug_pause
		end
	end


end

function mouse_func(button, state, x , y) 
	--print ("mouse_func button = " .. button .. " state = " .. state .. " x = " .. x .. " y = " .. y)
	mouse_x = x
	mouse_y = y
end

function mouse_entry(state) 
	--print ("mouse_entry state=" .. state)
end

function mouse_motion(x, y) 
	--print ("mouse move x = " .. x .. " y = " .. y)
	mouse_x = x
	mouse_y = y
	
	if debugmode == 1 then
		mouse_over_slider = 0
		
		-- glunproject mouse coordz
	
	end
end

engine_main()




