
from json import *

class MobError(Exception):
    pass

class Entity:

    id = 0

    def __init__(self, mob, name = None, hp = None, baby = False, follow_range = None, movement_speed = None, attack_damage = None, knockback_resistance = None, right_hand = None, left_hand = None, feet = None, legs = None, chest = None, head = None, tags = [], silent = False, commands = [], other = None, death_trigger = False, Mtype = None, invisible = True):
        if not isinstance(mob, str):
            raise MobError('Mob must be a str')
        if not isinstance(name, (JTxt, type(None))):
            raise MobError('Name must be a JTxt or None')
        for i,e in enumerate([hp, follow_range, movement_speed, attack_damage, knockback_resistance]):
            if not isinstance(e, (int, float, type(None))):
                raise MobError('{0} must be a int or float'.format(['hp', 'follow_range', 'movement_speed', 'attack_damage', 'knockback_resistance'][i]))
        if not isinstance(baby, bool):
            raise MobError('Hp must be a bool')
        if not isinstance(silent, bool):
            raise MobError('silent must be a bool')
        if isinstance(tags, list):
            for i,e in enumerate(tags):
                if not isinstance(e, str):
                    raise MobError('Tags {0} must be a str'.format(tags[i]))
        if isinstance(commands, list):
            for i,e in enumerate(commands):
                if not isinstance(e, str):
                    raise MobError('Command {0} must be a str command'.format(tags[i]))
        else:
            raise MobError('Tags must be a list of str')

        self.mob = mob
        self.name = name
        self.name_ = name.plain_() if name != None else '#'+str(Enemy.id)
        self.baby = baby
        self.silent = silent
        self.invisible = invisible

        self.hp = hp
        self.follow_range = follow_range
        self.movement_speed = movement_speed
        self.attack_damage = attack_damage
        self.knockback_resistance = knockback_resistance

        self.right_hand = 'Unbreakable:1b,' + right_hand if right_hand != None else ''
        self.left_hand = 'Unbreakable:1b,' + left_hand if left_hand != None else ''

        self.feet = 'Unbreakable:1b,' + feet if feet != None else ''
        self.legs = 'Unbreakable:1b,' + legs if legs != None else ''
        self.chest = 'Unbreakable:1b,' + chest if chest != None else ''
        self.head = 'Unbreakable:1b,' + head if head != None else ''

        self.icon = {"item":"slime_ball", "nbt":"CustomModelData:5000"}
        if isinstance(head, str):

            self.icon["item"] = [s for s in head.split(',') if "id" in s][0][4:-1]
            self.icon["nbt"] = head[head.index("tag:{")+len("tag:{"):-1].replace('"','\\"')

        for i in ['feet', 'legs', 'chest', 'head']:
            attr = getattr(self, i)
            if attr!= '' and 'tag:{SkullOwner' not in attr:
                if 'tag:{' in attr:
                    setattr(self, i, attr.replace('tag:{', 'tag:{AttributeModifiers:[{AttributeName:"generic.armor",Name:"generic.armor",Amount:0,Operation:0,UUID:[I;1,1,1,1]},{AttributeName:"generic.armor_toughness",Name:"generic.armor_toughness",Amount:0,Operation:0,UUID:[I;1,1,1,1]}],'))
                else:
                    setattr(self, i, attr + ',tag:{AttributeModifiers:[{AttributeName:"generic.armor",Name:"generic.armor",Amount:0,Operation:0,UUID:[I;1,1,1,1]},{AttributeName:"generic.armor_toughness",Name:"generic.armor_toughness",Amount:0,Operation:0,UUID:[I;1,1,1,1]}]}')

        for i in ['right_hand', 'left_hand']:
            attr = getattr(self, i)
            if attr!= '' and any(weapon in attr for weapon in ["sword", "shovel", "axe", "hoe", "pickaxe"]) :
                if 'tag:{' in attr:
                    setattr(self, i, attr.replace('tag:{', 'tag:{AttributeModifiers:[{AttributeName:"generic.attack_damage",Name:"generic.attack_damage",Amount:0,Operation:0,UUID:[I;1,1,1,1]}],'))
                else:
                    setattr(self, i, attr + ',tag:{AttributeModifiers:[{AttributeName:"generic.attack_damage",Name:"generic.attack_damage",Amount:0,Operation:0,UUID:[I;1,1,1,1]}]}')

        self.tags = tags
        self.commands = commands
        self.other = other
        self.death_trigger = death_trigger
        self.type = Mtype

        Enemy.id += 1

    def nbt(self, cursed = False):
        f1 = 0.3 if cursed else 0
        f2 = 0.1 if cursed else 0

        name = "CustomName:'{0}',CustomNameVisible:1,".format(self.name.json()) if self.name != None else ''
        baby = 'IsBaby:{0},'.format(self.baby) if self.baby == True else ''
        silent = 'Silent:1b,' if self.silent == True else ''
        hp = 'Health:{0:0.2f},'.format(self.hp+abs(self.hp*f1)) if self.hp != None else ''
        other = self.other+"," if self.other != None else ''

        hp_atr = '{{Name:"generic.max_health",Base:{0:0.2f}}},'.format(self.hp+abs(self.hp*f1)) if self.hp != None else ''
        range = '{{Name:"generic.follow_range",Base:{0:0.2f}}},'.format(self.follow_range+abs(self.follow_range*f2)) if self.follow_range != None else ''
        damage = '{{Name:"generic.attack_damage",Base:{0:0.2f}}},'.format(self.attack_damage+abs(self.attack_damage*f1)) if self.attack_damage != None else ''
        speed = '{{Name:"generic.movement_speed",Base:{0:0.2f}}},'.format(self.movement_speed) if self.movement_speed != None else ''
        k_Res = '{{Name:"generic.knockback_resistance",Base:{0:0.2f}}},'.format(self.knockback_resistance+abs(self.knockback_resistance*f2)) if self.knockback_resistance != None else ''
        attr = hp_atr + range + damage + speed + k_Res
        if attr != '':
            attr = 'Attributes:[{0}],'.format(attr[:-1])

        if self.right_hand != None or self.left_hand != None:
            hands = 'HandItems:[{{{0}}},{{{1}}}],'.format(self.right_hand, self.left_hand)

        if self.feet != None or self.legs != None or self.chest != None or self.head != None:
            armor = 'ArmorItems:[{{{0}}},{{{1}}},{{{2}}},{{{3}}}],'.format(self.feet, self.legs, self.chest, self.head)

        tags = [self.type, self.name_, "E+B", "init"] + self.tags
        if cursed:
            tags.append('cursed')
        if self.death_trigger:
            tags.append('death_trigger')

        invisible = ''
        if self.invisible:
            invisible = "ActiveEffects:[{{Id:14b,Amplifier:1b,Duration:720000,ShowParticles:0b}}],Passengers:[{{id:'area_effect_cloud',CustomNameVisible:1b,Duration:2147483647,Tags:['must_ride','enemy','E+B'],CustomName:'{}'}}],".format(self.name.json())

        return '{{{0}{1}{2}{3}{4}{5}{6}{7}{8}Tags:{9},DeathLootTable:"mdd:drop_marker",HandDropChances:[0.0F,0.0F],ArmorDropChances:[0.0F,0.0F,0.0F,0.0F]}}'.format(name, baby, hp, attr, hands, armor, silent, other, invisible, tags)

