package net.moonleay.gimble.client.editor

import net.minecraft.client.MinecraftClient
import net.minecraft.text.Text
import net.moonleay.gimble.client.util.ChatUtil
import net.moonleay.gimble.editor.ServerEditorManager
import net.moonleay.gimble.editor.state.EditorState
import net.moonleay.gimble.editor.state.GimblePolicyType
import net.moonleay.gimble.editor.state.mode.Capability
import net.moonleay.gimble.editor.state.mode.Mode
import net.moonleay.gimble.editor.state.mode.ModeModifier
import net.moonleay.gimble.editor.util.EditorUtil
import net.moonleay.gimble.editor.util.GimblePolicy
import net.moonleay.gimble.networking.GimbleClient

object ClientEditor {
    private var POLICY = GimblePolicyType.NOT_PRESENT

    private var CURRENT_MODE = Mode.NORMAL
    private var TEMP_DISABLED_MODE = Mode.UNKNOWN

    private val CURRENT_MODE_MODIFIER = mutableListOf<ModeModifier>()
    private val TEMP_DISABLED_MODIFIERS = mutableListOf<ModeModifier>()

    private val DISABLED_MODIFIERS_STORAGE = mutableListOf<ModeModifier>()


    fun onConnectedToNewWorld() {
        POLICY = GimblePolicyType.NOT_PRESENT
        GimbleClient.checkIfServerHasGimble(EditorState(CURRENT_MODE, CURRENT_MODE_MODIFIER))
        if (TEMP_DISABLED_MODE == Mode.UNKNOWN) {
            TEMP_DISABLED_MODE = CURRENT_MODE
        }
        CURRENT_MODE = Mode.INSERT
        DISABLED_MODIFIERS_STORAGE.addAll(CURRENT_MODE_MODIFIER)
        CURRENT_MODE_MODIFIER.clear()
    }

    fun onAllowedCheck(data: GimblePolicy) {
        POLICY = data.policy
        if (data.policy == GimblePolicyType.ALLOWED) {
            if (TEMP_DISABLED_MODE != Mode.UNKNOWN) {
                CURRENT_MODE = TEMP_DISABLED_MODE
                TEMP_DISABLED_MODE = Mode.UNKNOWN
            }
            CURRENT_MODE_MODIFIER.addAll(DISABLED_MODIFIERS_STORAGE)
            DISABLED_MODIFIERS_STORAGE.clear()
            this.onUpdated()
        }
    }

    fun isAllowed(): Boolean {
        return POLICY == GimblePolicyType.ALLOWED
    }

    fun shouldClient(capability: Capability): Boolean {
        return EditorUtil.shouldPlayer(capability, EditorState(CURRENT_MODE, CURRENT_MODE_MODIFIER))
    }

    fun isInNonDefaultMode(): Boolean {
        return CURRENT_MODE != Mode.NORMAL
    }

    /*
    * Send an updated player state to the server
    * */
    private fun updateServerState() {
        val state = EditorState(CURRENT_MODE, CURRENT_MODE_MODIFIER)
        GimbleClient.sendEditorState(state)
        ServerEditorManager.updateEditorState(MinecraftClient.getInstance().player!!.uuid, state)
    }


    /*
    * Set the current mode
    * */
    fun setMode(mode: Mode) {
        if (!isAllowed()) {
            ChatUtil.showToastToSelf("Gimble is disabled", "You cannot change modes", MinecraftClient.getInstance())
            return
        }
        if (!MinecraftClient.getInstance().player!!.isCreative)
            return
        CURRENT_MODE = mode

        this.onUpdated()
    }

    /*
    * Toggle a mode modifier
    * */
    fun toggleModifier(mod: ModeModifier) {
        if (!isAllowed()) {
            ChatUtil.showToastToSelf("Gimble is disabled", "You cannot change modifiers", MinecraftClient.getInstance())
            return
        }
        if (!MinecraftClient.getInstance().player!!.isCreative)
            return

        if (CURRENT_MODE.incompatibleModifiers.contains(mod)){
            if (TEMP_DISABLED_MODIFIERS.contains(mod))
                TEMP_DISABLED_MODIFIERS.remove(mod)
            else
                TEMP_DISABLED_MODIFIERS.add(mod)
        }
        else {
            if (CURRENT_MODE_MODIFIER.contains(mod))
                CURRENT_MODE_MODIFIER.remove(mod)
            else
                CURRENT_MODE_MODIFIER.add(mod)
        }

        this.onUpdated()
    }

    private fun onUpdated() {
        CURRENT_MODE_MODIFIER.sortBy {
            it.displayName
        }
        this.checkForIncompatibleModeModifiers()
    }

    /**
     * This runs on Mode updated
     */
    private fun checkForIncompatibleModeModifiers() {
        if (TEMP_DISABLED_MODIFIERS.size > 0) {
            CURRENT_MODE_MODIFIER.addAll(
                TEMP_DISABLED_MODIFIERS
            )
            TEMP_DISABLED_MODIFIERS.clear()
        }

        val incompatibleModeModifiers = CURRENT_MODE.incompatibleModifiers
        for (mod in CURRENT_MODE_MODIFIER) {
            if (incompatibleModeModifiers.contains(mod)) {
                TEMP_DISABLED_MODIFIERS.add(mod)
            }
        }
        for (mod in TEMP_DISABLED_MODIFIERS) {
            if (CURRENT_MODE_MODIFIER.contains(mod)) { // This check is probably useless.
                CURRENT_MODE_MODIFIER.remove(mod)
            }
        }

        CURRENT_MODE_MODIFIER.sortBy {
            it.displayName
        }

        // Update State
        this.updateServerState()

        if(TEMP_DISABLED_MODIFIERS.isNotEmpty()) {
            ChatUtil.showToastToSelf("${CURRENT_MODE.displayName} Mode disabled", getListAsString(TEMP_DISABLED_MODIFIERS), MinecraftClient.getInstance())
        }
    }

    private fun getListAsString(list: List<ModeModifier>): String{
        if(list.isEmpty())
            return "Empty list"
        val sb = StringBuilder()
        for (mod in list) {
            sb.append(mod.displayName)
            sb.append(", ")
        }
        return sb.toString().dropLast(2)
    }

    /*
    * Get the display text to display in the HUD
    * */
    fun getModeDisplayText(): Text {
        val displayText = StringBuilder(CURRENT_MODE.displayName)
        if (CURRENT_MODE_MODIFIER.isNotEmpty() && this.isAllowed() && MinecraftClient.getInstance().player?.isCreative == true) {
            displayText.append(" [")
            for (i in CURRENT_MODE_MODIFIER.indices) {
                displayText.append(CURRENT_MODE_MODIFIER[i].displayName)
                if (i != CURRENT_MODE_MODIFIER.size - 1) {
                    displayText.append(", ")
                }
            }
            displayText.append("]")
        } else if (!this.isAllowed() || !MinecraftClient.getInstance().player?.isCreative!!) {
            displayText.clear()
            displayText.append("[GIMBLE DISABLED]")
        }
        return Text.of(displayText.toString())
    }

    fun getCurrentColor(): Int {
        return CURRENT_MODE.color
    }
}
