/*
 * This file is part of project Shortranks, licensed under the MIT License (MIT).
 *
 * Copyright (c) 2017-2019 Mark Vainomaa <mikroskeem@mikroskeem.eu>
 * Copyright (c) Contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package eu.mikroskeem.shortranks.bukkit.hooks

import eu.mikroskeem.shortranks.api.ScoreboardHook
import eu.mikroskeem.shortranks.api.ScoreboardTeam
import eu.mikroskeem.shortranks.api.ScoreboardTeamProcessor
import eu.mikroskeem.shortranks.bukkit.Shortranks
import eu.mikroskeem.shortranks.bukkit.common.asPlayer
import eu.mikroskeem.shortranks.bukkit.common.sbStrip
import eu.mikroskeem.shortranks.common.debug
import eu.mikroskeem.shortranks.common.warning
import me.libraryaddict.disguise.DisguiseAPI
import me.libraryaddict.disguise.LibsDisguises
import me.libraryaddict.disguise.events.DisguiseEvent
import me.libraryaddict.disguise.events.UndisguiseEvent
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import java.util.WeakHashMap

/**
 * LibsDisguises plugin hook
 *
 * @author Mark Vainomaa
 */
class LibsDisguisesHook(private val plugin: Shortranks): ScoreboardHook, ScoreboardTeamProcessor, Listener {
    private val disguisedPlayers = WeakHashMap<Player, Boolean>()

    override fun hook() {
        plugin.server.pluginManager.registerEvents(this, plugin)
        checkLibsDisguisesSettings {
            warning("** LibsDisguises's 'SelfDisguisesScoreboard' was not 'IGNORE_SCOREBOARD'!")
            warning("Changing it to 'IGNORE_SCOREBOARD' to avoid Shortranks breakage, as " +
                    "Shortranks has proper workaround for LibsDisguises")
            warning("Don't forget to change that setting by hand in plugin configuration, as " +
                    "altering configuration file by Shortranks gets rid of comments and LibsDisguises reload " +
                    "causes modified setting to reset.")
        }
    }

    override fun unhook() {
        DisguiseEvent.getHandlerList().unregister(this)
        UndisguiseEvent.getHandlerList().unregister(this)
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    fun on(event: DisguiseEvent) {
        checkLibsDisguisesSettings {}
        if(event.entity is Player && !event.entity.hasMetadata("NPC")) {
            val player = event.entity as Player
            val team = plugin.scoreboardManager.getTeam(player.uniqueId)

            if(DisguiseAPI.isDisguised(player)) {
                // UndisguiseEvent is not fired when player tries to disguise itself if already disguised.
                // So clean up old entity id
                DisguiseAPI.getDisguise(player)!!.run {
                    debug("Cleaning up old disguise ${entity.entityId} from team $team")
                    team.removeMember("${entity.entityId}")
                }
            }

            // Mark player disguised
            disguisedPlayers.compute(player) { _, _ -> true }

            // Add disguised entity's id to team
            val disguiseId = event.disguise.entity.entityId
            debug("Adding disguised entity id $disguiseId to team $team, as player disguised")
            team.addMember("$disguiseId")
            plugin.sbManager.updateTeam(team)
        }
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    fun on(event: UndisguiseEvent) {
        checkLibsDisguisesSettings {}
        if(event.entity is Player && !event.entity.hasMetadata("NPC")) {
            val player = event.entity as Player
            if(!player.isOnline) return // Can be fired after player disconnects
            val team = plugin.scoreboardManager.getTeam(player.uniqueId)

            // Mark player undisguised
            disguisedPlayers.compute(player) { _, _ -> false }

            // Remove disguised entity's id from team
            val disguiseId = event.disguise.entity.entityId
            debug("Removing disguised entity id $disguiseId from team $team, as player undisguised")
            team.removeMember("$disguiseId")
            plugin.sbManager.updateTeam(team)
        }
    }

    // Does not actually process teams, listens for changes instead
    override fun accept(team: ScoreboardTeam) {
        val player = team.teamOwnerPlayer.asPlayer
        disguisedPlayers.computeIfPresent(player) compute@ { _, value ->
            player.setPlayerListName(if(value) formatPlayerListName(team, player) else null)
            return@compute value
        }
    }

    override fun getName(): String = "LibsDisguises hook"
    override fun getDescription(): String = "Built in LibsDisguises hook"
    override fun getAuthors(): List<String> = listOf("mikroskeem")
    override fun getScoreboardTeamProcessors()= listOf(this)
    override fun getProcessorPriority(): Int = 1000

    private inline fun checkLibsDisguisesSettings(callback: () -> Unit) {
        LibsDisguises.getInstance().run {
            if(config.get("SelfDisguisesScoreboard", "MODIFY_SCOREBOARD") != "IGNORE_SCOREBOARD") {
                config.set("SelfDisguisesScoreboard", "IGNORE_SCOREBOARD")
                callback()
            }
        }
    }

    private fun formatPlayerListName(team: ScoreboardTeam, player: Player) = (team.prefix + player.name + team.suffix).sbStrip()
}