import math

from core.chat_blob import ChatBlob
from core.command_param_types import Int, Decimal, Item
from core.decorators import instance, command
from core.dict_object import DictObject


@instance()
class SpecialsController:
    def __init__(self):
        pass

    def inject(self, registry):
        self.db = registry.get_instance("db")
        self.text = registry.get_instance("text")
        self.util = registry.get_instance("util")
        self.items_controller = registry.get_instance("items_controller")
        self.command_alias_service = registry.get_instance("command_alias_service")

    def pre_start(self):
        self.db.load_sql_file(self.module_dir + "/" + "weapon_attributes.sql", pre_optimized=True)
        self.db.create_view("weapon_attributes")

    def start(self):
        self.command_alias_service.add_alias("aimshot", "aimedshot")
        self.command_alias_service.add_alias("as", "aimedshot")
        self.command_alias_service.add_alias("inits", "weapon")
        self.command_alias_service.add_alias("init", "weapon")
        self.command_alias_service.add_alias("specials", "weapon")
        self.command_alias_service.add_alias("fling", "flingshot")

    @command(command="aggdef", params=[Decimal("weapon_attack"), Decimal("weapon_recharge"), Int("init_skill")],
             access_level="member",
             description="Show agg/def information")
    def aggdef_cmd(self, _, weapon_attack, weapon_recharge, init_skill):
        init_result = self.get_init_result(weapon_attack, weapon_recharge, init_skill)

        inits_full_agg = self.get_inits_needed(100, weapon_attack, weapon_recharge)
        inits_neutral = self.get_inits_needed(87.5, weapon_attack, weapon_recharge)
        inits_full_def = self.get_inits_needed(0, weapon_attack, weapon_recharge)

        blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
        blob += f"Recharge: <highlight>{weapon_recharge:.2f} secs</highlight>\n"
        blob += f"Init Skill: <highlight>{init_skill:d}</highlight>\n\n"

        blob += "You must set your AGG/DEF bar at <highlight>%d%%</highlight> to wield your weapon at 1/1.\n\n" % int(
            init_result)

        blob += f"Init needed for max speed at Full Agg (100%): <highlight>{inits_full_agg:d}</highlight>\n"
        blob += f"Init needed for max speed at Neutral (87.5%): <highlight>{inits_neutral:d}</highlight>\n"
        blob += f"Init needed for max speed at Full Def (0%): <highlight>{inits_full_def:d}</highlight>\n\n"

        blob += self.get_inits_display(weapon_attack, weapon_recharge) + "\n\n"

        blob += "Note that at the neutral position (87.5%), your attack and recharge time " \
                "will match that of the weapon you are using.\n\n\n"

        blob += "Based on the !aggdef command from Budabot, which was based upon a RINGBOT module made " \
                "by NoGoal(RK2) and modified for Budabot by Healnjoo(RK2)"

        return ChatBlob("Agg/Def Results", blob)

    @command(command="aimedshot",
             params=[Decimal("weapon_attack"), Decimal("weapon_recharge"), Int("aimed_shot_skill")],
             access_level="member",
             description="Show aimed shot information")
    def aimedshot_cmd(self, _, weapon_attack, weapon_recharge, aimed_shot_skill):
        as_info = self.get_aimed_shot_info(weapon_attack, weapon_recharge, aimed_shot_skill)

        blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
        blob += f"Recharge: <highlight>{weapon_recharge:.2f} secs</highlight>\n"
        blob += f"Aimed Shot Skill: <highlight>{aimed_shot_skill:d}</highlight>\n\n"

        blob += f"Aimed Shot Multiplier: <highlight>1 - {as_info.multiplier:d}x</highlight>\n"
        blob += f"Aimed Shot Recharge: <highlight>{as_info.recharge:d} secs</highlight>\n\n"

        blob += f"You need <highlight>{as_info.skill_cap:d}</highlight> Aimed Shot Skill " \
                f"to cap your recharge at <highlight>{as_info.hard_cap:d}</highlight> seconds."

        return ChatBlob("Aimed Shot Results", blob)

    @command(command="brawl", params=[Int("brawl_skill")], access_level="member",
             description="Show brawl information")
    def brawl_cmd(self, _, brawl_skill):
        brawl_info = self.get_brawl_info(brawl_skill)

        blob = f"Brawl Skill: <highlight>{brawl_skill:d}</highlight>\n\n"

        blob += "Brawl Recharge: <highlight>15 secs</highlight> (constant)\n"
        blob += f"Brawl Damage: <highlight> {brawl_info.min_dmg:d} - {brawl_info.max_dmg:d} " \
                f"({brawl_info.crit_dmg:d})</highlight>\n"
        blob += f"Stun Change: <highlight>{brawl_info.stun_chance:d}%</highlight>\n"
        blob += f"Stun Duration: <highlight>{brawl_info.stun_duration:d} secs</highlight>\n\n"

        blob += "Stun chance is 10% for brawl skill less than 1000 and 20% for brawl skill 1000 or greater.\n"
        blob += "Stun duration is 3 seconds for brawl skill less than 2001 and " \
                "4 seconds for brawl skill 2001 or greater.\n\n\n"

        blob += "Based on the !brawl command from Budabot by Imoutochan (RK1)"

        return ChatBlob("Brawl Results", blob)

    @command(command="burst",
             params=[Decimal("weapon_attack"), Decimal("weapon_recharge"), Int("burst_recharge"), Int("burst_skill")],
             access_level="member",
             description="Show burst information")
    def burst_cmd(self, _, weapon_attack, weapon_recharge, burst_recharge, burst_skill):
        burst_info = self.get_burst_info(weapon_attack, weapon_recharge, burst_recharge, burst_skill)

        blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
        blob += f"Recharge: <highlight>{weapon_recharge:.2f} secs</highlight>\n"
        blob += f"Burst Recharge: <highlight>{burst_recharge:d}</highlight>\n"
        blob += f"Burst Skill: <highlight>{burst_skill:d}</highlight>\n\n"

        blob += f"Burst Recharge: <highlight>{burst_info.recharge:d} secs</highlight>\n\n"

        blob += f"You need <highlight>{burst_info.skill_cap:d}</highlight> Burst Skill " \
                f"to cap your recharge at <highlight>{burst_info.hard_cap:d} secs</highlight>."

        return ChatBlob("Burst Results", blob)

    @command(command="dimach", params=[Int("dimach_skill")], access_level="member",
             description="Show dimach information")
    def dimach_cmd(self, _, dimach_skill):
        dimach_info = self.get_dimach_info(dimach_skill)

        blob = f"Dimach Skill: <highlight>{dimach_skill:d}</highlight>\n\n"

        blob += "<header2>Martial Artist</header2>\n"
        blob += f"Damage: <highlight>{dimach_info.ma_dmg:d}</highlight>\n"
        blob += f"Recharge: <highlight>{self.util.time_to_readable(dimach_info.ma_recharge)}</highlight>\n\n"

        blob += "<header2>Keeper</header2>\n"
        blob += f"Self Heal: <highlight>{dimach_info.keeper_heal:d}</highlight>\n"
        blob += "Recharge: <highlight>5 mins</highlight> (constant)\n\n"

        blob += "<header2>Shade</header2>\n"
        blob += f"Damage: <highlight>{dimach_info.shade_dmg:d}</highlight>\n"
        blob += f"Self Heal: <highlight>{dimach_info.shade_heal_percentage:d}%</highlight> " \
                f"* <highlight>{dimach_info.shade_dmg:d}</highlight> " \
                f"= <highlight>{round(dimach_info.shade_heal_percentage * dimach_info.shade_dmg / 100):d}</highlight>\n"
        blob += f"Recharge: <highlight>{self.util.time_to_readable(dimach_info.shade_recharge)}</highlight>\n\n"

        blob += "<header2>All other professions</header2>\n"
        blob += f"Damage: <highlight>{dimach_info.general_dmg:d}</highlight>\n"
        blob += "Recharge: <highlight>30 mins</highlight> (constant)\n\n\n"

        blob += "Based on the !dimach command from Budabot by Imoutochan (RK1)"

        return ChatBlob("Dimach Results", blob)

    @command(command="fastattack", params=[Decimal("weapon_attack"), Int("fast_attack_skill")], access_level="member",
             description="Show fast attack information")
    def fastattack_cmd(self, _, weapon_attack, fast_attack_skill):
        fast_attack_info = self.get_fast_attack_info(weapon_attack, fast_attack_skill)

        blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
        blob += f"Fast Attack Skill: <highlight>{fast_attack_skill:d}</highlight>\n\n"

        blob += f"Fast Attack Recharge: <highlight>{fast_attack_info.recharge:.2f} secs</highlight>\n\n"

        blob += f"You need <highlight>{fast_attack_info.skill_cap:d}</highlight> Fast Attack Skill " \
                f"to cap your recharge at <highlight>{fast_attack_info.hard_cap:.2f} secs</highlight>."

        return ChatBlob("Fast Attack Results", blob)

    @command(command="flingshot", params=[Decimal("weapon_attack"), Int("fling_shot_skill")], access_level="member",
             description="Show fling shot information")
    def flingshot_cmd(self, _, weapon_attack, fling_shot_skill):
        fling_shot_info = self.get_fling_shot_info(weapon_attack, fling_shot_skill)

        blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
        blob += f"Fling Shot Skill: <highlight>{fling_shot_skill:d}</highlight>\n\n"

        blob += f"Fling Shot Recharge: <highlight>{fling_shot_info.recharge:.2f} secs</highlight>\n\n"

        blob += f"You need <highlight>{fling_shot_info.skill_cap:d}</highlight> Fling Shot Skill " \
                f"to cap your recharge at <highlight>{fling_shot_info.hard_cap:.2f} secs</highlight>."

        return ChatBlob("Fling Shot Results", blob)

    @command(command="fullauto",
             params=[Decimal("weapon_attack"), Decimal("weapon_recharge"), Int("full_auto_recharge"),
                     Int("full_auto_skill")], access_level="member",
             description="Show full auto information")
    def fullauto_cmd(self, _, weapon_attack, weapon_recharge, full_auto_recharge, full_auto_skill):
        full_auto_info = self.get_full_auto_info(weapon_attack, weapon_recharge, full_auto_recharge, full_auto_skill)

        blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
        blob += f"Recharge: <highlight>{weapon_recharge:.2f} secs</highlight>\n"
        blob += f"Full Auto Recharge: <highlight>{full_auto_recharge:.2f}</highlight>\n"
        blob += f"Full Auto Skill: <highlight>{full_auto_skill:.2f}</highlight>\n\n"

        blob += f"Full Auto Recharge: <highlight>{full_auto_info.recharge:.2f} secs</highlight>\n"
        blob += f"Max Number of Bullets: <highlight>{full_auto_info.max_bullets:.2f}</highlight>\n\n"

        blob += f"You need <highlight>{full_auto_info.skill_cap:.2f}</highlight> Full Auto Skill " \
                f"to cap your recharge at <highlight>{full_auto_info.hard_cap:.2f} secs</highlight>.\n\n"

        blob += "From <highlight>0 to 10K</highlight> damage, the bullet damage is unchanged.\n"
        blob += "From <highlight>10K to 11.5K</highlight> damage, each bullet damage is halved.\n"
        blob += "From <highlight>11K to 15K</highlight> damage, each bullet damage is halved again.\n"
        blob += "<highlight>15K</highlight> is the damage cap."

        return ChatBlob("Full Auto Results", blob)

    @command(command="mafist", params=[Int("ma_skill")], access_level="member",
             description="Show martial arts information")
    def mafist_cmd(self, _, ma_skill):
        ma_info = self.get_martial_arts_info(ma_skill)

        blob = f"Martial Arts Skill: <highlight>{ma_skill:d}</highlight>\n\n"

        blob += "<header2>Martial Artist</header2>\n"
        blob += f"Speed: <highlight>{ma_info.ma_speed:.2f} / {ma_info.ma_speed:.2f} secs</highlight>\n"
        blob += f"Damage: <highlight>{ma_info.ma_min_dmg:.2f} - {ma_info.ma_max_dmg:.2f} " \
                f"({ma_info.ma_crit_dmg:.2f})</highlight>\n\n"

        blob += "<header2>Shade</header2>\n"
        blob += f"Speed: <highlight>{ma_info.shade_speed:.2f} / {ma_info.shade_speed:.2f} secs</highlight>\n"
        blob += f"Damage: <highlight>{ma_info.shade_min_dmg:.2f} - {ma_info.shade_max_dmg:.2f} " \
                f"({ma_info.shade_crit_dmg:.2f})</highlight>\n\n"

        blob += "<header2>All other professions</header2>\n"
        blob += f"Speed: <highlight>{ma_info.gen_speed:.2f} / {ma_info.gen_speed:.2f} secs</highlight>\n"
        blob += f"Damage: <highlight>{ma_info.gen_min_dmg:.2f} - {ma_info.gen_max_dmg:.2f} " \
                f"({ma_info.gen_crit_dmg:.2f})</highlight>\n\n"

        return ChatBlob("Martial Arts Results", blob)

    @command(command="nanoinit", params=[Decimal("nano_attack_time"), Int("nano_cast_init")], access_level="member",
             description="Show nano cast init information")
    def nanoinit_cmd(self, _, nano_attack_time, nano_cast_init):
        nano_cast_info = self.get_nano_cast_info(nano_cast_init, nano_attack_time)

        blob = f"Attack: <highlight>{nano_attack_time:.2f} secs</highlight>\n"
        blob += f"Nano Cast Init: <highlight>{nano_cast_init}</highlight>\n\n"

        blob += f"Cast Time Reduction: <highlight>{nano_cast_info.cast_time_reduction:.2f}</highlight>\n"
        blob += f"Effective Cast Time: <highlight>{nano_cast_info.effective_cast_time:.2f}</highlight>\n\n"

        if nano_cast_info.bar_setting > 100:
            blob += "You cannot instacast this nano at any AGG/DEF setting.\n\n"
        else:
            blob += f"You must set your AGG/DEF bar to <highlight>{nano_cast_info.bar_setting:d}%</highlight> " \
                    f"to instacast this nano.\n\n"

        blob += f"NanoC. Init needed to instacast at Full Agg (100%): " \
                f"<highlight>{nano_cast_info.instacast_full_agg:.2f}</highlight>\n"
        blob += f"NanoC. Init needed to instacast at Neutral (87.5%): " \
                f"<highlight>{nano_cast_info.instacast_neutral:.2f}</highlight>\n"
        blob += f"NanoC. Init needed to instacast at Half (50%): " \
                f"<highlight>{nano_cast_info.instacast_half:.2f}</highlight>\n"
        blob += f"NanoC. Init needed to instacast at Full Def (0%): " \
                f"<highlight>{nano_cast_info.instacast_full_def:.2f}</highlight>\n\n"

        blob += f"Cast time at Full Agg (100%): <highlight>{nano_cast_info.cast_time_full_agg:.2f}</highlight>\n"
        blob += f"Cast time at Neutral (87.5%): <highlight>{nano_cast_info.cast_time_neutral:.2f}</highlight>\n"
        blob += f"Cast time at Half (50%): <highlight>{nano_cast_info.cast_time_half:.2f}</highlight>\n"
        blob += f"Cast time at Full Def (0%): <highlight>{nano_cast_info.cast_time_full_def:.2f}</highlight>"

        return ChatBlob("Nano Cast Init Results", blob)

    @command(command="weapon", params=[Int("item_id"), Int("ql", is_optional=True)], access_level="member",
             description="Show weapon information")
    def weapon_cmd(self, _, item_id, ql):
        return self.get_weapon_info(item_id, ql)

    @command(command="deflect", params=[Int("deflect_skill")], access_level="member",
             description="Show deflect information")
    def deflect_cmd(self, _, deflect_skill):
        if deflect_skill > 3000 or deflect_skill < 1:
            return "Your deflect skill can only be between 1 and 3000"
        return f"With {deflect_skill} skill you will deflect {round(deflect_skill / 300, 2)}% of attacks"

    @command(command="weapon", params=[Item("weapon_link")], access_level="member",
             description="Show weapon information")
    def weapon_cmd(self, _, item):
        return self.get_weapon_info(item.high_id, item.ql)

    @command(command="weapon", params=[Int("item_id"), Int("ql", is_optional=True)], access_level="member",
             description="Show weapon information")
    def weapon_manual_cmd(self, _, item_id, ql):
        if not ql:
            item = self.items_controller.get_by_item_id(item_id)
            if item:
                ql = item.highql
            else:
                return f"Could not find item with id <highlight>{item_id:d}</highlight>."

        return self.get_weapon_info(item_id, ql)

    def get_weapon_info(self, item_id, ql):
        if ql:
            item = self.db.query_single(
                "SELECT * FROM aodb WHERE highid = ? AND lowql <= ? AND highql >= ? "
                "UNION "
                "SELECT * FROM aodb WHERE lowid = ? AND lowql <= ? AND highql >= ? "
                "LIMIT 1", [item_id, ql, ql, item_id, ql, ql])
        else:
            item = self.db.query_single(
                "SELECT * FROM aodb WHERE highid = ? UNION SELECT * FROM aodb WHERE lowid = ? LIMIT 1",
                [item_id, item_id])

        if not item:
            return f"Could not find item with ID <highlight>{item_id}</highlight>."

        ql = ql or item.highql

        low_attributes = self.db.query_single("SELECT * FROM weapon_attributes WHERE id = ?", [item.lowid])
        high_attributes = self.db.query_single("SELECT * FROM weapon_attributes WHERE id = ?", [item.highid])

        if not low_attributes or not high_attributes:
            return f"Could not find weapon information or item is not a weapon for " \
                   f"ID <highlight>{item_id}</highlight>."

        weapon_attack = self.util.interpolate_value(ql, {item.lowql: low_attributes.attack_time,
                                                         item.highql: high_attributes.attack_time}) / 100
        weapon_recharge = self.util.interpolate_value(ql, {item.lowql: low_attributes.recharge_time,
                                                           item.highql: high_attributes.recharge_time}) / 100

        blob = f"{self.text.make_item(item.lowid, item.highid, ql, item.name)} (QL {ql:d})\n\n"
        blob += f"Attack: <highlight>{weapon_attack:.2f}</highlight>\n"
        blob += f"Recharge: <highlight>{weapon_recharge:.2f}</highlight>\n\n"

        blob += self.get_inits_display(weapon_attack, weapon_recharge) + "\n"

        blob += "<header2>Specials</header2>\n"

        if high_attributes.aimed_shot:
            as_info = self.get_aimed_shot_info(weapon_attack, weapon_recharge, 1)
            blob += f"Aimed Shot\n<highlight>{as_info.skill_cap:.0f}</highlight> skill needed to cap " \
                    f"Aimed Shot recharge at <highlight>{as_info.hard_cap:.2f} secs</highlight>\n\n"

        if high_attributes.burst:
            burst_recharge = self.util.interpolate_value(ql, {item.lowql: low_attributes.burst,
                                                              item.highql: high_attributes.burst})
            burst_info = self.get_burst_info(weapon_attack, weapon_recharge, burst_recharge, 1)
            blob += f"Burst Recharge: <highlight>{burst_recharge:.2f}</highlight>\n" \
                    f"<highlight>{burst_info.skill_cap:d}</highlight> skill needed " \
                    f"to cap Burst recharge at <highlight>{burst_info.hard_cap:.2f} secs</highlight>\n\n"

        if high_attributes.fast_attack:
            fast_attack_info = self.get_fast_attack_info(weapon_attack, 1)
            blob += f"Fast Attack\n<highlight>{fast_attack_info.skill_cap:.0f}</highlight> skill needed " \
                    f"to cap Fast Attack recharge at <highlight>{fast_attack_info.hard_cap:.2f} secs</highlight>\n\n"

        if high_attributes.fling_shot:
            fling_shot_info = self.get_fling_shot_info(weapon_attack, 1)
            blob += f"Fling Shot\n<highlight>{fling_shot_info.skill_cap}</highlight> skill needed " \
                    f"to cap Fling Shot recharge at <highlight>{fling_shot_info.hard_cap:.2f} secs</highlight>\n\n"

        if high_attributes.full_auto:
            full_auto_recharge = self.util.interpolate_value(ql, {item.lowql: low_attributes.full_auto,
                                                                  item.highql: high_attributes.full_auto})
            full_auto_info = self.get_full_auto_info(weapon_attack, weapon_recharge, full_auto_recharge, 1)
            blob += f"Full Auto Recharge: <highlight>{full_auto_recharge:.2f}</highlight>\n" \
                    f"<highlight>{full_auto_info.skill_cap:.0f}</highlight> skill needed " \
                    f"to cap Full Auto recharge at <highlight>{full_auto_info.hard_cap:.2f} secs</highlight>\n\n"

        return ChatBlob(f"Weapon Info for {item.name} (QL {ql})", blob)

    def get_init_result(self, weapon_attack, weapon_recharge, init_skill):
        if init_skill < 1200:
            attack_calc = (((weapon_attack - (init_skill / 600)) - 1) / 0.02) + 87.5
            recharge_calc = (((weapon_recharge - (init_skill / 300)) - 1) / 0.02) + 87.5
        else:
            attack_calc = (((weapon_attack - (1200 / 600) - ((init_skill - 1200) / 600 / 3)) - 1) / 0.02) + 87.5
            recharge_calc = (((weapon_recharge - (1200 / 300) - ((init_skill - 1200) / 300 / 3)) - 1) / 0.02) + 87.5

        if attack_calc < recharge_calc:
            init_result = recharge_calc
        else:
            init_result = attack_calc

        init_result = min(init_result, 100)  # max of 100
        init_result = max(init_result, 0)  # min of 0

        return init_result

    def get_inits_needed(self, init_result, weapon_attack, weapon_recharge):
        inits_attack = (((init_result - 87.5) * 0.02) + 1 - weapon_attack) * -600
        inits_recharge = (((init_result - 87.5) * 0.02) + 1 - weapon_recharge) * -300

        if inits_attack > 1200:
            inits_attack = ((((init_result - 87.5) * 0.02) + 1 - weapon_attack + 2) * -1800) + 1200

        if inits_recharge > 1200:
            inits_recharge = ((((init_result - 87.5) * 0.02) + 1 - weapon_attack + 4) * -900) + 1200

        if inits_attack < inits_recharge:
            return int(inits_recharge)
        else:
            return int(inits_attack)

    def get_aimed_shot_info(self, weapon_attack, weapon_recharge, aimed_shot_skill):
        result = DictObject()
        result.multiplier = round(aimed_shot_skill / 95)
        result.hard_cap = math.floor(weapon_attack + 10)
        result.skill_cap = math.ceil((4000 * weapon_recharge - 1100) / 3)

        as_recharge = math.ceil((weapon_recharge * 40) - (aimed_shot_skill * 3 / 100) + weapon_attack - 1)
        if as_recharge < result.hard_cap:
            as_recharge = result.hard_cap

        result.recharge = as_recharge

        return result

    def get_brawl_info(self, brawl_skill):
        min_values = {1: 1, 1000: 100, 1001: 101, 2000: 170, 2001: 171, 3000: 235}
        max_values = {1: 2, 1000: 500, 1001: 501, 2000: 850, 2001: 851, 3000: 1145}
        crit_values = {1: 3, 1000: 500, 1001: 501, 2000: 600, 2001: 601, 3000: 725}

        brawl_info = DictObject()
        brawl_info.min_dmg = self.util.interpolate_value(brawl_skill, min_values)
        brawl_info.max_dmg = self.util.interpolate_value(brawl_skill, max_values)
        brawl_info.crit_dmg = self.util.interpolate_value(brawl_skill, crit_values)
        brawl_info.stun_chance = 10 if brawl_skill < 1000 else 20
        brawl_info.stun_duration = 3 if brawl_skill < 2001 else 4

        return brawl_info

    def get_burst_info(self, weapon_attack, weapon_recharge, burst_recharge, burst_skill):
        result = DictObject()
        result.hard_cap = round(weapon_attack + 8)
        result.skill_cap = math.floor(((weapon_recharge * 20) + (burst_recharge / 100) - 8) * 25)

        recharge = math.floor((weapon_recharge * 20) + (burst_recharge / 100) - (burst_skill / 25) + weapon_attack)
        if recharge < result.hard_cap:
            recharge = result.hard_cap

        result.recharge = recharge

        return result

    def get_dimach_info(self, dimach_skill):
        # item ids: 42033, 42032, 213260, 213261, 213262, 213263
        general_dmg = {1: 1, 1000: 2000, 1001: 2001, 2000: 2500, 2001: 2501, 3000: 2850}

        # item ids: 42033, 42032, 213264, 213265, 213266, 213267
        ma_recharge = {1: 1800, 1000: 1800, 1001: 1188, 2000: 600, 2001: 600, 3000: 300}
        ma_dmg = {1: 1, 1000: 2000, 1001: 2001, 2000: 2340, 2001: 2341, 3000: 2550}

        # item ids: 213269, 213270, 213271, 213272, 213273, 213274
        shade_recharge = {1: 300, 1000: 300, 1001: 300, 2000: 300, 2001: 240, 3000: 200}
        shade_dmg = {1: 1, 1000: 920, 1001: 921, 2000: 1872, 2001: 1873, 3000: 2750}
        shade_heal_percentage = {1: 70, 1000: 70, 1001: 70, 2000: 75, 2001: 75, 3000: 80}

        # item ids: 211399, 211400, 213275, 213276, 213277, 213278
        keeper_heal = {1: 1, 1000: 3000, 1001: 3001, 2000: 10500, 2001: 10501, 3000: 15000}

        result = DictObject()
        result.general_dmg = self.util.interpolate_value(dimach_skill, general_dmg)
        result.ma_recharge = self.util.interpolate_value(dimach_skill, ma_recharge)
        result.ma_dmg = self.util.interpolate_value(dimach_skill, ma_dmg)
        result.shade_recharge = self.util.interpolate_value(dimach_skill, shade_recharge)
        result.shade_dmg = self.util.interpolate_value(dimach_skill, shade_dmg)
        result.shade_heal_percentage = self.util.interpolate_value(dimach_skill, shade_heal_percentage)
        result.keeper_heal = self.util.interpolate_value(dimach_skill, keeper_heal)

        return result

    def get_fast_attack_info(self, weapon_attack, fast_attack_skill):
        result = DictObject()
        result.hard_cap = weapon_attack + 5
        result.skill_cap = ((weapon_attack * 16) - result.hard_cap) * 100

        recharge = (weapon_attack * 16) - (fast_attack_skill / 100)
        if recharge < result.hard_cap:
            recharge = result.hard_cap

        result.recharge = recharge

        return result

    def get_fling_shot_info(self, weapon_attack, fling_shot_skill):
        result = DictObject()
        result.hard_cap = weapon_attack + 5
        result.skill_cap = ((weapon_attack * 16) - result.hard_cap) * 100

        recharge = (weapon_attack * 16) - (fling_shot_skill / 100)
        if recharge < result.hard_cap:
            recharge = result.hard_cap

        result.recharge = recharge

        return result

    def get_full_auto_info(self, weapon_attack, weapon_recharge, full_auto_recharge, full_auto_skill):
        result = DictObject()
        result.hard_cap = math.floor(weapon_attack + 10)
        result.skill_cap = ((weapon_recharge * 40) + (full_auto_recharge / 100) - 11) * 25
        result.max_bullets = 5 + math.floor(full_auto_skill / 100)

        recharge = round(
            (weapon_recharge * 40) + (full_auto_recharge / 100) - (full_auto_skill / 25) + round(weapon_attack - 1))
        if recharge < result.hard_cap:
            recharge = result.hard_cap

        result.recharge = recharge

        return result

    def get_martial_arts_info(self, ma_skill):
        result = DictObject()

        # ma items: http://budabot.com/forum/viewtopic.php?f=7&t=1264&p=5739#p5739
        #  QL    1       100     500     1      500      1      500
        #     211349, 211350, 211351, 211359, 211360, 211365, 211366    // Shade
        #     211352, 211353, 211354, 211357, 211358, 211363, 211364    // MA
        #      43712, 144745,  43713, 211355, 211356, 211361, 211362    // Gen/other

        ma_min_dmg = {1: 4, 200: 45, 1000: 125, 1001: 130, 2000: 220, 2001: 225, 3000: 450}
        ma_max_dmg = {1: 8, 200: 75, 1000: 400, 1001: 405, 2000: 830, 2001: 831, 3000: 1300}
        ma_crit_dmg = {1: 3, 200: 50, 1000: 500, 1001: 501, 2000: 560, 2001: 561, 3000: 800}
        ma_speed = {1: 1.15, 200: 1.20, 1000: 1.25, 1001: 1.30, 2000: 1.35, 2001: 1.45, 3000: 1.50}

        shade_min_dmg = {1: 3, 200: 25, 1000: 55, 1001: 56, 2000: 130, 2001: 131, 3000: 280}
        shade_max_dmg = {1: 5, 200: 60, 1000: 258, 1001: 259, 2000: 682, 2001: 683, 3000: 890}
        shade_crit_dmg = {1: 3, 200: 50, 1000: 250, 1001: 251, 2000: 275, 2001: 276, 3000: 300}
        shade_speed = {1: 1.25, 200: 1.25, 1000: 1.45, 1001: 1.45, 2000: 1.65, 2001: 1.65, 3000: 1.85}

        gen_min_dmg = {1: 3, 200: 25, 1000: 65, 1001: 66, 2000: 140, 2001: 204, 3000: 300}
        gen_max_dmg = {1: 5, 200: 60, 1000: 280, 1001: 281, 2000: 715, 2001: 831, 3000: 990}
        gen_crit_dmg = {1: 3, 200: 50, 1000: 500, 1001: 501, 2000: 605, 2001: 605, 3000: 630}
        gen_speed = {1: 1.25, 200: 1.25, 1000: 1.45, 1001: 1.45, 2000: 1.65, 2001: 1.65, 3000: 1.85}

        result.ma_min_dmg = self.util.interpolate_value(ma_skill, ma_min_dmg)
        result.ma_max_dmg = self.util.interpolate_value(ma_skill, ma_max_dmg)
        result.ma_crit_dmg = self.util.interpolate_value(ma_skill, ma_crit_dmg)
        result.ma_speed = self.util.interpolate_value(ma_skill, ma_speed, 2)

        result.shade_min_dmg = self.util.interpolate_value(ma_skill, shade_min_dmg)
        result.shade_max_dmg = self.util.interpolate_value(ma_skill, shade_max_dmg)
        result.shade_crit_dmg = self.util.interpolate_value(ma_skill, shade_crit_dmg)
        result.shade_speed = self.util.interpolate_value(ma_skill, shade_speed, 2)

        result.gen_min_dmg = self.util.interpolate_value(ma_skill, gen_min_dmg)
        result.gen_max_dmg = self.util.interpolate_value(ma_skill, gen_max_dmg)
        result.gen_crit_dmg = self.util.interpolate_value(ma_skill, gen_crit_dmg)
        result.gen_speed = self.util.interpolate_value(ma_skill, gen_speed, 2)

        return result

    def get_nano_cast_info(self, nano_cast_init, nano_attack_time):
        if nano_cast_init > 1200:
            nano_cast_reduction = (nano_cast_init - 1200) / 600 + 6
        else:
            nano_cast_reduction = nano_cast_init / 200

        result = DictObject()
        result.cast_time_reduction = nano_cast_reduction
        result.effective_cast_time = nano_attack_time - nano_cast_reduction
        result.instacast_full_agg = self.get_nano_init_for_instacast(nano_attack_time - 1)
        result.instacast_neutral = self.get_nano_init_for_instacast(nano_attack_time - 0.75)
        result.instacast_half = self.get_nano_init_for_instacast(nano_attack_time)
        result.instacast_full_def = self.get_nano_init_for_instacast(nano_attack_time + 1)
        result.cast_time_full_agg = result.effective_cast_time - 1
        result.cast_time_neutral = result.effective_cast_time - 0.75
        result.cast_time_half = result.effective_cast_time
        result.cast_time_full_def = result.effective_cast_time + 1

        bar_setting = round(result.effective_cast_time / 0.02 + 50)
        if bar_setting < 0:
            bar_setting = 0
        result.bar_setting = bar_setting

        return result

    def get_nano_init_for_instacast(self, nano_attack_time):
        if nano_attack_time < 6:
            return nano_attack_time * 200
        else:
            return 1200 + (nano_attack_time - 6) * 600

    def get_inits_display(self, weapon_attack, weapon_recharge):
        num_steps = 9
        step_size = 100 / (num_steps - 1)
        blob = ""
        for i in reversed(range(0, num_steps)):
            inits_needed = self.get_inits_needed(i * step_size, weapon_attack, weapon_recharge)
            blob += f"DEF [{'=' * i}||{'=' * (num_steps - 1 - i)}] AGG {i * step_size:.0f}% {inits_needed:.0f} init \n"

        return blob
