package sectr

import    "base:runtime"
import  c "core:c/libc"
import    "core:dynlib"
import    "core:mem"
import    "core:mem/virtual"
import    "core:os"
import    "core:slice"
import    "core:strings"
import rl "vendor:raylib"

Path_Assets       :: "../assets/"
Path_Input_Replay :: "scratch.sectr_replay"

ModuleAPI :: struct {
	lib         : dynlib.Library,
	write_time  : FileTime,
	lib_version : i32,

	startup    : type_of( startup ),
	shutdown   : type_of( sectr_shutdown ),
	reload     : type_of( reload ),
	tick       : type_of( tick ),
	clean_temp : type_of( clean_temp ),
}

@export
startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ Logger )
{
	logger_init( & Memory_App.logger, "Sectr", host_logger.file_path, host_logger.file )
	context.logger = to_odin_logger( & Memory_App.logger )

	// Setup memory for the first time
	{
		arena_size     :: size_of( Arena)
		internals_size :: 4 * Megabyte

		using Memory_App;
		block := live_mem.curr_block

		live     = live_mem
		snapshot = snapshot_mem

		persistent_slice := slice_ptr( block.base, Memory_Persistent_Size )
		transient_slice  := slice_ptr( memory_after( persistent_slice), Memory_Trans_Temp_Szie )
		temp_slice       := slice_ptr( memory_after( transient_slice),  Memory_Trans_Temp_Szie )

		when Use_TrackingAllocator {
			// We assign the beginning of the block to be the host's persistent memory's arena.
			// Then we offset past the arena and determine its slice to be the amount left after for the size of host's persistent.
			persistent = tracked_allocator_init_vmem( persistent_slice, internals_size )
			transient  = tracked_allocator_init_vmem( transient_slice,  internals_size )
			temp       = tracked_allocator_init_vmem( temp_slice ,      internals_size )
		}
		else {
			persistent = arena_allocator_init_vmem( persistent_slice )
			transient  = arena_allocator_init_vmem( transient_slice )
			temp       = arena_allocator_init_vmem( temp_slice )
		}

		context.allocator      = transient_allocator()
		context.temp_allocator = temp_allocator()
	}

	state := new( State, persistent_allocator() )
	using state

	context.user_ptr = state

	input      = & input_data[1]
	input_prev = & input_data[0]

	// rl.Odin_SetMalloc( RL_MALLOC )

	rl.SetConfigFlags( {
		rl.ConfigFlag.WINDOW_RESIZABLE,
		// rl.ConfigFlag.WINDOW_TOPMOST,
	})

	// Rough setup of window with rl stuff
	window_width  : i32 = 1000
	window_height : i32 = 600
	win_title     : cstring = "Sectr Prototype"
	rl.InitWindow( window_width, window_height, win_title )
	log( "Raylib initialized and window opened" )

	window := & state.app_window
	window.extent.x = f32(window_width)  * 0.5
	window.extent.y = f32(window_height) * 0.5

	// We do not support non-uniform DPI.
	window.dpi_scale = rl.GetWindowScaleDPI().x
	window.ppcm      = os_default_ppcm * window.dpi_scale

	// Determining current monitor and setting the target frametime based on it..
	monitor_id         = rl.GetCurrentMonitor()
	monitor_refresh_hz = rl.GetMonitorRefreshRate( monitor_id )
	rl.SetTargetFPS( monitor_refresh_hz )
	log( str_fmt_tmp( "Set target FPS to: %v", monitor_refresh_hz ) )

	// Basic Font Setup
	{
		font_provider_startup()
		// path_rec_mono_semicasual_reg := strings.concatenate( { Path_Assets, "RecMonoSemicasual-Regular-1.084.ttf" })
		// font_rec_mono_semicasual_reg  = font_load( path_rec_mono_semicasual_reg, 24.0, "RecMonoSemiCasual_Regular" )

		// path_squidgy_slimes := strings.concatenate( { Path_Assets, "Squidgy Slimes.ttf" } )
		// font_squidgy_slimes = font_load( path_squidgy_slimes, 24.0, "Squidgy_Slime" )

		path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" }, temp_allocator() )
		font_firacode  = font_load( path_firacode, 24.0, "FiraCode" )

		// font_data, read_succeded : = os.read_entire_file( path_rec_mono_semicasual_reg  )
		// verify( read_succeded, fmt.tprintf("Failed to read font file for: %v", path_rec_mono_semicasual_reg) )

		// cstr                         := strings.clone_to_cstring( path_rec_mono_semicasual_reg )
		// font_rec_mono_semicasual_reg  = rl.LoadFontEx( cstr, cast(i32) points_to_pixels(24.0), nil, 0 )
		// delete( cstr)

		// rl.GuiSetFont( font_rec_mono_semicasual_reg ) // TODO(Ed) : Does this do anything?
		default_font = font_firacode
		log( "Default font loaded" )
	}

	// Demo project setup
	{
		using project
		path           = "./"
		name           = "First Project"
		workspace.name = "First Workspace"
		{
			using project.workspace
			cam = {
				target   = { 0, 0 },
				offset   = transmute(Vec2) window.extent,
				rotation = 0,
				zoom     = 1.0,
			}
			// cam = {
			// 	position   = { 0, 0, -100 },
			// 	target     = { 0, 0, 0 },
			// 	up         = { 0, 1, 0 },
			// 	fovy       = 90,
			// 	projection = rl.CameraProjection.ORTHOGRAPHIC,
			// }

			// Setup workspace UI state
			ui_startup( & workspace.ui, persistent_allocator() )
		}
	}
}

