import com.badlogic.gdx.*
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.utils.ScreenUtils
import com.badlogic.gdx.utils.StreamUtils
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
import java.io.File
import java.io.IOException
import java.io.OutputStream

/**
 * Created by minjaesong on 2018-07-26.
 */
class FontTestGDX : Game() {

    lateinit var font: TerrarumSansBitmap

    lateinit var inputText: List<String>

    lateinit var batch: SpriteBatch

    lateinit var frameBuffer: FrameBuffer

    lateinit var camera: OrthographicCamera

    private val testing = false

    private val demotextName = if (testing) "testtext.txt" else "demotext.txt"
    private val outimageName = if (testing) "testing.PNG" else "demo.PNG"

    private lateinit var faketex: Texture

    private val lineHeight = 24

    override fun create() {
        font = TerrarumSansBitmap("./assets", debug = true, flipY = false, errorOnUnknownChar = false, shadowAlpha = 0.5f) // must test for two flipY cases

        val inTextFile = Gdx.files.internal("./$demotextName")
        val reader = inTextFile.reader("UTF-8")
        inputText = reader.readLines()
        reader.close()

        batch = SpriteBatch()


        // create faketex
        val fakepix = Pixmap(1,1,Pixmap.Format.RGBA8888)
        fakepix.drawPixel(0,0,-1)
        faketex = Texture(fakepix)
        fakepix.dispose()

        frameBuffer = FrameBuffer(Pixmap.Format.RGBA8888, TEXW, TEXH, true)

        camera = OrthographicCamera(TEXW.toFloat(), TEXH.toFloat())
        camera.translate(TEXW.div(2f), 0f)
        camera.setToOrtho(true, TEXW.toFloat(), TEXH.toFloat())
        camera.update()


        Gdx.input.inputProcessor = Navigator(this)
    }

    override fun getScreen(): Screen? {
        return null
    }

    var scrollOffsetY = 0f

    override fun setScreen(screen: Screen?) {
    }

    var tex: Texture? = null
    var screenshotExported = false

    private val backcol = Color(.141f, .141f, .141f, 1f)

    override fun render() {

        if (tex == null) {
            frameBuffer.begin()

            Gdx.gl.glClearColor(.141f, .141f, .141f, 1f)
            Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
            Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
            Gdx.gl.glEnable(GL20.GL_BLEND)
            batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA) // for not premultiplied textures

            batch.projectionMatrix = camera.combined
            batch.begin()

            batch.color = backcol
            batch.draw(faketex, 0f, 0f, TEXW.toFloat(), TEXH.toFloat())
            batch.flush()

            batch.color = Color.WHITE
            inputText.forEachIndexed { index, s ->
                font.draw(batch, s, 10f, TEXH - 30f - index * lineHeight)
            }

            batch.end()


            // dump to file
            if (!screenshotExported) {
                val pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, frameBuffer.width, frameBuffer.height)

                PixmapIO.writePNG(Gdx.files.local(outimageName), pixmap)
//                writeTGA(Gdx.files.local(outimageName), pixmap, false)
                pixmap.dispose()

                screenshotExported = true
            }


            frameBuffer.end()

            ///////////////

