import Foundation
import OpenGL.GL


public protocol Renderer
{
	func create(sdlWindow: OpaquePointer) throws
	func delete()

	func newFrame()
	func resize(width: Int32, height: Int32)

	var clearColour: Colour { get set }
	var wireframe: Bool { get set }

	func setVsync(mode: VSyncMode) throws

	func createMesh<V: Vertex>(mesh: Mesh<V>) throws -> RenderMesh<V>
	func createTexture(data: UnsafeRawPointer, width: Int, height: Int) throws -> RenderTexture2D
	func createTexture(data: UnsafeRawPointer, width: Int, height: Int,
		filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D

	func deleteMesh<V: Vertex>(_ mesh: RenderMesh<V>)
	func deleteTexture(_ texture: RenderTexture2D)

	func setProjection(matrix: Mat4f)
	func setView(matrix: Mat4f)

	func setMaterial(_ mat: Material)

	func draw<V: Vertex>(mesh: RenderMesh<V>, model: Mat4f, environment: Environment)
	func draw<V: Vertex>(mesh: RenderMesh<V>, environment: Environment)
	func draw<V: Vertex>(mesh: RenderMesh<V>, subMesh: Mesh<V>.SubMesh, environment: Environment)

	func drawGizmos(lines: [Line])
}

public enum RendererError: Error
{
	case sdlError(message: String)
}

public enum VSyncMode
{
	case off
	case on
	case adaptive
}

public enum FilterMode
{
	case point
	case linear
}

public enum MipMode
{
	case off
	case nearest
	case linear
}

public enum WrapMode
{
	case clamping
	case clampBorder
	case clampEdge
	case mirrored
	case repeating
}

public protocol RendererResource
{
	associatedtype T: Resource

	static var empty: Self { get }

	var isValid: Bool { get }
}

public struct RenderMesh<V: Vertex>: RendererResource
{
	public typealias T = Mesh<V>

	public static var empty: RenderMesh { .init() }

	public var isValid: Bool { _valid }

	private let _valid: Bool;
	let vboHnd: Int, iboHnd: Int
	public let subMeshes: [Mesh<V>.SubMesh]
	public let materials: [Material]  // HACK !!!!

	private init()
	{
		self._valid = false
		self.vboHnd = 0
		self.iboHnd = 0
		self.subMeshes = []
		self.materials = []
	}

	init(vbo: Int, ibo: Int, subMeshes: [Mesh<V>.SubMesh], materials: [Material] = .init())
	{
		self._valid = true
		self.vboHnd = vbo
		self.iboHnd = ibo
		self.subMeshes = subMeshes
		self.materials = materials
	}
}

public struct RenderTexture2D: RendererResource
{
	public typealias T = Texture2D

	public static var empty: Self { .init(0) }

	public var isValid: Bool { id != 0 }

	let id: UInt32

	init(_ id: UInt32)
	{
		self.id = id
	}
}

public struct Line
{
	let from: Vec3f, to: Vec3f, colour: Colour

	public init(from: Vec3f, to: Vec3f, colour: Colour)
	{
		self.from = from
		self.to = to
		self.colour = colour
	}
}