// For some reason odin's symbols conflict with native foreign symbols...
@export
sectr_shutdown :: proc()
{
	if Memory_App.persistent == nil {
		return
	}
	state := get_state()

	// Replay
	{
		os.close( Memory_App.replay.active_file )
	}

	font_provider_shutdown()

	log("Module shutdown complete")
}

@export
reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ Logger )
{
	using Memory_App;
	block := live_mem.curr_block

	live     = live_mem
	snapshot = snapshot_mem

	// This is no longer necessary as we have proper base address setting
	when true
	{
		persistent_slice := slice_ptr( block.base, Memory_Persistent_Size )
		transient_slice  := slice_ptr( memory_after( persistent_slice), Memory_Trans_Temp_Szie )
		temp_slice       := slice_ptr( memory_after( transient_slice),  Memory_Trans_Temp_Szie )

		when Use_TrackingAllocator {
			persistent = cast( ^ TrackedAllocator ) & persistent_slice[0]
			transient  = cast( ^ TrackedAllocator ) & transient_slice[0]
			temp       = cast( ^ TrackedAllocator ) & temp_slice[0]
		}
		else {
			persistent = cast( ^ Arena ) & persistent_slice[0]
			transient  = cast( ^ Arena ) & transient_slice[0]
			temp       = cast( ^ Arena ) & temp_slice[0]
		}
	}
	context.allocator      = transient_allocator()
	context.temp_allocator = temp_allocator()

	// Procedure Addresses are not preserved on hot-reload. They must be restored for persistent data.
	// The only way to alleviate this is to either do custom handles to allocators
	// Or as done below, correct containers using allocators on reload.
	// Thankfully persistent dynamic allocations are rare, and thus we know exactly which ones they are.

	// font_provider_data := & get_state().font_provider_data
	// font_provider_data.font_cache.allocator = arena_allocator( & font_provider_data.font_arena )

	ui_reload( & get_state().project.workspace.ui, persistent_allocator() )

	log("Module reloaded")
}

// TODO(Ed) : This lang really not have a fucking swap?
swap :: proc( a, b : ^ $Type ) -> ( ^ Type, ^ Type ) {
	return b, a
}

@export
tick :: proc( delta_time : f64, delta_ns : Duration ) -> b32
{
	context.allocator      = transient_allocator()
	context.temp_allocator = temp_allocator()

	get_state().frametime_delta_ns = delta_ns

	result := update( delta_time )
	render()
	return result
}

@export
clean_temp :: proc() {
	when Use_TrackingAllocator {
		mem.tracking_allocator_clear( & Memory_App.temp.tracker )
	}
	else {
		free_all( temp_allocator() )
	}
}