from core.helper import enum, sendMessage

from siege import game, Locale, InventoryItem
from siege.log import Log
from siege.network import NetworkManager
from siege.util import Random, Sizes, Vector
from siege.world import World
from siege.world.realm import Layer


Privileges = enum("Aer", "Admin", "Standard")


class CommandSystem(object):
    NAME = "command"

    def getName(self):
        return self.NAME

    def __init__(self):
        self.players = {}
        self.commands = {}
        game.events["populate_command"].invoke(self)
        game.events['player_joined'].listen(self.addPlayer)
        game.events['world_unloaded'].listen(self.cleanup)

    def addPlayer(self, player):
        if game.network.getId() == player.networkId:
            self.players[player.info.name] = Privileges.Aer
        else:
            self.players[player.info.name] = Privileges.Standard

    def cleanup(self, world):
        self.players.clear()

    def add(self, command):
        self.commands[command.name] = command

    def isCommand(self, message):
        '''Determines if the provided message is a command.'''
        return message.startswith("/")

    def execute(self, player, message):
        if not NetworkManager.isHost():
            return
        assert self.isCommand(message), "Provided message is not a command. Check before calling this."
        name, ignore, content = message.partition(" ")
        name = name[1:]
        error = ""
        if name in self.commands:
            command = self.commands[name]
            privilege = self.players[player.info.name]
            if privilege <= command.privilege:
                command.callback(self, player, content)
            else:
                error = Locale.get("CmdNotPrivileged")
        else:
            error = Locale.get("CmdUnknown")
        if error:
            sendMessage("general", Locale.get("ChatSystem"), error, player.networkId)

    @staticmethod
    def register():
        game.registerSystem(CommandSystem.NAME, CommandSystem())


class Command(object):
    def __init__(self, name, privilege, callback, help):
        self.name = name
        self.privilege = privilege
        self.callback = callback
        self.help = help

    def __cmp__(self, other):
        return self.name - other.name

    @staticmethod
    def report(player, message, **kwargs):
        sendMessage("general", Locale.get("ChatSystem"), message, player.networkId, **kwargs)


