class Weapon:
    name = ""
    cost = "0 gp"
    base_damage = "1d4"
    damage_bonus = 0
    attack_bonus = 0
    damage_type = "p"
    weight = 1  # In lbs
    properties = ""
    ability = "strength"
    is_finesse = False
    features_applied = False

    def __init__(self, wielder=None):
        self.wielder = wielder

    @classmethod
    def improved_version(cls, bonus):
        bonus = int(bonus)

        class NewWeapon(cls):
            name = f"+{bonus} " + cls.name
            damage_bonus = bonus
            attack_bonus = bonus

        return NewWeapon

    def apply_features(self):
        if (not self.features_applied) and (self.wielder is not None):
            self.features_applied = True
            for f in self.wielder.features:
                f.weapon_func(self)

    @property
    def ability_mod(self):
        if self.wielder is None:
            return 0
        else:
            if self.is_finesse:
                return max(
                    self.wielder.strength.modifier, self.wielder.dexterity.modifier
                )
            else:
                return getattr(self.wielder, self.ability).modifier

    @property
    def attack_modifier(self):
        self.apply_features()
        mod = self.attack_bonus
        if self.wielder is not None:
            mod += self.ability_mod
            if self.wielder.is_proficient(self):
                mod += self.wielder.proficiency_bonus
        return mod

    @property
    def damage(self):
        self.apply_features()
        dam_str = str(self.base_damage)
        bonus = self.damage_bonus
        if self.wielder is not None:
            bonus += self.ability_mod
        if bonus != 0:
            dam_str += "{:+d}".format(bonus)
        return dam_str

    def __str__(self):
        return self.name

    def __repr__(self):
        return '"{:s}"'.format(self.name)


class MeleeWeapon(Weapon):
    name = "Melee Weapons"
    ability = "strength"


class RangedWeapon(Weapon):
    name = "Ranged Weapons"
    ability = "dexterity"


class SimpleWeapon(Weapon):
    name = "Simple Weapons"


class MartialWeapon(Weapon):
    name = "Martial Weapons"


class Club(SimpleWeapon, MeleeWeapon):
    name = "Club"
    cost = "1 sp"
    base_damage = "1d4"
    damage_type = "b"
    weight = 2
    properties = "Light"
    ability = "strength"


class Dagger(SimpleWeapon, MeleeWeapon):
    name = "Dagger"
    cost = "2 gp"
    base_damage = "1d4"
    damage_type = "p"
    weight = 1
    properties = "Finesse, light, thrown (range 20/60)"
    is_finesse = True
    ability = "strength"


class Greatclub(SimpleWeapon, MeleeWeapon):
    name = "Greatclub"
    cost = "2 sp"
    base_damage = "1d8"
    damage_type = "b"
    weight = 10
    properties = "Two-handed"
    ability = "strength"


class Handaxe(SimpleWeapon, MeleeWeapon):
    name = "Handaxe"
    cost = "5 gp"
    base_damage = "1d6"
    damage_type = "s"
    weight = 2
    properties = "Light, thrown (range 20/60)"
    ability = "strength"


class Javelin(SimpleWeapon, MeleeWeapon):
    name = "Javelin"
    cost = "5 sp"
    base_damage = "1d6"
    damage_type = "p"
    weight = 2
    properties = "Thrown (range 30/120)"
    ability = "strength"


class LightHammer(SimpleWeapon, MeleeWeapon):
    name = "Light hammer"
    cost = "2 gp"
    base_damage = "1d4"
    damage_type = "b"
    weight = 2
    properties = "Light, thrown (range 20/60)"
    ability = "strength"


class Mace(SimpleWeapon, MeleeWeapon):
    name = "Mace"
    cost = "5 gp"
    base_damage = "1d6"
    damage_type = "b"
    weight = 4
    properties = ""
    ability = "strength"