# ----------- Vanila -------------
class Slime(Entity):

    def __init__(self, name = None, hp = None, follow_range = None, movement_speed = None, attack_damage = None, knockback_resistance = None, size = None, tags = [], silent = False, commands = [], head = None, other = None, death_trigger = False, Mtype = "enemy", invisible = False):
        super().__init__('slime', name, hp, False, follow_range, movement_speed, attack_damage, knockback_resistance, None, None, None, None, None, head, tags, silent, commands, other, death_trigger, Mtype, invisible)

        if size not in range(256) and size != None:
            raise MobError('Size must be bettwen 0-255')
        self.size = size

    def nbt(self, curse = False):
        if self.size != None:
            return super().nbt(curse).replace("Tags:","Size:{0},Tags:".format(self.size))
        return super().nbt(curse)

class Panda(Entity):

    def __init__(self, name = None, hp = None, follow_range = None, movement_speed = None, attack_damage = None, knockback_resistance = None, gene = "normal", tags = [], silent = False, commands = [], head = None, other = None, death_trigger = False, Mtype = "enemy", invisible = False):
        super().__init__('panda', name, hp, False, follow_range, movement_speed, attack_damage, knockback_resistance, None, None, None, None, None, head, tags, silent, commands, other, death_trigger, Mtype, invisible)

        self.gene = gene

    def nbt(self, curse = False):
        return super().nbt(curse).replace("Tags:","MainGene:{0},Tags:".format(self.gene))


# ----------- Enemies -------------
class Enemy(Entity):
    def __init__(self, mob, name = None, hp = None, baby = False, follow_range = None, movement_speed = None, attack_damage = None, knockback_resistance = None, right_hand = None, left_hand = None, feet = None, legs = None, chest = None, head = None, tags = [], silent = False, commands = [], other = None, death_trigger = False, invisible = False):
        super().__init__(mob, name, hp, baby, follow_range, movement_speed, attack_damage, knockback_resistance, right_hand, left_hand, feet, legs, chest, head, tags, silent, commands, other, death_trigger, "enemy", invisible)

# ----------- Bosses -------------
class Boss(Entity):
    def __init__(self, mob, name = None, hp = None, baby = False, follow_range = None, movement_speed = None, attack_damage = None, knockback_resistance = None, right_hand = None, left_hand = None, feet = None, legs = None, chest = None, head = None, tags = [], silent = False, commands = [], other = None, death_trigger = False, invisible = False):
        super().__init__(mob, name, hp, baby, follow_range, movement_speed, attack_damage, knockback_resistance, right_hand, left_hand, feet, legs, chest, head, tags, silent, commands, other, death_trigger, "boss", invisible)