            tex = frameBuffer.colorBufferTexture
        }


        Gdx.gl.glClearColor(.141f, .141f, .141f, 1f)
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
        Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
        Gdx.gl.glEnable(GL20.GL_BLEND)
        batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA) // for not premultiplied textures


        batch.begin()
        batch.color = Color.WHITE
        batch.draw(tex, 0f, (TEXH.toFloat()/HEIGHT)*TEXH - scrollOffsetY, TEXW.toFloat(), -(TEXH.toFloat() / HEIGHT) * TEXH.toFloat())


        batch.end()
    }

    override fun pause() {
    }

    override fun resume() {
    }

    override fun resize(width: Int, height: Int) {
    }

    override fun dispose() {
        font.dispose()
        faketex.dispose()
    }

    fun scrollAdd(x: Int = 1) {
        scrollOffsetY -= (TEXH.toFloat() / HEIGHT) * lineHeight * x
    }

    fun scrollSub(x: Int = 1) {
        scrollOffsetY += (TEXH.toFloat() / HEIGHT) * lineHeight * x
    }

    class Navigator(val main: FontTestGDX) : InputAdapter() {
        override fun scrolled(amountX: Float, amountY: Float): Boolean {
            if (amountY >= 0)
                main.scrollSub(amountY.toInt())
            else
                main.scrollAdd(-amountY.toInt())

            return true
        }

        override fun keyDown(keycode: Int): Boolean {
            if (keycode == Input.Keys.UP)
                main.scrollAdd()
            else if (keycode == Input.Keys.DOWN)
                main.scrollSub()

            return true
        }
    }


    @Throws(IOException::class)
    private fun writeTGA(file: FileHandle, pixmap: Pixmap, flipY: Boolean) {
        val output = file.write(false)
        try {
            _writeTGA(output, pixmap, true, flipY)
        } finally {
            StreamUtils.closeQuietly(output)
        }
    }

    @Throws(IOException::class)
    private fun _writeTGA(out: OutputStream, pixmap: Pixmap, verbatim: Boolean, flipY: Boolean) {
        val width: ByteArray =  toShortLittle(pixmap.width)
        val height: ByteArray = toShortLittle(pixmap.height)
        val zero: ByteArray =   toShortLittle(0)
        out.write(0) // ID field: empty
        out.write(0) // no colour map, but should be ignored anyway as it being unmapped RGB
        out.write(2) // 2 means unmapped RGB
        out.write(byteArrayOf(0, 0, 0, 0, 0)) // color map spec: empty
        out.write(zero) // x origin: 0
        out.write(zero) // y origin: 0
        out.write(width) // width
        out.write(height) // height
        out.write(32) // image pixel size: we're writing 32-bit image (8bpp BGRA)
        out.write(8) // image descriptor: dunno, Photoshop writes 8 in there

        // write actual image data
        // since we're following Photoshop's conventional header, we also follows Photoshop's
        // TGA saving scheme, that is:
        //     1. BGRA order
        //     2. Y-Flipped but not X-Flipped
        if (!flipY) {
            for (y in pixmap.height - 1 downTo 0) {
                for (x in 0 until pixmap.width) {
                    writeTga(x, y, verbatim, pixmap, out)
                }
            }
        } else {
            for (y in 0 until pixmap.height) {
                for (x in 0 until pixmap.width) {
                    writeTga(x, y, verbatim, pixmap, out)
                }
            }
        }


        // write footer
        // 00 00 00 00 00 00 00 00 TRUEVISION-XFILE 2E 00
        out.write(byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0))
        if (verbatim) out.write("TRUEVISION-XFILE".toByteArray()) else out.write("TerrarumHappyTGA".toByteArray())
        out.write(byteArrayOf(0x2E, 0))
        out.flush()
        out.close()
    }

    private val zeroalpha = byteArrayOf(0, 0, 0, 0)
    @Throws(IOException::class)
    private fun writeTga(x: Int, y: Int, verbatim: Boolean, pixmap: Pixmap, out: OutputStream) {
        val color = pixmap.getPixel(x, y)

        // if alpha == 0, write special value instead
        if (verbatim && color and 0xFF == 0) {
            out.write(zeroalpha)
        } else {
            out.write(RGBAtoBGRA(color))
        }
    }

    private fun toShortLittle(i: Int): ByteArray {
        return byteArrayOf(
            (i and 0xFF).toByte(),
            (i ushr 8 and 0xFF).toByte()
        )
    }

    private fun RGBAtoBGRA(rgba: Int): ByteArray {
        return byteArrayOf(
            (rgba ushr 8 and 0xFF).toByte(),
            (rgba ushr 16 and 0xFF).toByte(),
            (rgba ushr 24 and 0xFF).toByte(),
            (rgba and 0xFF).toByte()
        )
    }
}

lateinit var appConfig: Lwjgl3ApplicationConfiguration
const val TEXW = 874
const val TEXH = 24 * 130

const val WIDTH = TEXW
const val HEIGHT = 768

fun main(args: Array<String>) {
    appConfig = Lwjgl3ApplicationConfiguration()
    appConfig.useVsync(false)
    appConfig.setResizable(false)
    appConfig.setWindowedMode(WIDTH, HEIGHT)
    appConfig.setTitle("Terrarum Sans Bitmap Test")
    
    Lwjgl3Application(FontTestGDX(), appConfig)
}
