package com.example.cmpt362_group30_maze_game.game

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.lifecycle.ViewModel
import java.io.FileNotFoundException
import kotlin.math.floor

class MapDataViewModel: ViewModel() {
    var mapObject : GameObject? = null
    var collisionMap : Bitmap? = null
    var mapBackground : GameObject = GameObject(1.0f,1.0f,0.0f, 0.0f,0.0f,0.0f, 1.0f,1.0f,1.0f)

    var mazeStartX = 0.0f
    var mazeStartY = 0.0f
    var mazeEndX = 0.0f
    var mazeEndY = 0.0f

    init{
        val mapColours : FloatArray = floatArrayOf(
            0.0f,0.671f,1.0f,1.0f,
            0.0f,0.188f,1.0f,1.0f,
            0.0f,1.0f,0.761f,1.0f,
            0.0f,0.925f,1.0f,1.0f,)
        mapBackground.mesh = Mesh(Mesh.squareVerts, mapColours, Mesh.squareDrawOrder, mapBackground)
    }

    // Note: This must be called at least once before rendering starts
    // Initializes map mesh and collision data based on passed URI to png file
    // returns 0 on success or 1 on failure
    @RequiresApi(Build.VERSION_CODES.Q)
    fun loadData(context : Context, uri : Uri) : Int{
        // Load png information into bitmap
        try {
            // Extract Collision data from png
            collisionMap = BitmapFactory.decodeStream(context.contentResolver.openInputStream(uri))

            // Extract mesh data from bitMap
            val verts : MutableList<Float> = mutableListOf()
            val drawOrder : MutableList<Short> = mutableListOf()
            val colours : MutableList<Float> = mutableListOf()

            // flag for colouring vertices
            var flipColours = false

            for (j in 0..collisionMap!!.height-1){
                for (i in 0.. collisionMap!!.width-1){
                    val pixelCol = collisionMap!!.getColor(i,j)

                    // Found start location
                    if (pixelCol.red() == 1.0f && pixelCol.blue() == 0.0f && pixelCol.green() == 0.0f ){
                        mazeStartX = i+0.5f
                        mazeStartY = j+0.5f
                    }

                    // Found end location
                    else if (pixelCol.red() == 0.0f && pixelCol.blue() == 1.0f && pixelCol.green() == 0.0f ){
                        mazeEndX = i+0.5f
                        mazeEndY = j+0.5f

                        // Create vertices
                        addMeshData(i, j, verts, drawOrder)

                        // add colour of pixel (set exit area to red square)
                        for (k in 0 ..3) {
                            colours.add(1.0f)
                            colours.add(0.0f)
                            colours.add(0.0f)
                            colours.add(1.0f)
                        }
                    }

                    // Found maze wall
                    else if (pixelCol.red() != 1.0f || pixelCol.blue() != 1.0f || pixelCol.green() != 1.0f ) {
                        // Create vertices
                        addMeshData(i, j, verts, drawOrder)

                        // Note: if time revisit maze wall colour choice
                        // add colour of pixel
                        if (flipColours) {
                            colours.add(0.039f)
                            colours.add(0.059f)
                            colours.add(0.114f)
                            colours.add(0.0f)

                            colours.add(0.149f)
                            colours.add(0.153f)
                            colours.add(0.173f)
                            colours.add(1.0f)

                            colours.add(0.039f)
                            colours.add(0.059f)
                            colours.add(0.114f)
                            colours.add(0.0f)

                            colours.add(0.149f)
                            colours.add(0.153f)
                            colours.add(0.173f)
                            colours.add(1.0f)
                        }
                        else{
                            colours.add(0.149f)
                            colours.add(0.153f)
                            colours.add(0.173f)
                            colours.add(1.0f)

                            colours.add(0.039f)
                            colours.add(0.059f)
                            colours.add(0.114f)
                            colours.add(0.0f)

                            colours.add(0.149f)
                            colours.add(0.153f)
                            colours.add(0.173f)
                            colours.add(1.0f)

                            colours.add(0.039f)
                            colours.add(0.059f)
                            colours.add(0.114f)
                            colours.add(0.0f)
                        }
                    }

                    // No tile data
                    else{
                        // Do nothing
                    }

                    // Causes checker boarding of maze wall cells
                    flipColours = !flipColours
                }
            }

            mapObject = GameObject(0.0f,0.0f,0.0f, 0.0f,0.0f,0.0f, 1.0f,1.0f,1.0f)
            mapObject!!.mesh = Mesh(verts.toFloatArray(), colours.toFloatArray(), drawOrder.toShortArray(), mapObject!!)

            return 0
        }
        catch(error: FileNotFoundException){
            // Note: add exception handling if time permits
            return 1
        }
    }

    // Helper function for loadData
    // Note: Does not 100% reuse adjacent vertices
    private fun addMeshData(horzOffset : Int, vertOffset : Int, verts : MutableList<Float>, drawOrder :  MutableList<Short>){
        val numVerts = (verts.size)/3

        // add square mesh for pixel
        verts.add(1.0f*horzOffset) // x
        verts.add(1.0f*vertOffset) // y
        verts.add(0.0f) // z

        verts.add((1.0f*horzOffset)+1.0f)
        verts.add(1.0f*vertOffset)
        verts.add(0.0f)

        verts.add((1.0f*horzOffset)+1.0f)
        verts.add((1.0f*vertOffset)+1.0f)
        verts.add(0.0f)

        verts.add(1.0f*horzOffset)
        verts.add((1.0f*vertOffset)+1.0f)
        verts.add(0.0f)

        // add drawOrder of pixel
        drawOrder.add(numVerts.toShort())
        drawOrder.add((numVerts+1).toShort())
        drawOrder.add((numVerts+2).toShort())

        drawOrder.add(numVerts.toShort())
        drawOrder.add((numVerts+3).toShort())
        drawOrder.add((numVerts+2).toShort())
    }

    // Note: loadData must have been called at least once successfully before this can be called
    // Tests collision with map data at specified (x,y)
    // Returns 0, if no collision
    // Returns 1 if collided with maze
    // Returns 2 if collided with end of maze
    // Returns 3 on undefined collision data
    @RequiresApi(Build.VERSION_CODES.Q)
    fun isCollision(xPos : Float, yPos : Float) : Int{
        val posCol = collisionMap!!.getColor(floor(xPos).toInt(), floor(yPos).toInt())

        if (posCol.red() == 1.0f && posCol.blue() == 1.0f && posCol.green() == 1.0f ) {
            return 0
        }
        else if (posCol.red() == 0.0f && posCol.blue() == 0.0f && posCol.green() == 0.0f ) {
            return 1
        }
        else if (posCol.red() == 0.0f && posCol.blue() == 1.0f && posCol.green() == 0.0f ) {
            return 2
        }
        else {
            return 3
        }
    }
}