class Quarterstaff(SimpleWeapon, MeleeWeapon):
    name = "Quarterstaff"
    cost = "2 sp"
    base_damage = "1d6"
    damage_type = "b"
    weight = 4
    properties = "Versatile (1d8)"
    ability = "strength"


class Sickle(SimpleWeapon, MeleeWeapon):
    name = "Sickle"
    cost = "1 gp"
    base_damage = "1d4"
    damage_type = "s"
    weight = 2
    properties = "Light"
    ability = "strength"


class Spear(SimpleWeapon, MeleeWeapon):
    name = "Spear"
    cost = "1 gp"
    base_damage = "1d6"
    damage_type = "p"
    weight = 3
    properties = "Thrown (range 20/60), versatile (1d8)"
    ability = "strength"


class LightCrossbow(SimpleWeapon, RangedWeapon):
    name = "Light crossbow"
    cost = "25 gp"
    base_damage = "1d8"
    damage_type = "p"
    weight = 5
    properties = "Ammunition (range 80/320), loading, two-handed"
    ability = "dexterity"


class Dart(SimpleWeapon, RangedWeapon):
    name = "Dart"
    cost = "5 cp"
    base_damage = "1d4"
    damage_type = "p"
    weight = 0.25
    properties = "Finesse, thrown (range 20/60)"
    is_finesse = True
    ability = "dexterity"


class Shortbow(SimpleWeapon, RangedWeapon):
    name = "Shortbow"
    cost = "25 gp"
    base_damage = "1d6"
    damage_type = "p"
    weight = 2
    properties = "Ammunition (range 80/320), two-handed"
    ability = "dexterity"


class Sling(SimpleWeapon, RangedWeapon):
    name = "Sling"
    cost = "1 sp"
    base_damage = "1d4"
    damage_type = "b"
    weight = 0
    properties = "Ammunition (range 30/120)"
    ability = "dexterity"


class Battleaxe(MartialWeapon, MeleeWeapon):
    name = "Battleaxe"
    cost = "10 gp"
    base_damage = "1d8"
    damage_type = "s"
    weight = 4
    properties = "Versatile (1d10)"
    ability = "strength"


class Flail(MartialWeapon, MeleeWeapon):
    name = "Flail"
    cost = "10gp"
    base_damage = "1d8"
    damage_type = "b"
    weight = 2
    properties = ""
    ability = "strength"


class Glaive(MartialWeapon, MeleeWeapon):
    name = "Glaive"
    cost = "20 gp"
    base_damage = "1d10"
    damage_type = "s"
    weight = 6
    properties = "Heavy, reach, two-handed"
    ability = "strength"


class Greataxe(MartialWeapon, MeleeWeapon):
    name = "Greataxe"
    cost = "30 gp"
    base_damage = "1d12"
    damage_type = "s"
    weight = 7
    properties = "Heavy, two-handed"
    ability = "strength"


class Greatsword(MartialWeapon, MeleeWeapon):
    name = "Greatsword"
    cost = "50 gp"
    base_damage = "2d6"
    damage_type = "s"
    weight = 6
    properties = "Heavy, two-handed"
    ability = "strength"


class Halberd(MartialWeapon, MeleeWeapon):
    name = "Halberd"
    cost = "20 gp"
    base_damage = "1d10"
    damage_type = "s"
    weight = 6
    properties = "Heavy, reach, two-handed"
    ability = "strength"


class Lance(MartialWeapon, MeleeWeapon):
    name = "Lance"
    cost = "10gp"
    base_damage = "1d12"
    damage_type = "p"
    weight = 6
    properties = "Reach, special"
    ability = "strength"


class Longsword(MartialWeapon, MeleeWeapon):
    name = "Longsword"
    cost = "15 gp"
    base_damage = "1d8"
    damage_type = "s"
    weight = 3
    properties = "Versatile (1d10)"
    ability = "strength"


class Maul(MartialWeapon, MeleeWeapon):
    name = "Maul"
    cost = "10 gp"
    base_damage = "2d6"
    damage_type = "b"
    weight = 10
    properties = "Heavy, two-handed"
    ability = "strength"


