package net.torvald.terrarum.serialise

import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
import java.io.IOException
import java.io.InputStream
import java.lang.IllegalArgumentException
import java.util.*

/**
 * Created by minjaesong on 2016-08-24.
 */
// internal for everything: prevent malicious module from messing up the savedata
internal object ReadLayerData {


    internal operator fun invoke(inputStream: InputStream, inWorld: GameWorldExtension? = null): GameWorldExtension {
        val magicBytes = ByteArray(4)
        val layerSizeBytes = ByteArray(1)
        val layerCountBytes = ByteArray(1)
        val worldWidthBytes = ByteArray(4)
        val worldHeightBytes = ByteArray(4)
        val spawnCoordXBytes = ByteArray(4)
        val spawnCoordYBytes = ByteArray(4)

        // read header first
        inputStream.read(magicBytes)
        if (!Arrays.equals(magicBytes, WriteLayerData.MAGIC)) {
            throw IllegalArgumentException("File not a Layer Data")
        }

        inputStream.read(layerSizeBytes)
        inputStream.read(layerCountBytes)
        inputStream.skip(2) // reserved bytes
        inputStream.read(worldWidthBytes)
        inputStream.read(worldHeightBytes)
        inputStream.read(spawnCoordXBytes)
        inputStream.read(spawnCoordYBytes)

        val worldWidth = worldWidthBytes.toLittleInt()
        val worldHeight = worldHeightBytes.toLittleInt()
        val bytesPerTile = layerSizeBytes[0].toUint()
        val layerCount = layerCountBytes[0].toUint()
        val layerSize = worldWidth * worldHeight * bytesPerTile

        val terrainLayerMSB = ByteArray(layerSize)
        val wallLayerMSB = ByteArray(layerSize)
        val terrainLayerLSB = ByteArray(layerSize / 2)
        val wallLayerLSB = ByteArray(layerSize / 2)
        var wireLayer: ByteArray? = null

        inputStream.read(terrainLayerMSB)
        inputStream.read(wallLayerMSB)
        inputStream.read(terrainLayerLSB)
        inputStream.read(wallLayerLSB)

        if (layerCount == 4) {
            wireLayer = ByteArray(layerSize)
            inputStream.read(wireLayer)
        }



        // create world out of tiles data

        val retWorld = inWorld ?: GameWorldExtension(1, worldWidth, worldHeight)

        retWorld.layerTerrain.data = terrainLayerMSB
        retWorld.layerWall.data = wallLayerMSB
        retWorld.layerTerrainLowBits.data = terrainLayerLSB
        retWorld.layerWallLowBits.data = wallLayerLSB

        if (wireLayer != null) {
            retWorld.layerWire.data = wireLayer
        }

        retWorld.spawnX = spawnCoordXBytes.toLittleInt()
        retWorld.spawnY = spawnCoordYBytes.toLittleInt()


        return retWorld
    }


	internal fun InputStream.readRelative(b: ByteArray, off: Int, len: Int): Int {
        if (b == null) {
            throw NullPointerException()
        } else if (off < 0 || len < 0 || len > b.size) {
            throw IndexOutOfBoundsException()
        } else if (len == 0) {
            return 0
        }

        var c = read()
        if (c == -1) {
            return -1
        }
        b[0] = c.toByte()

        var i = 1
        try {
            while (i < len) {
                c = read()
                if (c == -1) {
                    break
                }
                b[i] = c.toByte()
                i++
            }
        } catch (ee: IOException) {
        }

        return i
    }
}