enum Theorafile_VideoState
{
	Stopped,
	Playing,
	Paused
}

function Theorafile_Open(filename)
{
	var _handle = ptr(TheorafileGMS_Open(filename));
	show_debug_message("Successfully loaded Theorafile video with handle: " + string(_handle));
	
	var _yWidth = TheorafileGMS_Width(_handle);
	var _yHeight = TheorafileGMS_Height(_handle);
	var _frames_per_second = TheorafileGMS_FPS(_handle);
	var _uvWidth = TheorafileGMS_UVWidth(_handle);
	var _uvHeight = TheorafileGMS_UVHeight(_handle);
	
	var _bufferSize = TheorafileGMS_RequiredBufferSizeInBytes(_handle);
	
	vertex_format_begin();
	vertex_format_add_position();
	vertex_format_add_texcoord();
	var _vformat = vertex_format_end();
	
	var _vbuff = vertex_create_buffer();
	vertex_begin(_vbuff, _vformat);
	vertex_position(_vbuff, -1, 1);
	vertex_texcoord(_vbuff, 0, 0);
	vertex_position(_vbuff, 3, 1);
	vertex_texcoord(_vbuff, 2, 0);
	vertex_position(_vbuff, -1, -3);
	vertex_texcoord(_vbuff, 0, 2);
	vertex_end(_vbuff);
	
	var _video = {
		handle: _handle,
		bufferSize: _bufferSize,
		yuvBuffer: buffer_create(_bufferSize, buffer_fixed, 4),
		yWidth: _yWidth,
		yHeight: _yHeight,
		uvWidth: _uvWidth,
		uvHeight: _uvHeight,
		framesPerSecond: _frames_per_second,
		ySurface: surface_create(_yWidth, _yHeight),
		uSurface: surface_create(_uvWidth, _uvHeight),
		vSurface: surface_create(_uvWidth, _uvHeight),
		videoSurface: surface_create(_yWidth, _yHeight),
		elapsed_time: 0,
		currentFrame: 0,
		state: Theorafile_VideoState.Stopped,
		looping: false,
		loopCounter: 0,
		timesToLoop: 0,
		triangle_buffer: _vbuff,
		triangle_buffer_format: _vformat
	};
	
	// grab the first frame ASAP
	while (TheorafileGMS_ReadVideo(_video.handle, buffer_get_address(_video.yuvBuffer), 1) == 0) { }
	
	return _video;
}

function Theorafile_Close(video)
{
	surface_free(video.ySurface);
	surface_free(video.uSurface);
	surface_free(video.vSurface);
	surface_free(video.videoSurface);
	buffer_delete(video.yuvBuffer);
	vertex_format_delete(video.triangle_buffer_format);
	vertex_delete_buffer(video.triangle_buffer);
	TheorafileGMS_Close(video.handle);
}

function Theorafile_Play(video)
{
	if (video.state != Theorafile_VideoState.Playing)
	{
		if (video.handle != 0 && TheorafileGMS_HasVideo(video.handle) == 1)
		{
			video.state = Theorafile_VideoState.Playing;
		}
	}
}

function Theorafile_SetLoop(video, loop = true, times = 10000000000000)
{
	video.looping = loop;
	video.loopCounter = 0;
	video.timesToLoop = times;
}

function Theorafile_Reset(video)
{
	TheorafileGMS_Reset(video.handle);
	// grab the first frame ASAP
	while (TheorafileGMS_ReadVideo(video.handle, buffer_get_address(video.yuvBuffer), 1) == 0) { }
	
	video.elapsed_time = 0;
	video.currentFrame = 0;
}

function Theorafile_Pause(video)
{
	video.state = Theorafile_VideoState.Paused;	
}

function Theorafile_Stop(video)
{
	Theorafile_Reset(video);
	video.state = Theorafile_VideoState.Stopped;
}

function Theorafile_UpdateTextures(video)
{
	if (!surface_exists(video.ySurface))
	{
		video.ySurface = surface_create(video.yWidth, video.yHeight);
	}
	
	if (!surface_exists(video.uSurface))
	{
		video.uSurface = surface_create(video.uvWidth, video.uvHeight);	
	}
	
	if (!surface_exists(video.vSurface))
	{
		video.vSurface = surface_create(video.uvWidth, video.uvHeight);	
	}
	
	var _y_data_length = (video.yWidth * video.yHeight * 4);
	var _uv_data_length = (video.uvWidth * video.uvHeight * 4);
	
	buffer_set_surface(video.yuvBuffer, video.ySurface, 0);
	buffer_set_surface(video.yuvBuffer, video.uSurface, _y_data_length);
	buffer_set_surface(video.yuvBuffer, video.vSurface, _y_data_length + _uv_data_length);
}

function Theorafile_UpdateSurface(video)
{
	if (video.state == Theorafile_VideoState.Playing)
{
		video.elapsed_time += 1 / room_speed;

		var _current_frame = floor(video.elapsed_time * video.framesPerSecond);

		if (_current_frame > video.currentFrame)
		{
			if TheorafileGMS_ReadVideo(video.handle, buffer_get_address(video.yuvBuffer), _current_frame - video.currentFrame) >= 1
			{
				Theorafile_UpdateTextures(video);
				
				if !surface_exists(video.videoSurface)
				{
					video.videoSurface = surface_create(video.yWidth, video.yHeight);	
				}
				
				surface_set_target(video.videoSurface);
				shader_set(YUVToRGB);
				
				ySampler = shader_get_sampler_index(YUVToRGB, "y_tex");
				uSampler = shader_get_sampler_index(YUVToRGB, "u_tex");
				vSampler = shader_get_sampler_index(YUVToRGB, "v_tex");
				texture_set_stage(ySampler, surface_get_texture(video.ySurface));
				texture_set_stage(uSampler, surface_get_texture(video.uSurface));
				texture_set_stage(vSampler, surface_get_texture(video.vSurface));
				
				vertex_submit(video.triangle_buffer, pr_trianglelist, -1);
				
				shader_reset();
				surface_reset_target();
			}
			
			video.currentFrame = _current_frame;
		}
		
		var _ended = TheorafileGMS_EndOfStream(video.handle);
		
		if (_ended)
		{
			Theorafile_Reset(video);
			
			if (video.looping)
			{
				video.loopCounter += 1;
				if video.loopCounter > video.timesToLoop
				{
					video.looping = false;
					video.state = Theorafile_VideoState.Stopped;
				}
			}
			else
			{
				video.state = Theorafile_VideoState.Stopped;	
			}
		}
	}
}
