package ru.dbotthepony.kstarbound.client.freetype

import com.sun.jna.*
import com.sun.jna.ptr.LongByReference
import ru.dbotthepony.kstarbound.client.freetype.struct.FT_FaceRec
import kotlin.reflect.KProperty

private typealias LPointer = Long

interface IFreeType : Library {
	fun FT_Init_FreeType(handle: LongByReference): Int

	fun FT_New_Face(handle: LPointer, filename: String, faceIndex: NativeLong, aface: LongByReference): Int

	fun FT_Set_Pixel_Sizes(handle: LPointer, width: Int, heigth: Int): Int
	fun FT_Load_Char(handle: LPointer, charCode: Long, flags: Int): Int
}

val FREE_TYPE_JNA: IFreeType = Native.load("freetype-2.11.1", IFreeType::class.java)

private class StateTrackingMemoryAccess<T : Structure>(
	private val state: () -> Int,
	private val factory: () -> T,
) {
	private var value: T? = null
	private var knownState = 0

	operator fun getValue(thisRef: Any, property: KProperty<*>): T {
		if (value == null) {
			value = factory.invoke()
			knownState = state.invoke()
			return value!!
		}

		if (knownState != state.invoke()) {
			value!!.read()
			knownState = state.invoke()
		}

		return value!!
	}
}

class FreeType {
	/**
	 * На самом деле это указатель памяти, где хранится структура
	 */
	private val pointer: LPointer
	var state: Int = 0
		private set

	init {
		val handle = LongByReference()
		throwFreeTypeError(FREE_TYPE_JNA.FT_Init_FreeType(handle))
		pointer = handle.value
	}

	inner class Face(path: String, faceIndex: Long) {
		private val pointer: LPointer
		var state: Int = 0
			private set

		init {
			val handle = LongByReference()
			throwFreeTypeError(FREE_TYPE_JNA.FT_New_Face(this@FreeType.pointer, path, NativeLong(faceIndex), handle))
			pointer = handle.value
			this@FreeType.state++
			state++
		}

		val nativeMemory by StateTrackingMemoryAccess(this::state) lazy@{
			val p = Pointer(pointer)
			val createStruct = FT_FaceRec(p)
			createStruct.read()
			return@lazy createStruct
		}

		fun setSize(width: Int, heigth: Int) {
			throwFreeTypeError(FREE_TYPE_JNA.FT_Set_Pixel_Sizes(pointer, width, heigth))
			this@FreeType.state++
			state++
		}

		fun loadChar(charCode: Char, vararg flags: LoadFlag) {
			var lflags = 0L
			flags.forEach { lflags = lflags or it.value }

			throwFreeTypeError(FREE_TYPE_JNA.FT_Load_Char(pointer, charCode.code.toLong(), lflags.toInt() or 4))
			this@FreeType.state++
			state++
		}
	}
}