class PromoteCommand(object):
    @staticmethod
    def create():
        return Command("promote", Privileges.Aer, PromoteCommand.execute, "CmdPromoteHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        commandSystem = game.command
        if content in commandSystem.players:
            privilege = commandSystem.players[content]
            if privilege is Privileges.Standard:
                privilege = Privileges.Admin
            elif privilege is Privileges.Admin:
                privilege = Privileges.Aer
            commandSystem.players[content] = privilege
            Command.report(player, "CmdPromote", player=content, privilege=Privileges.getString[privilege])


class DemoteCommand(object):
    @staticmethod
    def create():
        return Command("demote", Privileges.Aer, DemoteCommand.execute, "CmdPromoteHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        if content in commandSystem.players:
            privilege = commandSystem.players[content]
            if privilege is Privileges.Aer:
                privilege = Privileges.Admin
            elif privilege is Privileges.Admin:
                privilege = Privileges.Standard
            commandSystem.players[content] = privilege
            Command.report(player, "CmdDemote", player=content, privilege=Privileges.getString[privilege])


class PrivilegeCommand(object):
    @staticmethod
    def create():
        return Command("privilege", Privileges.Standard, PrivilegeCommand.execute, "CmdPromoteHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        if content in commandSystem.commands:
            privilege = commandSystem.commands[content].privilege
            Command.report(player, "CmdPrivilege", command=content, privilege=Privileges.getString[privilege])


class HelpCommand(object):
    @staticmethod
    def create():
        return Command("help", Privileges.Standard, HelpCommand.execute, "CmdHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        if content and content in commandSystem.commands:
            Command.report(player, Locale.get(commandSystem.commands[content].help))
        else:
            Command.report(player, "CmdHelp")
            for name, command in commandSystem.commands.iteritems():
                Command.report(player, " /{}".format(name))


class EvalCommand(object):
    @staticmethod
    def create():
        return Command("eval", Privileges.Aer, EvalCommand.execute, "CmdEvalHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        result = eval(content, globals(), locals())
        Command.report(player, str(result))


class SpawnCommand(object):
    @staticmethod
    def create():
        return Command("spawn", Privileges.Aer, SpawnCommand.execute, "CmdSpawnHelp")

    @staticmethod
    def execute(commandSystem, player, commandContent):
        contentName, ignore, quantity = commandContent.partition(" ")
        try:
            quantity = int(quantity)
            content = game.content.get(contentName)
            if content:
                if content.entity.has("monster"):
                    monster = game.entity.create(content)
                    monster.monster.create()
                    monster.combat.setLevel(player.entity.combat.level.get())
                    monster.setPosition(World.get().getMousePosition())
                    World.get().move(monster, player.entity.realm, Layer.Active)
                else:
                    inventoryItem = InventoryItem(content, quantity)
                    if inventoryItem.entity.item.isUnique:
                        if inventoryItem.entity.has('equipment'):
                            inventoryItem.entity = game.equipment.create(content, Random.get(50, 255))
                        else:
                            inventoryItem.entity = game.entity.create(content)
                    player.entity.realm.dropped.create(inventoryItem, World.get().getMousePosition(), Vector(0, -6))
                Command.report(player, "CmdSpawn", name=Locale.get(content.entity.name), amount=quantity)
            else:
                Command.report(player, "Content not found.")
        except ValueError as e:
            Log.error(str(e))
            Command.report(player, "Couldn't spawn.")


class TimeCommand(object):
    @staticmethod
    def create():
        return Command("time", Privileges.Admin, TimeCommand.execute, "CmdTimeHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        try:
            hour = int(content)
            if hour < 0 or hour > 23:
                raise ValueError("Time must be 0-23.")
            World.get().time.hours.set(hour)
            World.get().time.minutes.set(0)
            Command.report(player, "CmdTime", time=content)
        except ValueError:
            Command.report(player, "CmdTimeHelp")


class LearnCommand(object):
    @staticmethod
    def create():
        return Command("iknoweverything", Privileges.Admin, LearnCommand.execute, "CmdTimeHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        #Give the player all of the recipes for now
        research = player.entity.research
        for craftable in game.craft.getComponents():
            research.learnRecipe(craftable.key())
        Command.report(player, "Yes you do!")


class WarpCommand(object):
    @staticmethod
    def create():
        return Command("warp", Privileges.Aer, WarpCommand.execute, "CmdWarpHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        x, y = content.split(",")

        dest = Vector(int(x) * Sizes.TILE, int(y) * Sizes.TILE)
        player.entity.setPosition(dest)
        player.entity.physics.wake()

        Command.report(player, "CmdWarp")


class AerLikeCommand(object):
    @staticmethod
    def create():
        return Command("aerlike", Privileges.Aer, AerLikeCommand.execute, "CmdAerLikeHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        player.entity.combat.adjustExperience(30000)
        player.entity.talents.arcane.gainPoints(30000)
        player.entity.talents.arms.gainPoints(30000)
        player.entity.talents.divine.gainPoints(30000)
        player.entity.talents.gather.gainPoints(30000)
        player.entity.talents.craft.gainPoints(30000)
        player.entity.breath.current.set(100000)
        player.entity.breath.capacity.set(100000)
        player.entity.descent.factor = 0
        player.entity.stats.SPRegen.set(1000)
        player.entity.event["tool_power"].listen(AerLikeCommand.handleToolPower)
        Command.report(player, "CmdAerLike")

    @staticmethod
    def handleToolPower(item, power):
        power.final = 999


class LevelCommand(object):
    @staticmethod
    def create():
        return Command("level", Privileges.Aer, LevelCommand.execute, "CmdLevelHelp")

    @staticmethod
    def execute(commandSystem, player, content):
        level = int(content)
        player.entity.combat.adjustExperience(1000 * level)
        player.entity.talents.arcane.gainPoints(1000 * level)
        player.entity.talents.arms.gainPoints(1000 * level)
        player.entity.talents.divine.gainPoints(1000 * level)
        player.entity.talents.gather.gainPoints(1000 * level)
        player.entity.talents.craft.gainPoints(1000 * level)
        Command.report(player, "CmdLevel")


def populateCommands(commandSystem):
    commandSystem.add(PromoteCommand.create())
    commandSystem.add(DemoteCommand.create())
    commandSystem.add(PrivilegeCommand.create())
    commandSystem.add(HelpCommand.create())
    commandSystem.add(EvalCommand.create())
    commandSystem.add(SpawnCommand.create())
    commandSystem.add(TimeCommand.create())
    commandSystem.add(LearnCommand.create())
    commandSystem.add(WarpCommand.create())
    commandSystem.add(AerLikeCommand.create())
    commandSystem.add(LevelCommand.create())