class Morningstar(MartialWeapon, MeleeWeapon):
    name = "Morningstar"
    cost = "15 gp"
    base_damage = "1d8"
    damage_type = "p"
    weight = 4
    properties = ""
    ability = "strength"


class Pike(MartialWeapon, MeleeWeapon):
    name = "Pike"
    cost = "5 gp"
    base_damage = "1d10"
    damage_type = "p"
    weight = 18
    properties = "Heavy, reach, two-handed"
    ability = "strength"


class Rapier(MartialWeapon, MeleeWeapon):
    name = "Rapier"
    cost = "25 gp"
    base_damage = "1d8"
    damage_type = "p"
    weight = 2
    properties = "Finesse"
    is_finesse = True
    ability = "strength"


class Scimitar(MartialWeapon, MeleeWeapon):
    name = "Scimitar"
    cost = "25 gp"
    base_damage = "1d6"
    damage_type = "s"
    weight = 3
    properties = "Finesse, light"
    is_finesse = True
    ability = "strength"


class Shortsword(MartialWeapon, MeleeWeapon):
    name = "Shortsword"
    cost = "10 gp"
    base_damage = "1d6"
    damage_type = "p"
    weight = 0
    properties = "Finesse, light"
    is_finesse = True
    ability = "strength"


class ThrowingHammer(MartialWeapon, MeleeWeapon):
    name = "Throwing Hammer"
    cost = "15 gp"
    base_damage = "1d6"
    damage_type = "b"
    weight = 4
    properties = "Thrown (range 60/120)"
    ability = "strength"


class Trident(MartialWeapon, MeleeWeapon):
    name = "Trident"
    cost = "5 gp"
    base_damage = "1d6"
    damage_type = "p"
    weight = 4
    properties = "Thrown (range 20/60), versatile (1d8)"
    ability = "strength"


class WarPick(MartialWeapon, MeleeWeapon):
    name = "War pick"
    cost = "5 gp"
    base_damage = "1d8"
    damage_type = "p"
    weight = 2
    properties = ""
    ability = "strength"


class Warhammer(MartialWeapon, MeleeWeapon):
    name = "Warhammer"
    cost = "15 gp"
    base_damage = "1d8"
    damage_type = "b"
    weight = 2
    properties = "Versatile (1d10)"
    ability = "strength"


class Whip(MartialWeapon, MeleeWeapon):
    name = "Whip"
    cost = "2 gp"
    base_damage = "1d4"
    damage_type = "s"
    weight = 3
    properties = "Finesse, reach"
    is_finesse = True
    ability = "strength"


class Blowgun(MartialWeapon, RangedWeapon):
    name = "Blowgun"
    cost = "10 gp"
    base_damage = "1"
    damage_type = "p"
    weight = 1
    properties = "Ammunition (range 25/100), loading"
    ability = "dexterity"


class HandCrossbow(MartialWeapon, RangedWeapon):
    name = "Hand Crossbow"
    cost = "75 gp"
    base_damage = "1d6"
    damage_type = "p"
    weight = 3
    properties = "Ammunition (range 30/120), light, loading"
    ability = "dexterity"


class HeavyCrossbow(MartialWeapon, RangedWeapon):
    name = "Heavy Crossbow"
    cost = "50 gp"
    base_damage = "1d10"
    damage_type = "p"
    weight = 18
    properties = "Ammunition (range 100/400), heaving, loading, two-handed"
    ability = "dexterity"


class Longbow(MartialWeapon, RangedWeapon):
    name = "Longbow"
    cost = "50 gp"
    base_damage = "1d8"
    damage_type = "p"
    weight = 2
    properties = "Ammunition (range 150/600), heavy, two-handed"
    ability = "dexterity"


