package net.torvald.terrarum.ui

import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.Second
import net.torvald.terrarum.roundInt


/**
 * UIItems must be added manually at the init!
 *
 * FIXME: Bad design!!
 *
 * Created by minjaesong on 2015-12-31.
 */
abstract class UICanvas(
        toggleKeyLiteral: Int? = null, toggleButtonLiteral: Int? = null,
        // UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int))
        customPositioning: Boolean = false, // mainly used by vital meter
        doNotWarnConstant: Boolean = false
) {

    abstract var width: Int
    abstract var height: Int

    inline var posX: Int
        get() = handler.posX
        set(value) { handler.posX = value }
    inline var posY: Int
        get() = handler.posY
        set(value) { handler.posY = value }

    /**
     * Usage: (in StateInGame:) uiHandlerField.ui.handler = uiHandlerField
     */
    protected val handler = UIHandler(toggleKeyLiteral, toggleButtonLiteral, customPositioning, doNotWarnConstant)

    init {

    }

    /**
     * In milliseconds
     *
     * Timer itself is implemented in the ui.handler.
     */
    abstract var openCloseTime: Second


    protected val uiItems = ArrayList<UIItem>()


    val relativeMouseX: Int
        get() = Terrarum.mouseScreenX - handler.posX
    val relativeMouseY: Int
        get() = Terrarum.mouseScreenY - handler.posY

    /** If mouse is hovering over it regardless of its visibility */
    val mouseUp: Boolean
        get() = relativeMouseX in 0..width - 1 && relativeMouseY in 0..height - 1
    /** If mouse is hovering over it and mouse is down */
    val mousePushed: Boolean
        get() = mouseUp && Gdx.input.isButtonPressed(Terrarum.getConfigInt("mouseprimary"))


    fun update(delta: Float) {
        handler.update(this, delta)
    }
    fun render(batch: SpriteBatch, camera: Camera) {
        handler.render(this, batch, camera)
    }


    inline fun addSubUI(ui: UICanvas) {
        handler.addSubUI(ui)
    }


    /** Override this for the actual update. You must update uiItems by yourself. */
    abstract fun updateUI(delta: Float)
    /** Override this for the actual render. You must render uiItems by yourself. */
    abstract fun renderUI(batch: SpriteBatch, camera: Camera)

    /**
     * Do not modify ui.handler.openCloseCounter here.
     */
    abstract fun doOpening(delta: Float)

    /**
     * Do not modify ui.handler.openCloseCounter here.
     */
    abstract fun doClosing(delta: Float)

    /**
     * Do not modify ui.handler.openCloseCounter here.
     */
    abstract fun endOpening(delta: Float)

    /**
     * Do not modify ui.handler.openCloseCounter here.
     */
    abstract fun endClosing(delta: Float)

    abstract fun dispose()

    fun addItem(uiItem: UIItem) {
        uiItems.add(uiItem)
    }

    open fun mouseMoved(screenX: Int, screenY: Int): Boolean {
        if (this.isVisible) {
            uiItems.forEach { it.mouseMoved(screenX, screenY) }
            handler.subUIs.forEach { it.mouseMoved(screenX, screenY) }
            return true
        }
        else return false
    }
    open fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean {
        if (this.isVisible) {
            uiItems.forEach { it.touchDragged(screenX, screenY, pointer) }
            handler.subUIs.forEach { it.touchDragged(screenX, screenY, pointer) }
            return true
        }
        else return false
    }
    open fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
        if (this.isVisible) {
            uiItems.forEach { it.touchDown(screenX, screenY, pointer, button) }
            handler.subUIs.forEach { it.touchDown(screenX, screenY, pointer, button) }
            return true
        }
        else return false
    }
    open fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
        if (this.isVisible) {
            uiItems.forEach { it.touchUp(screenX, screenY, pointer, button) }
            handler.subUIs.forEach { it.touchUp(screenX, screenY, pointer, button) }
            return true
        }
        else return false
    }
    open fun scrolled(amount: Int): Boolean {
        if (this.isVisible) {
            uiItems.forEach { it.scrolled(amount) }
            handler.subUIs.forEach { it.scrolled(amount) }
            return true
        }
        else return false
    }
    open fun keyDown(keycode: Int): Boolean {
        if (this.isVisible) {
            uiItems.forEach { it.keyDown(keycode) }
            handler.subUIs.forEach { it.keyDown(keycode) }
            return true
        }
        else return false
    }
    open fun keyUp(keycode: Int): Boolean {
        if (this.isVisible) {
            uiItems.forEach { it.keyUp(keycode) }
            handler.subUIs.forEach { it.keyUp(keycode) }
            return true
        }
        else return false
    }
    open fun keyTyped(character: Char): Boolean {
        return false
        //uiItems.forEach { it.keyT }
    }

    open fun resize(width: Int, height: Int) {
        this.width = width
        this.height = height
    }


    // handler func aliases //

    inline fun setPosition(x: Int, y: Int) {
        handler.setPosition(x, y)
    }

    inline fun setAsAlwaysVisible() {
        handler.setAsAlwaysVisible()
    }

    inline fun setAsOpen() {
        handler.setAsOpen()
    }

    inline fun setAsClose() {
        handler.setAsClose()
    }

    inline fun toggleOpening() {
        handler.toggleOpening()
    }

    inline val isOpened: Boolean
        get() = handler.isOpened

    inline val isOpening: Boolean
        get() = handler.isOpening

    inline val isClosing: Boolean
        get() = handler.isClosing

    inline val isClosed: Boolean
        get() = handler.isClosed

    inline var opacity: Float
        get() = handler.opacity
        set(value) { handler.opacity = value }

    inline var scale: Float
        get() = handler.scale
        set(value) { handler.scale = value }

    inline val isTakingControl: Boolean
        get() = handler.isTakingControl

    inline var isVisible: Boolean
        get() = handler.isVisible
        set(value) { handler.isVisible = value }

    // end of handler func aliases


    companion object {
        const val OPENCLOSE_GENERIC = 0.2f

        fun doOpeningFade(ui: UICanvas, openCloseTime: Second) {
            ui.handler.opacity = ui.handler.openCloseCounter / openCloseTime
        }
        fun doClosingFade(ui: UICanvas, openCloseTime: Second) {
            ui.handler.opacity = (openCloseTime - ui.handler.openCloseCounter) / openCloseTime
        }
        fun endOpeningFade(ui: UICanvas) {
            ui.handler.opacity = 1f
        }
        fun endClosingFade(ui: UICanvas) {
            ui.handler.opacity = 0f
        }


        fun doOpeningPopOut(ui: UICanvas, openCloseTime: Second, position: Position) {
            when (position) {
                Position.LEFT -> ui.handler.posX = Movement.fastPullOut(
                        ui.handler.openCloseCounter / openCloseTime,
                        -ui.width.toFloat(),
                        0f
                ).roundInt()
                Position.TOP -> ui.handler.posY = Movement.fastPullOut(
                        ui.handler.openCloseCounter / openCloseTime,
                        -ui.height.toFloat(),
                        0f
                ).roundInt()
                Position.RIGHT -> ui.handler.posX = Movement.fastPullOut(
                        ui.handler.openCloseCounter / openCloseTime,
                        Terrarum.WIDTH.toFloat(),
                        Terrarum.WIDTH - ui.width.toFloat()
                ).roundInt()
                Position.BOTTOM -> ui.handler.posY = Movement.fastPullOut(
                        ui.handler.openCloseCounter / openCloseTime,
                        Terrarum.HEIGHT.toFloat(),
                        Terrarum.HEIGHT - ui.height.toFloat()
                ).roundInt()
            }
        }
        fun doClosingPopOut(ui: UICanvas, openCloseTime: Second, position: Position) {
            when (position) {
                Position.LEFT -> ui.handler.posX = Movement.fastPullOut(
                        ui.handler.openCloseCounter / openCloseTime,
                        0f,
                        -ui.width.toFloat()
                ).roundInt()
                Position.TOP -> ui.handler.posY = Movement.fastPullOut(
                        ui.handler.openCloseCounter / openCloseTime,
                        0f,
                        -ui.height.toFloat()
                ).roundInt()
                Position.RIGHT -> ui.handler.posX = Movement.fastPullOut(
                        ui.handler.openCloseCounter / openCloseTime,
                        Terrarum.WIDTH - ui.width.toFloat(),
                        Terrarum.WIDTH.toFloat()
                ).roundInt()
                Position.BOTTOM -> ui.handler.posY = Movement.fastPullOut(
                        ui.handler.openCloseCounter / openCloseTime,
                        Terrarum.HEIGHT - ui.height.toFloat(),
                        Terrarum.HEIGHT.toFloat()
                ).roundInt()
            }
        }
        fun endOpeningPopOut(ui: UICanvas, position: Position) {
            when (position) {
                Position.LEFT -> ui.handler.posX = 0
                Position.TOP -> ui.handler.posY = 0
                Position.RIGHT -> ui.handler.posX = Terrarum.WIDTH - ui.width
                Position.BOTTOM -> ui.handler.posY = Terrarum.HEIGHT - ui.height
            }
        }
        fun endClosingPopOut(ui: UICanvas, position: Position) {
            when (position) {
                Position.LEFT -> ui.handler.posX = -ui.width
                Position.TOP -> ui.handler.posY = -ui.height
                Position.RIGHT -> ui.handler.posX = Terrarum.WIDTH
                Position.BOTTOM -> ui.handler.posY = Terrarum.HEIGHT
            }
        }

        // TODO add blackboard take in/out (sinusoidal)

        enum class Position {
            LEFT, RIGHT, TOP, BOTTOM
        }
    }

}