class Net(MartialWeapon, RangedWeapon):
    name = "Net"
    cost = "1 gp"
    base_damage = "-"
    damage_type = ""
    weight = 3
    properties = "Special, thrown (range 5/15)"
    ability = "strength"


class Unarmed(MeleeWeapon):
    name = "Unarmed"
    cost = "0 gp"
    base_damage = "1"
    damage_type = "b"
    weight = 0
    properties = ""
    ability = "strength"


MonkUnarmedStrike = Unarmed
UnarmedStrike = Unarmed


class SunBolt(RangedWeapon):
    name = "Sun Bolt"
    cost = "0 gp"
    base_damage = "1d4"
    damage_type = "r"
    weight = 0
    properties = "(range 20/60)"
    ability = "dexterity"


# Custom weapons
class HeavyPunch(MeleeWeapon):
    base_damage = "1d4"
    name = "Heavy Punch"
    damage_type = "b"
    damage_bonus = 10  # Heavy weapon master
    attack_bonus = -5  # Heavy weapon master


class Bite(MeleeWeapon):
    name = "Bite"
    base_damage = "1d4"
    damage_type = "p"
    cost = "0 gp"
    weight = 0
    properties = ""
    ability = "strength"


class Talons(MeleeWeapon):
    name = "Talons"
    base_damage = "1d4"
    damage_type = "s"
    cost = "0 gp"
    weight = 0
    properties = ""
    ability = "strength"


class Claws(MeleeWeapon):
    name = "Claws"
    base_damage = "1d4"
    damage_type = "s"
    cost = "0 gp"
    weight = 0
    properties = ""
    ability = "strength"


class Firearm(RangedWeapon):
    name = "Firearm"
    ability = "dexterity"
    damage_type = "p"


class Blunderbuss(Firearm):
    name = "Blunderbuss"
    base_damage = "2d8"
    cost = "300 gp"
    weight = 10
    properties = "Ammunition (range 15/60), Reload 1, Misfire 2"


class Pistol(Firearm):
    name = "Pistol"
    base_damage = "1d10"
    cost = "150 gp"
    weight = 3
    properties = "Ammunition (range 60/240), Reload 4, Misfire 1"


class Musket(Firearm):
    name = "Musket"
    base_damage = "1d12"
    cost = "300"
    weight = 10
    properties = "Ammunition (range 120/480), Two-Handed, Reload 1, Misfire 2"


# Magic Items
class FlameTongue(Greatsword):
    name = "Flame Tongue +1"
    damage_bonus = 1
    attack_bonus = 1
    base_damage = "4d6"
    damage_type = "f"


class SpearOfLightning(Spear):
    name = "Spear +1"
    damage_bonus = 1
    attack_bonus = 1


# Some lists of weapons for easy proficiency resolution
simple_melee_weapons = (
    Club,
    Dagger,
    Greatclub,
    Handaxe,
    Javelin,
    LightHammer,
    Mace,
    Quarterstaff,
    Sickle,
    Spear,
)
simple_ranged_weapons = (LightCrossbow, Dart, Shortbow, Sling)
simple_weapons = simple_melee_weapons + simple_ranged_weapons

martial_melee_weapons = (
    Battleaxe,
    Flail,
    Glaive,
    Greataxe,
    Greatsword,
    Halberd,
    Lance,
    Longsword,
    Maul,
    Morningstar,
    Pike,
    Rapier,
    Scimitar,
    Shortsword,
    ThrowingHammer,
    Trident,
    WarPick,
    Warhammer,
    Whip,
)
martial_ranged_weapons = (Blowgun, HandCrossbow, HeavyCrossbow, Longbow, Net)
martial_weapons = martial_melee_weapons + martial_ranged_weapons

monk_weapons = (
    Shortsword,
    Unarmed,
    Club,
    Dagger,
    Handaxe,
    Javelin,
    LightHammer,
    Mace,
    Quarterstaff,
    Sickle,
    Spear,
    SunBolt,
)

firearms = (Firearm, Blunderbuss, Pistol, Musket)
