use super::{
    img_ids::{Imgs, ImgsRot},
    item_imgs::{animate_by_pulse, ItemImgs},
    Position, PositionSpecifier, Show, BLACK, CRITICAL_HP_COLOR, HP_COLOR, TEXT_COLOR,
    UI_HIGHLIGHT_0, UI_MAIN, XP_COLOR,
};
use crate::{
    game_input::GameInput,
    hud::{
        self,
        slots::{AbilitySlot, SlotManager},
        util,
    },
    ui::{
        fonts::Fonts,
        slot::{ContentSize, SlotMaker},
        ImageFrame, Tooltip, TooltipManager, Tooltipable,
    },
    GlobalState,
};
use conrod_core::{
    color, image,
    position::Relative,
    widget::{self, Button, Image, Rectangle, State, Text},
    widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon,
};
use i18n::Localization;
use vek::*;

use client::{self, Client};
use common::{
    combat,
    comp::{
        self,
        ability::{Ability, ActiveAbilities, AuxiliaryAbility, MAX_ABILITIES},
        inventory::{
            item::{
                item_key::ItemKey,
                tool::{AbilityContext, ToolKind},
                ItemKind, MaterialStatManifest,
            },
            slot::EquipSlot,
        },
        skills::{
            self, AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, MiningSkill,
            RollSkill, SceptreSkill, Skill, StaffSkill, SwimSkill, SwordSkill, SKILL_MODIFIERS,
        },
        skillset::{SkillGroupKind, SkillSet},
        Body, Energy, Health, Inventory, Poise,
    },
    consts::{ENERGY_PER_LEVEL, HP_PER_LEVEL},
};
use std::borrow::Cow;

const ART_SIZE: [f64; 2] = [320.0, 320.0];

widget_ids! {
    pub struct Ids {
        frame,
        bg,
        icon,
        close,
        title,
        content_align,
        section_imgs[],
        section_btns[],
        // Skill tree stuffs
        exp_bar_bg,
        exp_bar_frame,
        exp_bar_content_align,
        exp_bar_content,
        exp_bar_rank,
        exp_bar_txt,
        active_bar_checkbox,
        tree_title_txt,
        lock_imgs[],
        available_pts_txt,
        weapon_imgs[],
        weapon_btns[],
        skills_top_l_align,
        skills_top_r_align,
        skills_bot_l_align,
        skills_bot_r_align,
        skills_top_l[],
        skills_top_r[],
        skills_bot_l[],
        skills_bot_r[],
        skills[],
        skill_lock_imgs[],
        sword_bg,
        axe_render,
        skill_axe_combo_0,
        skill_axe_combo_1,
        skill_axe_combo_2,
        skill_axe_combo_3,
        skill_axe_combo_4,
        skill_axe_spin_0,
        skill_axe_spin_1,
        skill_axe_spin_2,
        skill_axe_spin_3,
        skill_axe_spin_4,
        skill_axe_spin_5,
        skill_axe_leap_0,
        skill_axe_leap_1,
        skill_axe_leap_2,
        skill_axe_leap_3,
        skill_axe_leap_4,
        hammer_render,
        skill_hammer_combo_0,
        skill_hammer_combo_1,
        skill_hammer_combo_2,
        skill_hammer_combo_3,
        skill_hammer_combo_4,
        skill_hammer_charged_0,
        skill_hammer_charged_1,
        skill_hammer_charged_2,
        skill_hammer_charged_3,
        skill_hammer_charged_4,
        skill_hammer_leap_0,
        skill_hammer_leap_1,
        skill_hammer_leap_2,
        skill_hammer_leap_3,
        skill_hammer_leap_4,
        skill_hammer_leap_5,
        bow_render,
        skill_bow_charged_0,
        skill_bow_charged_1,
        skill_bow_charged_2,
        skill_bow_charged_3,
        skill_bow_charged_4,
        skill_bow_charged_5,
        skill_bow_repeater_0,
        skill_bow_repeater_1,
        skill_bow_repeater_2,
        skill_bow_repeater_3,
        skill_bow_shotgun_0,
        skill_bow_shotgun_1,
        skill_bow_shotgun_2,
        skill_bow_shotgun_3,
        skill_bow_shotgun_4,
        skill_bow_passive_0,
        staff_render,
        skill_staff_basic_0,
        skill_staff_basic_1,
        skill_staff_basic_2,
        skill_staff_basic_3,
        skill_staff_basic_4,
        skill_staff_beam_0,
        skill_staff_beam_1,
        skill_staff_beam_2,
        skill_staff_beam_3,
        skill_staff_beam_4,
        skill_staff_shockwave_0,
        skill_staff_shockwave_1,
        skill_staff_shockwave_2,
        skill_staff_shockwave_3,
        skill_staff_shockwave_4,
        sceptre_render,
        skill_sceptre_lifesteal_0,
        skill_sceptre_lifesteal_1,
        skill_sceptre_lifesteal_2,
        skill_sceptre_lifesteal_3,
        skill_sceptre_lifesteal_4,
        skill_sceptre_heal_0,
        skill_sceptre_heal_1,
        skill_sceptre_heal_2,
        skill_sceptre_heal_3,
        skill_sceptre_heal_4,
        skill_sceptre_aura_0,
        skill_sceptre_aura_1,
        skill_sceptre_aura_2,
        skill_sceptre_aura_3,
        skill_sceptre_aura_4,
        pick_render,
        skill_pick_m1,
        skill_pick_m1_0,
        skill_pick_m1_1,
        skill_pick_m1_2,
        general_combat_render_0,
        general_combat_render_1,
        skill_general_stat_0,
        skill_general_stat_1,
        skill_general_tree_0,
        skill_general_tree_1,
        skill_general_tree_2,
        skill_general_tree_3,
        skill_general_tree_4,
        skill_general_tree_5,
        skill_general_roll_0,
        skill_general_roll_1,
        skill_general_roll_2,
        skill_general_roll_3,
        skill_general_climb_0,
        skill_general_climb_1,
        skill_general_climb_2,
        skill_general_swim_0,
        skill_general_swim_1,
        // Ability selection
        spellbook_art,
        sb_page_left_align,
        sb_page_right_align,
        spellbook_skills_bg,
        spellbook_btn,
        spellbook_btn_bg,
        ability_select_title,
        ability_page_left,
        ability_page_right,
        active_abilities[],
        active_abilities_keys[],
        main_weap_select,
        off_weap_select,
        abilities[],
        ability_frames[],
        abilities_dual[],
        ability_titles[],
        ability_descs[],
        dragged_ability,
        // Stats
        stat_names[],
        stat_values[],
    }
}

#[derive(WidgetCommon)]
pub struct Diary<'a> {
    show: &'a Show,
    _client: &'a Client,
    global_state: &'a GlobalState,
    skill_set: &'a SkillSet,
    active_abilities: &'a ActiveAbilities,
    inventory: &'a Inventory,
    health: &'a Health,
    energy: &'a Energy,
    poise: &'a Poise,
    body: &'a Body,
    msm: &'a MaterialStatManifest,
    imgs: &'a Imgs,
    item_imgs: &'a ItemImgs,
    fonts: &'a Fonts,
    localized_strings: &'a Localization,
    rot_imgs: &'a ImgsRot,
    tooltip_manager: &'a mut TooltipManager,
    slot_manager: &'a mut SlotManager,
    pulse: f32,
    context: Option<AbilityContext>,

    #[conrod(common_builder)]
    common: widget::CommonBuilder,
    created_btns_top_l: usize,
    created_btns_top_r: usize,
    created_btns_bot_l: usize,
    created_btns_bot_r: usize,
}

pub struct DiaryShow {
    pub skilltreetab: SelectedSkillTree,
    pub section: DiarySection,
}

impl Default for DiaryShow {
    fn default() -> Self {
        Self {
            skilltreetab: SelectedSkillTree::General,
            section: DiarySection::SkillTrees,
        }
    }
}

#[allow(clippy::too_many_arguments)]
impl<'a> Diary<'a> {
    pub fn new(
        show: &'a Show,
        _client: &'a Client,
        global_state: &'a GlobalState,
        skill_set: &'a SkillSet,
        active_abilities: &'a ActiveAbilities,
        inventory: &'a Inventory,
        health: &'a Health,
        energy: &'a Energy,
        poise: &'a Poise,
        body: &'a Body,
        msm: &'a MaterialStatManifest,
        imgs: &'a Imgs,
        item_imgs: &'a ItemImgs,
        fonts: &'a Fonts,
        localized_strings: &'a Localization,
        rot_imgs: &'a ImgsRot,
        tooltip_manager: &'a mut TooltipManager,
        slot_manager: &'a mut SlotManager,
        pulse: f32,
        context: Option<AbilityContext>,
    ) -> Self {
        Self {
            show,
            _client,
            global_state,
            skill_set,
            active_abilities,
            inventory,
            health,
            energy,
            poise,
            body,
            msm,
            imgs,
            item_imgs,
            fonts,
            localized_strings,
            rot_imgs,
            tooltip_manager,
            slot_manager,
            pulse,
            context,
            common: widget::CommonBuilder::default(),
            created_btns_top_l: 0,
            created_btns_top_r: 0,
            created_btns_bot_l: 0,
            created_btns_bot_r: 0,
        }
    }
}

pub type SelectedSkillTree = SkillGroupKind;

// TODO: make it enum?
const TREES: [&str; 8] = [
    "General Combat",
    "Sword",
    "Hammer",
    "Axe",
    "Sceptre",
    "Bow",
    "Fire Staff",
    "Mining",
];

// Possible future sections: Bestiary ("Pokedex" of fought enemies), Weapon and
// armour catalogue, Achievements...
const SECTIONS: [&str; 3] = ["Skill-Trees", "Abilities", "Stats"];

pub enum Event {
    Close,
    ChangeSkillTree(SelectedSkillTree),
    UnlockSkill(Skill),
    ChangeSection(DiarySection),
    SelectExpBar(Option<SkillGroupKind>),
}

#[derive(PartialEq, Eq)]
pub enum DiarySection {
    SkillTrees,
    AbilitySelection,
    Stats,
}

pub struct DiaryState {
    ids: Ids,
    ability_page: usize,
}

impl<'a> Widget for Diary<'a> {
    type Event = Vec<Event>;
    type State = DiaryState;
    type Style = ();

    fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
        DiaryState {
            ids: Ids::new(id_gen),
            ability_page: 0,
        }
    }

    fn style(&self) -> Self::Style {}

    fn update(mut self, args: widget::UpdateArgs<Self>) -> Self::Event {
        common_base::prof_span!("Diary::update");
        let widget::UpdateArgs { state, ui, .. } = args;
        let mut events = Vec::new();

        // Tooltips
        let diary_tooltip = Tooltip::new({
            // Edge images [t, b, r, l]
            // Corner images [tr, tl, br, bl]
            let edge = &self.rot_imgs.tt_side;
            let corner = &self.rot_imgs.tt_corner;
            ImageFrame::new(
                [edge.cw180, edge.none, edge.cw270, edge.cw90],
                [corner.none, corner.cw270, corner.cw90, corner.cw180],
                Color::Rgba(0.08, 0.07, 0.04, 1.0),
                5.0,
            )
        })
        .title_font_size(self.fonts.cyri.scale(15))
        .parent(ui.window)
        .desc_font_size(self.fonts.cyri.scale(12))
        .font_id(self.fonts.cyri.conrod_id)
        .desc_text_color(TEXT_COLOR);

        //Animation timer Frame
        let frame_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8;

        Image::new(self.imgs.diary_bg)
            .w_h(1202.0, 886.0)
            .mid_top_with_margin_on(ui.window, 5.0)
            .color(Some(UI_MAIN))
            .set(state.ids.bg, ui);

        Image::new(self.imgs.diary_frame)
            .w_h(1202.0, 886.0)
            .middle_of(state.ids.bg)
            .color(Some(UI_HIGHLIGHT_0))
            .set(state.ids.frame, ui);

        // Icon
        Image::new(self.imgs.spellbook_button)
            .w_h(30.0, 27.0)
            .top_left_with_margins_on(state.ids.frame, 8.0, 8.0)
            .set(state.ids.icon, ui);

        // X-Button
        if Button::image(self.imgs.close_button)
            .w_h(24.0, 25.0)
            .hover_image(self.imgs.close_btn_hover)
            .press_image(self.imgs.close_btn_press)
            .top_right_with_margins_on(state.ids.frame, 0.0, 0.0)
            .set(state.ids.close, ui)
            .was_clicked()
        {
            events.push(Event::Close);
        }

        // Title
        Text::new(&self.localized_strings.get_msg("hud-diary"))
            .mid_top_with_margin_on(state.ids.frame, 3.0)
            .font_id(self.fonts.cyri.conrod_id)
            .font_size(self.fonts.cyri.scale(29))
            .color(TEXT_COLOR)
            .set(state.ids.title, ui);

        // Content Alignment
        Rectangle::fill_with([599.0 * 2.0, 419.0 * 2.0], color::TRANSPARENT)
            .mid_top_with_margin_on(state.ids.frame, 46.0)
            .set(state.ids.content_align, ui);

        // Contents
        // Section buttons
        let sel_section = &self.show.diary_fields.section;

        // Update len
        state.update(|s| {
            s.ids
                .section_imgs
                .resize(SECTIONS.len(), &mut ui.widget_id_generator())
        });
        state.update(|s| {
            s.ids
                .section_btns
                .resize(SECTIONS.len(), &mut ui.widget_id_generator())
        });
        for (i, section_name) in SECTIONS.iter().copied().enumerate() {
            let section = match section_from_str(section_name) {
                Some(st) => st,
                None => {
                    tracing::warn!("unexpected section name: {}", section_name);
                    continue;
                },
            };
            // Section Icons
            let section_desc = match section_name {
                "Abilities" => "List of your currently available abilities.",
                "Skill-Trees" => "",
                "Stats" => "",
                _ => "",
            };
            let btn_img = {
                let img = match section_name {
                    "Abilities" => self.imgs.spellbook_ico,
                    "Skill-Trees" => self.imgs.skilltree_ico,
                    "Stats" => self.imgs.stats_ico,
                    _ => self.imgs.nothing,
                };
                if i == 0 {
                    Image::new(img).top_left_with_margins_on(state.ids.content_align, 0.0, -50.0)
                } else {
                    Image::new(img).down_from(state.ids.section_btns[i - 1], 5.0)
                }
            };
            btn_img.w_h(50.0, 50.0).set(state.ids.section_imgs[i], ui);
            // Section Buttons
            let border_image = if section == *sel_section {
                self.imgs.wpn_icon_border_pressed
            } else {
                self.imgs.wpn_icon_border
            };

            let hover_image = if section == *sel_section {
                self.imgs.wpn_icon_border_pressed
            } else {
                self.imgs.wpn_icon_border_mo
            };

            let press_image = if section == *sel_section {
                self.imgs.wpn_icon_border_pressed
            } else {
                self.imgs.wpn_icon_border_press
            };
            let section_buttons = Button::image(border_image)
                .w_h(50.0, 50.0)
                .hover_image(hover_image)
                .press_image(press_image)
                .middle_of(state.ids.section_imgs[i])
                .with_tooltip(
                    self.tooltip_manager,
                    section_name,
                    section_desc,
                    &diary_tooltip,
                    TEXT_COLOR,
                )
                .set(state.ids.section_btns[i], ui);
            if section_buttons.was_clicked() {
                events.push(Event::ChangeSection(section))
            }
        }
        match self.show.diary_fields.section {
            DiarySection::SkillTrees => {
                // Skill Trees
                let sel_tab = &self.show.diary_fields.skilltreetab;

                // Skill Tree Selection
                state.update(|s| {
                    s.ids
                        .weapon_btns
                        .resize(TREES.len(), &mut ui.widget_id_generator())
                });
                state.update(|s| {
                    s.ids
                        .weapon_imgs
                        .resize(TREES.len(), &mut ui.widget_id_generator())
                });
                state.update(|s| {
                    s.ids
                        .lock_imgs
                        .resize(TREES.len(), &mut ui.widget_id_generator())
                });

                // Draw skillgroup tab's icons
                for (i, skilltree_name) in TREES.iter().copied().enumerate() {
                    let skill_group = match skill_tree_from_str(skilltree_name) {
                        Some(st) => st,
                        None => {
                            tracing::warn!("unexpected tree name: {}", skilltree_name);
                            continue;
                        },
                    };

                    // Check if we have this skill tree unlocked
                    let locked = !self.skill_set.skill_group_accessible(skill_group);

                    // Weapon button image
                    let btn_img = {
                        let img = match skilltree_name {
                            "General Combat" => self.imgs.swords_crossed,
                            "Sword" => self.imgs.sword,
                            "Hammer" => self.imgs.hammer,
                            "Axe" => self.imgs.axe,
                            "Sceptre" => self.imgs.sceptre,
                            "Bow" => self.imgs.bow,
                            "Fire Staff" => self.imgs.staff,
                            "Mining" => self.imgs.mining,
                            _ => self.imgs.nothing,
                        };

                        if i == 0 {
                            Image::new(img).top_left_with_margins_on(
                                state.ids.content_align,
                                10.0,
                                5.0,
                            )
                        } else {
                            Image::new(img).down_from(state.ids.weapon_btns[i - 1], 5.0)
                        }
                    };
                    btn_img.w_h(50.0, 50.0).set(state.ids.weapon_imgs[i], ui);

                    // Lock Image
                    if locked {
                        Image::new(self.imgs.lock)
                            .w_h(50.0, 50.0)
                            .middle_of(state.ids.weapon_imgs[i])
                            .graphics_for(state.ids.weapon_imgs[i])
                            .color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.8)))
                            .set(state.ids.lock_imgs[i], ui);
                    }

                    // Weapon icons
                    let have_points = {
                        let available = self.skill_set.available_sp(skill_group);
                        let earned = self.skill_set.earned_sp(skill_group);
                        let total_cost = skill_group.total_skill_point_cost();

                        available > 0 && (earned - available) < total_cost
                    };

                    let border_image = if skill_group == *sel_tab || have_points {
                        self.imgs.wpn_icon_border_pressed
                    } else {
                        self.imgs.wpn_icon_border
                    };

                    let hover_image = if skill_group == *sel_tab {
                        self.imgs.wpn_icon_border_pressed
                    } else {
                        self.imgs.wpn_icon_border_mo
                    };

                    let press_image = if skill_group == *sel_tab {
                        self.imgs.wpn_icon_border_pressed
                    } else {
                        self.imgs.wpn_icon_border_press
                    };

                    let color = if skill_group != *sel_tab && have_points {
                        Color::Rgba(0.92, 0.76, 0.0, frame_ani)
                    } else {
                        TEXT_COLOR
                    };

                    let tooltip_txt = if locked {
                        self.localized_strings.get_msg("hud-skill-not_unlocked")
                    } else {
                        Cow::Borrowed("")
                    };

                    let wpn_button = Button::image(border_image)
                        .w_h(50.0, 50.0)
                        .hover_image(hover_image)
                        .press_image(press_image)
                        .middle_of(state.ids.weapon_imgs[i])
                        .image_color(color)
                        .with_tooltip(
                            self.tooltip_manager,
                            skilltree_name,
                            &tooltip_txt,
                            &diary_tooltip,
                            TEXT_COLOR,
                        )
                        .set(state.ids.weapon_btns[i], ui);
                    if wpn_button.was_clicked() {
                        events.push(Event::ChangeSkillTree(skill_group))
                    }
                }

                // Exp Bars and Rank Display
                let current_exp = self.skill_set.available_experience(*sel_tab) as f64;
                let max_exp = self.skill_set.skill_point_cost(*sel_tab) as f64;
                let exp_percentage = current_exp / max_exp;
                let rank = self.skill_set.earned_sp(*sel_tab);
                let rank_txt = format!("{}", rank);
                let exp_txt = format!("{}/{}", current_exp, max_exp);
                let available_pts = self.skill_set.available_sp(*sel_tab);
                Image::new(self.imgs.diary_exp_bg)
                    .w_h(480.0, 76.0)
                    .mid_bottom_with_margin_on(state.ids.content_align, 10.0)
                    .set(state.ids.exp_bar_bg, ui);
                Rectangle::fill_with([400.0, 40.0], color::TRANSPARENT)
                    .top_left_with_margins_on(state.ids.exp_bar_bg, 32.0, 40.0)
                    .set(state.ids.exp_bar_content_align, ui);
                Image::new(self.imgs.bar_content)
                    .w_h(400.0 * exp_percentage, 40.0)
                    .top_left_with_margins_on(state.ids.exp_bar_content_align, 0.0, 0.0)
                    .color(Some(XP_COLOR))
                    .set(state.ids.exp_bar_content, ui);
                Image::new(self.imgs.diary_exp_frame)
                    .w_h(480.0, 76.0)
                    .color(Some(UI_HIGHLIGHT_0))
                    .middle_of(state.ids.exp_bar_bg)
                    .set(state.ids.exp_bar_frame, ui);
                // Show as Exp bar below skillbar
                let exp_selected =
                    self.global_state.settings.interface.xp_bar_skillgroup == Some(*sel_tab);
                if Button::image(if !exp_selected {
                    self.imgs.checkbox
                } else {
                    self.imgs.checkbox_checked
                })
                .w_h(24.0, 25.0)
                .hover_image(if !exp_selected {
                    self.imgs.checkbox_mo
                } else {
                    self.imgs.checkbox_checked_mo
                })
                .press_image(if !exp_selected {
                    self.imgs.checkbox_press
                } else {
                    self.imgs.checkbox_checked
                })
                .top_right_with_margins_on(state.ids.exp_bar_frame, 50.0, -40.0)
                .label(&self.localized_strings.get_msg("hud-skill-set_as_exp_bar"))
                .label_font_size(self.fonts.cyri.scale(14))
                .label_color(TEXT_COLOR)
                .label_font_id(self.fonts.cyri.conrod_id)
                .label_x(Relative::Scalar(87.0))
                .label_y(Relative::Scalar(2.0))
                .set(state.ids.active_bar_checkbox, ui)
                .was_clicked()
                {
                    if self.global_state.settings.interface.xp_bar_skillgroup != Some(*sel_tab) {
                        events.push(Event::SelectExpBar(Some(*sel_tab)));
                    } else {
                        events.push(Event::SelectExpBar(None));
                    }
                }

                // Show EXP bar text on hover
                if ui
                    .widget_input(state.ids.exp_bar_frame)
                    .mouse()
                    .map_or(false, |m| m.is_over())
                {
                    Text::new(&exp_txt)
                        .mid_top_with_margin_on(state.ids.exp_bar_frame, 47.0)
                        .font_id(self.fonts.cyri.conrod_id)
                        .font_size(self.fonts.cyri.scale(14))
                        .color(TEXT_COLOR)
                        .graphics_for(state.ids.exp_bar_frame)
                        .set(state.ids.exp_bar_txt, ui);
                }
                Text::new(&rank_txt)
                    .mid_top_with_margin_on(state.ids.exp_bar_frame, match rank {
                        0..=99 => 5.0,
                        100..=999 => 8.0,
                        _ => 10.0,
                    })
                    .font_id(self.fonts.cyri.conrod_id)
                    .font_size(self.fonts.cyri.scale(match rank {
                        0..=99 => 28,
                        100..=999 => 21,
                        _ => 15,
                    }))
                    .color(TEXT_COLOR)
                    .set(state.ids.exp_bar_rank, ui);

                Text::new(&self.localized_strings.get_msg_ctx(
                    "hud-skill-sp_available",
                    &i18n::fluent_args! {
                        "number" => available_pts,
                    },
                ))
                .mid_top_with_margin_on(state.ids.content_align, 700.0)
                .font_id(self.fonts.cyri.conrod_id)
                .font_size(self.fonts.cyri.scale(28))
                .color(if available_pts > 0 {
                    Color::Rgba(0.92, 0.76, 0.0, frame_ani)
                } else {
                    TEXT_COLOR
                })
                .set(state.ids.available_pts_txt, ui);
                // Skill Trees
                // Alignment Placing
                let x = 200.0;
                let y = 100.0;
                // Alignment rectangles for skills
                Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
                    .top_left_with_margins_on(state.ids.content_align, y, x)
                    .set(state.ids.skills_top_l_align, ui);
                Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
                    .top_right_with_margins_on(state.ids.content_align, y, x)
                    .set(state.ids.skills_top_r_align, ui);
                Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
                    .bottom_left_with_margins_on(state.ids.content_align, y, x)
                    .set(state.ids.skills_bot_l_align, ui);
                Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
                    .bottom_right_with_margins_on(state.ids.content_align, y, x)
                    .set(state.ids.skills_bot_r_align, ui);

                match sel_tab {
                    SelectedSkillTree::General => {
                        self.handle_general_skills_window(&diary_tooltip, state, ui, events)
                    },
                    SelectedSkillTree::Weapon(ToolKind::Sword) => {
                        self.handle_sword_skills_window(&diary_tooltip, state, ui, events)
                    },
                    SelectedSkillTree::Weapon(ToolKind::Hammer) => {
                        self.handle_hammer_skills_window(&diary_tooltip, state, ui, events)
                    },
                    SelectedSkillTree::Weapon(ToolKind::Axe) => {
                        self.handle_axe_skills_window(&diary_tooltip, state, ui, events)
                    },
                    SelectedSkillTree::Weapon(ToolKind::Sceptre) => {
                        self.handle_sceptre_skills_window(&diary_tooltip, state, ui, events)
                    },
                    SelectedSkillTree::Weapon(ToolKind::Bow) => {
                        self.handle_bow_skills_window(&diary_tooltip, state, ui, events)
                    },
                    SelectedSkillTree::Weapon(ToolKind::Staff) => {
                        self.handle_staff_skills_window(&diary_tooltip, state, ui, events)
                    },
                    SelectedSkillTree::Weapon(ToolKind::Pick) => {
                        self.handle_mining_skills_window(&diary_tooltip, state, ui, events)
                    },
                    _ => events,
                }
            },
            DiarySection::AbilitySelection => {
                use comp::ability::AbilityInput;

                // Background Art
                Image::new(self.imgs.book_bg)
                    .w_h(299.0 * 4.0, 184.0 * 4.0)
                    .mid_top_with_margin_on(state.ids.content_align, 4.0)
                    //.graphics_for(state.ids.content_align)
                    .set(state.ids.spellbook_art, ui);
                Image::new(self.imgs.skills_bg)
                    .w_h(240.0 * 2.0, 40.0 * 2.0)
                    .mid_bottom_with_margin_on(state.ids.content_align, 8.0)
                    .set(state.ids.spellbook_skills_bg, ui);

                Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT)
                    .top_left_with_margins_on(state.ids.spellbook_art, 0.0, 0.0)
                    .set(state.ids.sb_page_left_align, ui);
                Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT)
                    .top_right_with_margins_on(state.ids.spellbook_art, 0.0, 0.0)
                    .set(state.ids.sb_page_right_align, ui);

                // Display all active abilities on bottom of window
                state.update(|s| {
                    s.ids
                        .active_abilities
                        .resize(MAX_ABILITIES, &mut ui.widget_id_generator())
                });
                state.update(|s| {
                    s.ids
                        .active_abilities_keys
                        .resize(MAX_ABILITIES, &mut ui.widget_id_generator())
                });

                let mut slot_maker = SlotMaker {
                    empty_slot: self.imgs.inv_slot,
                    filled_slot: self.imgs.inv_slot,
                    selected_slot: self.imgs.inv_slot_sel,
                    background_color: Some(UI_MAIN),
                    content_size: ContentSize {
                        width_height_ratio: 1.0,
                        max_fraction: 0.9,
                    },
                    selected_content_scale: 1.067,
                    amount_font: self.fonts.cyri.conrod_id,
                    amount_margins: Vec2::new(-4.0, 0.0),
                    amount_font_size: self.fonts.cyri.scale(12),
                    amount_text_color: TEXT_COLOR,
                    content_source: &(
                        self.active_abilities,
                        self.inventory,
                        self.skill_set,
                        self.context,
                    ),
                    image_source: self.imgs,
                    slot_manager: Some(self.slot_manager),
                    pulse: 0.0,
                };

                for i in 0..MAX_ABILITIES {
                    let ability_id = self
                        .active_abilities
                        .get_ability(
                            AbilityInput::Auxiliary(i),
                            Some(self.inventory),
                            Some(self.skill_set),
                        )
                        .ability_id(Some(self.inventory), self.context);
                    let (ability_title, ability_desc) = if let Some(ability_id) = ability_id {
                        util::ability_description(ability_id, self.localized_strings)
                    } else {
                        (
                            Cow::Borrowed("Drag an ability here to use it."),
                            Cow::Borrowed(""),
                        )
                    };

                    let image_size = 80.0;
                    let image_offsets = 92.0 * i as f64;

                    let slot = AbilitySlot::Slot(i);
                    let mut ability_slot = slot_maker.fabricate(slot, [image_size; 2]);

                    if i == 0 {
                        ability_slot = ability_slot.top_left_with_margins_on(
                            state.ids.spellbook_skills_bg,
                            0.0,
                            32.0 + image_offsets,
                        );
                    } else {
                        ability_slot =
                            ability_slot.right_from(state.ids.active_abilities[i - 1], 4.0)
                    }
                    ability_slot
                        .with_tooltip(
                            self.tooltip_manager,
                            &ability_title,
                            &ability_desc,
                            &diary_tooltip,
                            TEXT_COLOR,
                        )
                        .set(state.ids.active_abilities[i], ui);

                    // Display Slot Keybinding
                    let keys = &self.global_state.settings.controls;
                    let key_layout = &self.global_state.window.key_layout;
                    let ability_key = [
                        GameInput::Slot1,
                        GameInput::Slot2,
                        GameInput::Slot3,
                        GameInput::Slot4,
                        GameInput::Slot5,
                    ]
                    .get(i)
                    .and_then(|input| keys.get_binding(*input))
                    .map(|key| key.display_shortest(key_layout))
                    .unwrap_or_default();

                    Text::new(&ability_key)
                        .top_left_with_margins_on(state.ids.active_abilities[i], 0.0, 4.0)
                        .font_id(self.fonts.cyri.conrod_id)
                        .font_size(self.fonts.cyri.scale(20))
                        .color(TEXT_COLOR)
                        .graphics_for(state.ids.active_abilities[i])
                        .set(state.ids.active_abilities_keys[i], ui);
                }

                let same_weap_kinds = self
                    .inventory
                    .equipped(EquipSlot::ActiveMainhand)
                    .zip(self.inventory.equipped(EquipSlot::ActiveOffhand))
                    .map_or(false, |(a, b)| {
                        if let (ItemKind::Tool(tool_a), ItemKind::Tool(tool_b)) =
                            (&*a.kind(), &*b.kind())
                        {
                            (a.ability_spec(), tool_a.kind) == (b.ability_spec(), tool_b.kind)
                        } else {
                            false
                        }
                    });

                let main_weap_abilities = ActiveAbilities::iter_available_abilities(
                    Some(self.inventory),
                    Some(self.skill_set),
                    EquipSlot::ActiveMainhand,
                )
                .map(AuxiliaryAbility::MainWeapon)
                .map(|a| {
                    (
                        Ability::from(a).ability_id(Some(self.inventory), self.context),
                        a,
                    )
                });
                let off_weap_abilities = ActiveAbilities::iter_available_abilities(
                    Some(self.inventory),
                    Some(self.skill_set),
                    EquipSlot::ActiveOffhand,
                )
                .map(AuxiliaryAbility::OffWeapon)
                .map(|a| {
                    (
                        Ability::from(a).ability_id(Some(self.inventory), self.context),
                        a,
                    )
                });

                let abilities: Vec<_> = if same_weap_kinds {
                    // When the weapons have the same ability kind take only the main weapons,
                    main_weap_abilities.collect()
                } else {
                    main_weap_abilities.chain(off_weap_abilities).collect()
                };

                const ABILITIES_PER_PAGE: usize = 12;

                let page_indices = (abilities.len().saturating_sub(1)) / ABILITIES_PER_PAGE;

                if state.ability_page > page_indices {
                    state.update(|s| s.ability_page = 0);
                }

                state.update(|s| {
                    s.ids
                        .abilities
                        .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
                });
                state.update(|s| {
                    s.ids
                        .abilities_dual
                        .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
                });
                state.update(|s| {
                    s.ids
                        .ability_titles
                        .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
                });
                state.update(|s| {
                    s.ids
                        .ability_frames
                        .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
                });
                state.update(|s| {
                    s.ids
                        .ability_descs
                        .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
                });

                // Page button
                // Left Arrow
                let left_arrow = Button::image(if state.ability_page > 0 {
                    self.imgs.arrow_l
                } else {
                    self.imgs.arrow_l_inactive
                })
                .bottom_left_with_margins_on(state.ids.spellbook_art, -83.0, 10.0)
                .w_h(48.0, 55.0);
                // Grey out arrows when inactive
                if state.ability_page > 0 {
                    if left_arrow
                        .hover_image(self.imgs.arrow_l_click)
                        .press_image(self.imgs.arrow_l)
                        .set(state.ids.ability_page_left, ui)
                        .was_clicked()
                    {
                        state.update(|s| s.ability_page -= 1);
                    }
                } else {
                    left_arrow.set(state.ids.ability_page_left, ui);
                }
                // Right Arrow
                let right_arrow = Button::image(if state.ability_page < page_indices {
                    self.imgs.arrow_r
                } else {
                    self.imgs.arrow_r_inactive
                })
                .bottom_right_with_margins_on(state.ids.spellbook_art, -83.0, 10.0)
                .w_h(48.0, 55.0);
                if state.ability_page < page_indices {
                    // Only show right button if not on last page
                    if right_arrow
                        .hover_image(self.imgs.arrow_r_click)
                        .press_image(self.imgs.arrow_r)
                        .set(state.ids.ability_page_right, ui)
                        .was_clicked()
                    {
                        state.update(|s| s.ability_page += 1);
                    };
                } else {
                    right_arrow.set(state.ids.ability_page_right, ui);
                }

                let ability_start = state.ability_page * ABILITIES_PER_PAGE;

                let mut slot_maker = SlotMaker {
                    empty_slot: self.imgs.inv_slot,
                    filled_slot: self.imgs.inv_slot,
                    selected_slot: self.imgs.inv_slot_sel,
                    background_color: Some(UI_MAIN),
                    content_size: ContentSize {
                        width_height_ratio: 1.0,
                        max_fraction: 1.0,
                    },
                    selected_content_scale: 1.067,
                    amount_font: self.fonts.cyri.conrod_id,
                    amount_margins: Vec2::new(-4.0, 0.0),
                    amount_font_size: self.fonts.cyri.scale(12),
                    amount_text_color: TEXT_COLOR,
                    content_source: &(
                        self.active_abilities,
                        self.inventory,
                        self.skill_set,
                        self.context,
                    ),
                    image_source: self.imgs,
                    slot_manager: Some(self.slot_manager),
                    pulse: 0.0,
                };

                for (id_index, (ability_id, ability)) in abilities
                    .iter()
                    .skip(ability_start)
                    .take(ABILITIES_PER_PAGE)
                    .enumerate()
                {
                    let (ability_title, ability_desc) =
                        util::ability_description(ability_id.unwrap_or(""), self.localized_strings);

                    let (align_state, image_offsets) = if id_index < 6 {
                        (state.ids.sb_page_left_align, 120.0 * id_index as f64)
                    } else {
                        (state.ids.sb_page_right_align, 120.0 * (id_index - 6) as f64)
                    };

                    Image::new(if same_weap_kinds {
                        self.imgs.ability_frame_dual
                    } else {
                        self.imgs.ability_frame
                    })
                    .w_h(566.0, 108.0)
                    .top_left_with_margins_on(align_state, 16.0 + image_offsets, 16.0)
                    .color(Some(UI_HIGHLIGHT_0))
                    .set(state.ids.ability_frames[id_index], ui);

                    let slot = AbilitySlot::Ability(*ability);
                    slot_maker
                        .fabricate(slot, [100.0; 2])
                        .top_left_with_margins_on(align_state, 20.0 + image_offsets, 20.0)
                        .set(state.ids.abilities[id_index], ui);

                    if same_weap_kinds {
                        if let AuxiliaryAbility::MainWeapon(slot) = ability {
                            let ability = AuxiliaryAbility::OffWeapon(*slot);

                            let slot = AbilitySlot::Ability(ability);
                            slot_maker
                                .fabricate(slot, [100.0; 2])
                                .top_right_with_margins_on(align_state, 20.0 + image_offsets, 20.0)
                                .set(state.ids.abilities_dual[id_index], ui);
                        }
                    }
                    // The page width...
                    let text_width = 299.0 * 2.0
                        - if same_weap_kinds && matches!(ability, AuxiliaryAbility::MainWeapon(_)) {
                            // with double the width of an ability image and some padding subtracted
                            // if dual wielding two of the same weapon kind
                            (20.0 + 100.0 + 10.0) * 2.0
                        } else {
                            // or the width of an ability image and some padding subtracted
                            // otherwise
                            20.0 * 2.0 + 100.0
                        };
                    Text::new(&ability_title)
                        .top_left_with_margins_on(state.ids.abilities[id_index], 5.0, 110.0)
                        .font_id(self.fonts.cyri.conrod_id)
                        .font_size(self.fonts.cyri.scale(28))
                        .color(TEXT_COLOR)
                        .w(text_width)
                        .graphics_for(state.ids.abilities[id_index])
                        .set(state.ids.ability_titles[id_index], ui);
                    Text::new(&ability_desc)
                        .top_left_with_margins_on(state.ids.abilities[id_index], 40.0, 110.0)
                        .font_id(self.fonts.cyri.conrod_id)
                        .font_size(self.fonts.cyri.scale(18))
                        .color(TEXT_COLOR)
                        .w(text_width)
                        .graphics_for(state.ids.abilities[id_index])
                        .set(state.ids.ability_descs[id_index], ui);
                }

                events
            },
            DiarySection::Stats => {
                const STATS: [&str; 13] = [
                    "Hitpoints",
                    "Energy",
                    "Poise",
                    "Combat-Rating",
                    "Protection",
                    "Stun-Resistance",
                    "Crit-Power",
                    "Energy Reward",
                    "Stealth",
                    "Weapon Power",
                    "Weapon Speed",
                    "Weapon Poise",
                    "Weapon Crit-Chance",
                ];

                // Background Art
                Image::new(self.imgs.book_bg)
                    .w_h(299.0 * 4.0, 184.0 * 4.0)
                    .mid_top_with_margin_on(state.ids.content_align, 4.0)
                    .set(state.ids.spellbook_art, ui);

                state.update(|s| {
                    s.ids
                        .stat_names
                        .resize(STATS.len(), &mut ui.widget_id_generator())
                });
                state.update(|s| {
                    s.ids
                        .stat_values
                        .resize(STATS.len(), &mut ui.widget_id_generator())
                });
                for (i, stat) in STATS.iter().copied().enumerate() {
                    // Stat names
                    let mut txt = Text::new(stat)
                        .font_id(self.fonts.cyri.conrod_id)
                        .font_size(self.fonts.cyri.scale(29))
                        .color(BLACK);

                    if i == 0 {
                        txt = txt.top_left_with_margins_on(state.ids.spellbook_art, 20.0, 20.0);
                    } else {
                        txt = txt.down_from(state.ids.stat_names[i - 1], 10.0);
                    };
                    txt.set(state.ids.stat_names[i], ui);

                    let main_weap_stats = self
                        .inventory
                        .equipped(EquipSlot::ActiveMainhand)
                        .and_then(|item| match &*item.kind() {
                            ItemKind::Tool(tool) => Some(tool.stats),
                            _ => None,
                        });

                    let off_weap_stats = self
                        .inventory
                        .equipped(EquipSlot::ActiveOffhand)
                        .and_then(|item| match &*item.kind() {
                            ItemKind::Tool(tool) => Some(tool.stats),
                            _ => None,
                        });

                    // Stat values
                    let value = match stat {
                        "Hitpoints" => format!("{}", self.health.base_max() as u32),
                        "Energy" => format!("{}", self.energy.base_max() as u32),
                        "Poise" => format!("{}", self.poise.base_max() as u32),
                        "Combat-Rating" => {
                            let cr = combat::combat_rating(
                                self.inventory,
                                self.health,
                                self.energy,
                                self.poise,
                                self.skill_set,
                                *self.body,
                                self.msm,
                            );
                            format!("{:.2}", cr * 10.0)
                        },
                        "Protection" => {
                            let protection =
                                combat::compute_protection(Some(self.inventory), self.msm);
                            match protection {
                                Some(prot) => format!("{}", prot),
                                None => String::from("Invincible"),
                            }
                        },
                        "Stun-Resistance" => {
                            let stun_res = Poise::compute_poise_damage_reduction(
                                Some(self.inventory),
                                self.msm,
                                None,
                                None,
                            );
                            format!("{:.2}%", stun_res * 100.0)
                        },
                        "Crit-Power" => {
                            let critpwr = combat::compute_crit_mult(Some(self.inventory), self.msm);
                            format!("x{:.2}", critpwr)
                        },
                        "Energy Reward" => {
                            let energy_rew =
                                combat::compute_energy_reward_mod(Some(self.inventory), self.msm);
                            format!("{:+.0}%", (energy_rew - 1.0) * 100.0)
                        },
                        "Stealth" => {
                            let stealth_perception_multiplier =
                                combat::perception_dist_multiplier_from_stealth(
                                    Some(self.inventory),
                                    None,
                                    self.msm,
                                );
                            let txt =
                                format!("{:+.1}%", (1.0 - stealth_perception_multiplier) * 100.0);

                            txt
                        },
                        "Weapon Power" => match (main_weap_stats, off_weap_stats) {
                            (Some(m_stats), Some(o_stats)) => {
                                format!("{}   {}", m_stats.power * 10.0, o_stats.power * 10.0)
                            },
                            (Some(stats), None) | (None, Some(stats)) => {
                                format!("{}", stats.power * 10.0)
                            },
                            (None, None) => String::new(),
                        },
                        "Weapon Speed" => {
                            let spd_fmt = |sp| (sp - 1.0) * 100.0;
                            match (main_weap_stats, off_weap_stats) {
                                (Some(m_stats), Some(o_stats)) => format!(
                                    "{:+.0}%   {:+.0}%",
                                    spd_fmt(m_stats.speed),
                                    spd_fmt(o_stats.speed)
                                ),
                                (Some(stats), None) | (None, Some(stats)) => {
                                    format!("{:+.0}%", spd_fmt(stats.speed))
                                },
                                _ => String::new(),
                            }
                        },
                        "Weapon Poise" => match (main_weap_stats, off_weap_stats) {
                            (Some(m_stats), Some(o_stats)) => {
                                format!(
                                    "{}   {}",
                                    m_stats.effect_power * 10.0,
                                    o_stats.effect_power * 10.0
                                )
                            },
                            (Some(stats), None) | (None, Some(stats)) => {
                                format!("{}", stats.effect_power * 10.0)
                            },
                            (None, None) => String::new(),
                        },
                        "Weapon Crit-Chance" => {
                            let crit_fmt = |cc| cc * 100.0;
                            match (main_weap_stats, off_weap_stats) {
                                (Some(m_stats), Some(o_stats)) => format!(
                                    "{:.1}%   {:.1}%",
                                    crit_fmt(m_stats.crit_chance),
                                    crit_fmt(o_stats.crit_chance)
                                ),
                                (Some(stats), None) | (None, Some(stats)) => {
                                    format!("{:.1}%", crit_fmt(stats.crit_chance))
                                },
                                (None, None) => String::new(),
                            }
                        },
                        unknown => unreachable!("{}", unknown),
                    };

                    let mut number = Text::new(&value)
                        .font_id(self.fonts.cyri.conrod_id)
                        .font_size(self.fonts.cyri.scale(29))
                        .color(BLACK);

                    if i == 0 {
                        number = number.right_from(state.ids.stat_names[i], 265.0);
                    } else {
                        number = number.down_from(state.ids.stat_values[i - 1], 10.0);
                    };
                    number.set(state.ids.stat_values[i], ui);
                }

                events
            },
        }
    }
}

fn skill_tree_from_str(string: &str) -> Option<SelectedSkillTree> {
    match string {
        "General Combat" => Some(SelectedSkillTree::General),
        "Sword" => Some(SelectedSkillTree::Weapon(ToolKind::Sword)),
        "Hammer" => Some(SelectedSkillTree::Weapon(ToolKind::Hammer)),
        "Axe" => Some(SelectedSkillTree::Weapon(ToolKind::Axe)),
        "Sceptre" => Some(SelectedSkillTree::Weapon(ToolKind::Sceptre)),
        "Bow" => Some(SelectedSkillTree::Weapon(ToolKind::Bow)),
        "Fire Staff" => Some(SelectedSkillTree::Weapon(ToolKind::Staff)),
        "Mining" => Some(SelectedSkillTree::Weapon(ToolKind::Pick)),
        _ => None,
    }
}

fn section_from_str(string: &str) -> Option<DiarySection> {
    match string {
        "Abilities" => Some(DiarySection::AbilitySelection),
        "Skill-Trees" => Some(DiarySection::SkillTrees),
        "Stats" => Some(DiarySection::Stats),
        _ => None,
    }
}

enum SkillIcon<'a> {
    Unlockable {
        skill: Skill,
        image: image::Id,
        position: PositionSpecifier,
        id: widget::Id,
    },
    Descriptive {
        title: &'a str,
        desc: &'a str,
        image: image::Id,
        position: PositionSpecifier,
        id: widget::Id,
    },
    Ability {
        skill: Skill,
        ability_id: &'a str,
        position: PositionSpecifier,
    },
}

impl<'a> Diary<'a> {
    fn handle_general_skills_window(
        &mut self,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
        ui: &mut UiCell,
        mut events: Vec<Event>,
    ) -> Vec<Event> {
        let tree_title = &self.localized_strings.get_msg("common-weapons-general");
        Text::new(tree_title)
            .mid_top_with_margin_on(state.ids.content_align, 2.0)
            .font_id(self.fonts.cyri.conrod_id)
            .font_size(self.fonts.cyri.scale(34))
            .color(TEXT_COLOR)
            .set(state.ids.tree_title_txt, ui);

        // Number of skills per rectangle per weapon, start counting at 0
        // Maximum of 9 skills/8 indices
        let skills_top_l = 2;
        let skills_top_r = 6;
        let skills_bot_l = 4;
        let skills_bot_r = 5;

        self.setup_state_for_skill_icons(
            state,
            ui,
            skills_top_l,
            skills_top_r,
            skills_bot_l,
            skills_bot_r,
        );
        use skills::{GeneralSkill::*, RollSkill::*};
        use SkillGroupKind::*;
        use ToolKind::*;
        // General Combat
        Image::new(animate_by_pulse(
            &self.item_imgs.img_ids_or_not_found_img(ItemKey::Simple(
                "example_general_combat_left".to_string(),
            )),
            self.pulse,
        ))
        .wh(ART_SIZE)
        .middle_of(state.ids.content_align)
        .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
        .set(state.ids.general_combat_render_0, ui);

        Image::new(animate_by_pulse(
            &self.item_imgs.img_ids_or_not_found_img(ItemKey::Simple(
                "example_general_combat_right".to_string(),
            )),
            self.pulse,
        ))
        .wh(ART_SIZE)
        .middle_of(state.ids.general_combat_render_0)
        .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
        .set(state.ids.general_combat_render_1, ui);

        use PositionSpecifier::MidTopWithMarginOn;
        let skill_buttons = &[
            // Top Left skills
            //        5 1 6
            //        3 0 4
            //        8 2 7
            SkillIcon::Unlockable {
                skill: Skill::General(HealthIncrease),
                image: self.imgs.health_plus_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
                id: state.ids.skill_general_stat_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::General(EnergyIncrease),
                image: self.imgs.energy_plus_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
                id: state.ids.skill_general_stat_1,
            },
            // Top right skills
            SkillIcon::Unlockable {
                skill: Skill::UnlockGroup(Weapon(Sword)),
                image: self.imgs.unlock_sword_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
                id: state.ids.skill_general_tree_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::UnlockGroup(Weapon(Axe)),
                image: self.imgs.unlock_axe_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
                id: state.ids.skill_general_tree_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::UnlockGroup(Weapon(Hammer)),
                image: self.imgs.unlock_hammer_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
                id: state.ids.skill_general_tree_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::UnlockGroup(Weapon(Bow)),
                image: self.imgs.unlock_bow_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
                id: state.ids.skill_general_tree_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::UnlockGroup(Weapon(Staff)),
                image: self.imgs.unlock_staff_skill0,
                position: MidTopWithMarginOn(state.ids.skills_top_r[4], 3.0),
                id: state.ids.skill_general_tree_4,
            },
            SkillIcon::Unlockable {
                skill: Skill::UnlockGroup(Weapon(Sceptre)),
                image: self.imgs.unlock_sceptre_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[5], 3.0),
                id: state.ids.skill_general_tree_5,
            },
            // Bottom left skills
            SkillIcon::Descriptive {
                title: "hud-skill-dodge_title",
                desc: "hud-skill-dodge",
                image: self.imgs.skill_dodge_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
                id: state.ids.skill_general_roll_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Roll(Cost),
                image: self.imgs.utility_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
                id: state.ids.skill_general_roll_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Roll(Strength),
                image: self.imgs.utility_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
                id: state.ids.skill_general_roll_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Roll(Duration),
                image: self.imgs.utility_duration_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
                id: state.ids.skill_general_roll_3,
            },
            // Bottom right skills
            SkillIcon::Descriptive {
                title: "hud-skill-climbing_title",
                desc: "hud-skill-climbing",
                image: self.imgs.skill_climbing_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_r[0], 3.0),
                id: state.ids.skill_general_climb_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Climb(ClimbSkill::Cost),
                image: self.imgs.utility_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_r[1], 3.0),
                id: state.ids.skill_general_climb_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Climb(ClimbSkill::Speed),
                image: self.imgs.utility_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_r[2], 3.0),
                id: state.ids.skill_general_climb_2,
            },
            SkillIcon::Descriptive {
                title: "hud-skill-swim_title",
                desc: "hud-skill-swim",
                image: self.imgs.skill_swim_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_r[3], 3.0),
                id: state.ids.skill_general_swim_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Swim(SwimSkill::Speed),
                image: self.imgs.utility_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_r[4], 3.0),
                id: state.ids.skill_general_swim_1,
            },
        ];

        self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
        events
    }

    fn handle_sword_skills_window(
        &mut self,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
        ui: &mut UiCell,
        mut events: Vec<Event>,
    ) -> Vec<Event> {
        // Title text
        let tree_title = &self.localized_strings.get_msg("common-weapons-sword");

        Text::new(tree_title)
            .mid_top_with_margin_on(state.ids.content_align, 2.0)
            .font_id(self.fonts.cyri.conrod_id)
            .font_size(self.fonts.cyri.scale(34))
            .color(TEXT_COLOR)
            .set(state.ids.tree_title_txt, ui);

        // Sword
        Image::new(self.imgs.sword_bg)
            .wh([1000.0, 600.0])
            .mid_top_with_margin_on(state.ids.content_align, 80.0)
            .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
            .set(state.ids.sword_bg, ui);

        use PositionSpecifier::TopLeftWithMarginsOn;
        let skill_buttons = &[
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::BalancedFinisher),
                ability_id: "common.abilities.sword.balanced_finisher",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 489.0, 462.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::OffensiveCombo),
                ability_id: "common.abilities.sword.offensive_combo",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 389.0, 313.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::OffensiveFinisher),
                ability_id: "common.abilities.sword.offensive_finisher",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 489.0, 265.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::OffensiveAdvance),
                ability_id: "common.abilities.sword.offensive_advance",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 489.0, 361.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::CripplingCombo),
                ability_id: "common.abilities.sword.crippling_combo",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 289.0, 164.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::CripplingFinisher),
                ability_id: "common.abilities.sword.crippling_finisher",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 193.0, 164.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::CripplingStrike),
                ability_id: "common.abilities.sword.crippling_strike",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 97.0, 164.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::CripplingGouge),
                ability_id: "common.abilities.sword.crippling_gouge",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 2.0, 164.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::CleavingCombo),
                ability_id: "common.abilities.sword.cleaving_combo",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 289.0, 15.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::CleavingFinisher),
                ability_id: "common.abilities.sword.cleaving_finisher",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 193.0, 15.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::CleavingSpin),
                ability_id: "common.abilities.sword.cleaving_spin",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 97.0, 15.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::CleavingDive),
                ability_id: "common.abilities.sword.cleaving_dive",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 2.0, 15.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::DefensiveCombo),
                ability_id: "common.abilities.sword.defensive_combo",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 389.0, 611.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::DefensiveBulwark),
                ability_id: "common.abilities.sword.defensive_bulwark",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 489.0, 659.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::DefensiveRetreat),
                ability_id: "common.abilities.sword.defensive_retreat",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 489.0, 563.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::ParryingCombo),
                ability_id: "common.abilities.sword.parrying_combo",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 289.0, 760.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::ParryingParry),
                ability_id: "common.abilities.sword.parrying_parry",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 193.0, 760.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::ParryingRiposte),
                ability_id: "common.abilities.sword.parrying_riposte",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 97.0, 760.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::ParryingCounter),
                ability_id: "common.abilities.sword.parrying_counter",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 2.0, 760.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::HeavyCombo),
                ability_id: "common.abilities.sword.heavy_combo",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 289.0, 909.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::HeavyFinisher),
                ability_id: "common.abilities.sword.heavy_finisher",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 193.0, 909.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::HeavyPommelStrike),
                ability_id: "common.abilities.sword.heavy_pommelstrike",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 97.0, 909.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::HeavyFortitude),
                ability_id: "common.abilities.sword.heavy_fortitude",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 2.0, 909.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::MobilityCombo),
                ability_id: "common.abilities.sword.mobility_combo",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 289.0, 462.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::MobilityFeint),
                ability_id: "common.abilities.sword.mobility_feint",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 289.0, 313.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::MobilityAgility),
                ability_id: "common.abilities.sword.mobility_agility",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 289.0, 611.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::ReachingCombo),
                ability_id: "common.abilities.sword.reaching_combo",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 141.0, 462.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::ReachingCharge),
                ability_id: "common.abilities.sword.reaching_charge",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 2.0, 367.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::ReachingFlurry),
                ability_id: "common.abilities.sword.reaching_flurry",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 2.0, 462.0),
            },
            SkillIcon::Ability {
                skill: Skill::Sword(SwordSkill::ReachingSkewer),
                ability_id: "common.abilities.sword.reaching_skewer",
                position: TopLeftWithMarginsOn(state.ids.sword_bg, 2.0, 558.0),
            },
        ];

        state.update(|s| {
            s.ids
                .skills
                .resize(skill_buttons.len(), &mut ui.widget_id_generator())
        });
        state.update(|s| {
            s.ids
                .skill_lock_imgs
                .resize(skill_buttons.len(), &mut ui.widget_id_generator())
        });

        self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
        events
    }

    fn handle_hammer_skills_window(
        &mut self,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
        ui: &mut UiCell,
        mut events: Vec<Event>,
    ) -> Vec<Event> {
        // Title text
        let tree_title = &self.localized_strings.get_msg("common-weapons-hammer");

        Text::new(tree_title)
            .mid_top_with_margin_on(state.ids.content_align, 2.0)
            .font_id(self.fonts.cyri.conrod_id)
            .font_size(self.fonts.cyri.scale(34))
            .color(TEXT_COLOR)
            .set(state.ids.tree_title_txt, ui);

        // Number of skills per rectangle per weapon, start counting at 0
        // Maximum of 9 skills/8 indices
        let skills_top_l = 5;
        let skills_top_r = 5;
        let skills_bot_l = 6;
        let skills_bot_r = 0;

        self.setup_state_for_skill_icons(
            state,
            ui,
            skills_top_l,
            skills_top_r,
            skills_bot_l,
            skills_bot_r,
        );

        // Skill icons and buttons
        use skills::HammerSkill::*;
        // Hammer
        Image::new(animate_by_pulse(
            &self
                .item_imgs
                .img_ids_or_not_found_img(ItemKey::Simple("example_hammer".to_string())),
            self.pulse,
        ))
        .wh(ART_SIZE)
        .middle_of(state.ids.content_align)
        .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
        .set(state.ids.hammer_render, ui);
        use PositionSpecifier::MidTopWithMarginOn;
        let skill_buttons = &[
            // Top Left skills
            //        5 1 6
            //        3 0 4
            //        8 2 7
            SkillIcon::Descriptive {
                title: "hud-skill-hmr_single_strike_title",
                desc: "hud-skill-hmr_single_strike",
                image: self.imgs.twohhammer_m1,
                position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
                id: state.ids.skill_hammer_combo_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(SsKnockback),
                image: self.imgs.physical_knockback_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
                id: state.ids.skill_hammer_combo_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(SsDamage),
                image: self.imgs.physical_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
                id: state.ids.skill_hammer_combo_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(SsSpeed),
                image: self.imgs.physical_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
                id: state.ids.skill_hammer_combo_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(SsRegen),
                image: self.imgs.physical_energy_regen_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[4], 3.0),
                id: state.ids.skill_hammer_combo_4,
            },
            // Top right skills
            SkillIcon::Descriptive {
                title: "hud-skill-hmr_charged_melee_title",
                desc: "hud-skill-hmr_charged_melee",
                image: self.imgs.hammergolf,
                position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
                id: state.ids.skill_hammer_charged_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(CKnockback),
                image: self.imgs.physical_knockback_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
                id: state.ids.skill_hammer_charged_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(CDamage),
                image: self.imgs.physical_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
                id: state.ids.skill_hammer_charged_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(CDrain),
                image: self.imgs.physical_energy_drain_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
                id: state.ids.skill_hammer_charged_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(CSpeed),
                image: self.imgs.physical_amount_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[4], 3.0),
                id: state.ids.skill_hammer_charged_4,
            },
            // Bottom left skills
            SkillIcon::Unlockable {
                skill: Skill::Hammer(UnlockLeap),
                image: self.imgs.hammerleap,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
                id: state.ids.skill_hammer_leap_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(LDamage),
                image: self.imgs.physical_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
                id: state.ids.skill_hammer_leap_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(LKnockback),
                image: self.imgs.physical_knockback_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
                id: state.ids.skill_hammer_leap_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(LCost),
                image: self.imgs.physical_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
                id: state.ids.skill_hammer_leap_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(LDistance),
                image: self.imgs.physical_distance_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[4], 3.0),
                id: state.ids.skill_hammer_leap_4,
            },
            SkillIcon::Unlockable {
                skill: Skill::Hammer(LRange),
                image: self.imgs.physical_radius_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[5], 3.0),
                id: state.ids.skill_hammer_leap_5,
            },
        ];

        self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
        events
    }

    fn handle_axe_skills_window(
        &mut self,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
        ui: &mut UiCell,
        mut events: Vec<Event>,
    ) -> Vec<Event> {
        // Title text
        let tree_title = &self.localized_strings.get_msg("common-weapons-axe");

        Text::new(tree_title)
            .mid_top_with_margin_on(state.ids.content_align, 2.0)
            .font_id(self.fonts.cyri.conrod_id)
            .font_size(self.fonts.cyri.scale(34))
            .color(TEXT_COLOR)
            .set(state.ids.tree_title_txt, ui);

        // Number of skills per rectangle per weapon, start counting at 0
        // Maximum of 9 skills/8 indices
        let skills_top_l = 5;
        let skills_top_r = 6;
        let skills_bot_l = 5;
        let skills_bot_r = 0;

        self.setup_state_for_skill_icons(
            state,
            ui,
            skills_top_l,
            skills_top_r,
            skills_bot_l,
            skills_bot_r,
        );

        // Skill icons and buttons
        use skills::AxeSkill::*;
        // Axe
        Image::new(animate_by_pulse(
            &self
                .item_imgs
                .img_ids_or_not_found_img(ItemKey::Simple("example_axe".to_string())),
            self.pulse,
        ))
        .wh(ART_SIZE)
        .middle_of(state.ids.content_align)
        .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
        .set(state.ids.axe_render, ui);
        use PositionSpecifier::MidTopWithMarginOn;
        let skill_buttons = &[
            // Top Left skills
            //        5 1 6
            //        3 0 4
            //        8 2 7
            SkillIcon::Descriptive {
                title: "hud-skill-axe_double_strike_title",
                desc: "hud-skill-axe_double_strike",
                image: self.imgs.twohaxe_m1,
                position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
                id: state.ids.skill_axe_combo_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(DsCombo),
                image: self.imgs.physical_combo_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
                id: state.ids.skill_axe_combo_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(DsDamage),
                image: self.imgs.physical_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
                id: state.ids.skill_axe_combo_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(DsSpeed),
                image: self.imgs.physical_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
                id: state.ids.skill_axe_combo_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(DsRegen),
                image: self.imgs.physical_energy_regen_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[4], 3.0),
                id: state.ids.skill_axe_combo_4,
            },
            // Top right skills
            SkillIcon::Descriptive {
                title: "hud-skill-axe_spin_title",
                desc: "hud-skill-axe_spin",
                image: self.imgs.axespin,
                position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
                id: state.ids.skill_axe_spin_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(SInfinite),
                image: self.imgs.physical_infinite_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
                id: state.ids.skill_axe_spin_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(SDamage),
                image: self.imgs.physical_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
                id: state.ids.skill_axe_spin_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(SHelicopter),
                image: self.imgs.physical_helicopter_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
                id: state.ids.skill_axe_spin_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(SSpeed),
                image: self.imgs.physical_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[4], 3.0),
                id: state.ids.skill_axe_spin_4,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(SCost),
                image: self.imgs.physical_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[5], 3.0),
                id: state.ids.skill_axe_spin_5,
            },
            // Bottom left skills
            SkillIcon::Unlockable {
                skill: Skill::Axe(UnlockLeap),
                image: self.imgs.skill_axe_leap_slash,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
                id: state.ids.skill_axe_leap_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(LDamage),
                image: self.imgs.physical_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
                id: state.ids.skill_axe_leap_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(LKnockback),
                image: self.imgs.physical_knockback_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
                id: state.ids.skill_axe_leap_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(LCost),
                image: self.imgs.physical_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
                id: state.ids.skill_axe_leap_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Axe(LDistance),
                image: self.imgs.physical_distance_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[4], 3.0),
                id: state.ids.skill_axe_leap_4,
            },
        ];

        self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
        events
    }

    fn handle_sceptre_skills_window(
        &mut self,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
        ui: &mut UiCell,
        mut events: Vec<Event>,
    ) -> Vec<Event> {
        // Title text
        let tree_title = &self.localized_strings.get_msg("common-weapons-sceptre");

        Text::new(tree_title)
            .mid_top_with_margin_on(state.ids.content_align, 2.0)
            .font_id(self.fonts.cyri.conrod_id)
            .font_size(self.fonts.cyri.scale(34))
            .color(TEXT_COLOR)
            .set(state.ids.tree_title_txt, ui);

        // Number of skills per rectangle per weapon, start counting at 0
        // Maximum of 9 skills/8 indices
        let skills_top_l = 5;
        let skills_top_r = 5;
        let skills_bot_l = 5;
        let skills_bot_r = 0;

        self.setup_state_for_skill_icons(
            state,
            ui,
            skills_top_l,
            skills_top_r,
            skills_bot_l,
            skills_bot_r,
        );

        // Skill icons and buttons
        use skills::SceptreSkill::*;
        // Sceptre
        Image::new(animate_by_pulse(
            &self
                .item_imgs
                .img_ids_or_not_found_img(ItemKey::Simple("example_sceptre".to_string())),
            self.pulse,
        ))
        .wh(ART_SIZE)
        .middle_of(state.ids.content_align)
        .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
        .set(state.ids.sceptre_render, ui);
        use PositionSpecifier::MidTopWithMarginOn;
        let skill_buttons = &[
            // Top Left skills
            //        5 1 6
            //        3 0 4
            //        8 2 7
            SkillIcon::Descriptive {
                title: "hud-skill-sc_lifesteal_title",
                desc: "hud-skill-sc_lifesteal",
                image: self.imgs.skill_sceptre_lifesteal,
                position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
                id: state.ids.skill_sceptre_lifesteal_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(LDamage),
                image: self.imgs.magic_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
                id: state.ids.skill_sceptre_lifesteal_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(LRange),
                image: self.imgs.magic_distance_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
                id: state.ids.skill_sceptre_lifesteal_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(LLifesteal),
                image: self.imgs.magic_lifesteal_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
                id: state.ids.skill_sceptre_lifesteal_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(LRegen),
                image: self.imgs.magic_energy_regen_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[4], 3.0),
                id: state.ids.skill_sceptre_lifesteal_4,
            },
            // Top right skills
            SkillIcon::Descriptive {
                title: "hud-skill-sc_heal_title",
                desc: "hud-skill-sc_heal",
                image: self.imgs.skill_sceptre_heal,
                position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
                id: state.ids.skill_sceptre_heal_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(HHeal),
                image: self.imgs.heal_heal_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
                id: state.ids.skill_sceptre_heal_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(HDuration),
                image: self.imgs.heal_duration_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
                id: state.ids.skill_sceptre_heal_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(HRange),
                image: self.imgs.heal_radius_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
                id: state.ids.skill_sceptre_heal_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(HCost),
                image: self.imgs.heal_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[4], 3.0),
                id: state.ids.skill_sceptre_heal_4,
            },
            // Bottom left skills
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(UnlockAura),
                image: self.imgs.skill_sceptre_aura,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
                id: state.ids.skill_sceptre_aura_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(AStrength),
                image: self.imgs.buff_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
                id: state.ids.skill_sceptre_aura_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(ADuration),
                image: self.imgs.buff_duration_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
                id: state.ids.skill_sceptre_aura_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(ARange),
                image: self.imgs.buff_radius_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
                id: state.ids.skill_sceptre_aura_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Sceptre(ACost),
                image: self.imgs.buff_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[4], 3.0),
                id: state.ids.skill_sceptre_aura_4,
            },
        ];

        self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
        events
    }

    fn handle_bow_skills_window(
        &mut self,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
        ui: &mut UiCell,
        mut events: Vec<Event>,
    ) -> Vec<Event> {
        // Title text
        let tree_title = &self.localized_strings.get_msg("common-weapons-bow");

        Text::new(tree_title)
            .mid_top_with_margin_on(state.ids.content_align, 2.0)
            .font_id(self.fonts.cyri.conrod_id)
            .font_size(self.fonts.cyri.scale(34))
            .color(TEXT_COLOR)
            .set(state.ids.tree_title_txt, ui);

        // Number of skills per rectangle per weapon, start counting at 0
        // Maximum of 9 skills/8 indices
        let skills_top_l = 6;
        let skills_top_r = 4;
        let skills_bot_l = 5;
        let skills_bot_r = 1;

        self.setup_state_for_skill_icons(
            state,
            ui,
            skills_top_l,
            skills_top_r,
            skills_bot_l,
            skills_bot_r,
        );

        // Skill icons and buttons
        use skills::BowSkill::*;
        // Bow
        Image::new(animate_by_pulse(
            &self
                .item_imgs
                .img_ids_or_not_found_img(ItemKey::Simple("example_bow".to_string())),
            self.pulse,
        ))
        .wh(ART_SIZE)
        .middle_of(state.ids.content_align)
        .set(state.ids.bow_render, ui);
        use PositionSpecifier::MidTopWithMarginOn;
        let skill_buttons = &[
            // Top Left skills
            //        5 1 6
            //        3 0 4
            //        8 2 7
            SkillIcon::Descriptive {
                title: "hud-skill-bow_charged_title",
                desc: "hud-skill-bow_charged",
                image: self.imgs.bow_m1,
                position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
                id: state.ids.skill_bow_charged_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(CDamage),
                image: self.imgs.physical_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
                id: state.ids.skill_bow_charged_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(CRegen),
                image: self.imgs.physical_energy_regen_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
                id: state.ids.skill_bow_charged_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(CKnockback),
                image: self.imgs.physical_knockback_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
                id: state.ids.skill_bow_charged_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(CSpeed),
                image: self.imgs.physical_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[4], 3.0),
                id: state.ids.skill_bow_charged_4,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(CMove),
                image: self.imgs.physical_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[5], 3.0),
                id: state.ids.skill_bow_charged_5,
            },
            // Top right skills
            SkillIcon::Descriptive {
                title: "hud-skill-bow_repeater_title",
                desc: "hud-skill-bow_repeater",
                image: self.imgs.bow_m2,
                position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
                id: state.ids.skill_bow_repeater_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(RDamage),
                image: self.imgs.physical_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
                id: state.ids.skill_bow_repeater_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(RCost),
                image: self.imgs.physical_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
                id: state.ids.skill_bow_repeater_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(RSpeed),
                image: self.imgs.physical_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
                id: state.ids.skill_bow_repeater_3,
            },
            // Bottom left skills
            SkillIcon::Unlockable {
                skill: Skill::Bow(UnlockShotgun),
                image: self.imgs.skill_bow_jump_burst,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
                id: state.ids.skill_bow_shotgun_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(SDamage),
                image: self.imgs.physical_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
                id: state.ids.skill_bow_shotgun_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(SCost),
                image: self.imgs.physical_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
                id: state.ids.skill_bow_shotgun_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(SArrows),
                image: self.imgs.physical_amount_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
                id: state.ids.skill_bow_shotgun_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Bow(SSpread),
                image: self.imgs.physical_explosion_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[4], 3.0),
                id: state.ids.skill_bow_shotgun_4,
            },
            // Bottom right skills
            SkillIcon::Unlockable {
                skill: Skill::Bow(ProjSpeed),
                image: self.imgs.physical_projectile_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_r[0], 3.0),
                id: state.ids.skill_bow_passive_0,
            },
        ];

        self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
        events
    }

    fn handle_staff_skills_window(
        &mut self,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
        ui: &mut UiCell,
        mut events: Vec<Event>,
    ) -> Vec<Event> {
        // Title text
        let tree_title = &self.localized_strings.get_msg("common-weapons-staff");

        Text::new(tree_title)
            .mid_top_with_margin_on(state.ids.content_align, 2.0)
            .font_id(self.fonts.cyri.conrod_id)
            .font_size(self.fonts.cyri.scale(34))
            .color(TEXT_COLOR)
            .set(state.ids.tree_title_txt, ui);

        // Number of skills per rectangle per weapon, start counting at 0
        // Maximum of 9 skills/8 indices
        let skills_top_l = 4;
        let skills_top_r = 5;
        let skills_bot_l = 5;
        let skills_bot_r = 0;

        self.setup_state_for_skill_icons(
            state,
            ui,
            skills_top_l,
            skills_top_r,
            skills_bot_l,
            skills_bot_r,
        );

        // Skill icons and buttons
        use skills::StaffSkill::*;
        // Staff
        Image::new(animate_by_pulse(
            &self
                .item_imgs
                .img_ids_or_not_found_img(ItemKey::Simple("example_staff_fire".to_string())),
            self.pulse,
        ))
        .wh(ART_SIZE)
        .middle_of(state.ids.content_align)
        .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
        .set(state.ids.staff_render, ui);

        use PositionSpecifier::MidTopWithMarginOn;
        let skill_buttons = &[
            // Top Left skills
            //        5 1 6
            //        3 0 4
            //        8 2 7
            SkillIcon::Descriptive {
                title: "hud-skill-st_fireball_title",
                desc: "hud-skill-st_fireball",
                image: self.imgs.fireball,
                position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
                id: state.ids.skill_staff_basic_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(BDamage),
                image: self.imgs.magic_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
                id: state.ids.skill_staff_basic_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(BRegen),
                image: self.imgs.magic_energy_regen_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
                id: state.ids.skill_staff_basic_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(BRadius),
                image: self.imgs.magic_radius_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
                id: state.ids.skill_staff_basic_3,
            },
            // Top right skills
            SkillIcon::Descriptive {
                title: "hud-skill-st_flamethrower_title",
                desc: "hud-skill-st_flamethrower",
                image: self.imgs.flamethrower,
                position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
                id: state.ids.skill_staff_beam_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(FDamage),
                image: self.imgs.magic_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
                id: state.ids.skill_staff_beam_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(FDrain),
                image: self.imgs.magic_energy_drain_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
                id: state.ids.skill_staff_beam_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(FRange),
                image: self.imgs.magic_radius_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
                id: state.ids.skill_staff_beam_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(FVelocity),
                image: self.imgs.magic_projectile_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_r[4], 3.0),
                id: state.ids.skill_staff_beam_4,
            },
            // Bottom left skills
            SkillIcon::Unlockable {
                skill: Skill::Staff(UnlockShockwave),
                image: self.imgs.fire_aoe,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
                id: state.ids.skill_staff_shockwave_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(SDamage),
                image: self.imgs.magic_damage_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
                id: state.ids.skill_staff_shockwave_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(SKnockback),
                image: self.imgs.magic_knockback_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
                id: state.ids.skill_staff_shockwave_2,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(SCost),
                image: self.imgs.magic_cost_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
                id: state.ids.skill_staff_shockwave_3,
            },
            SkillIcon::Unlockable {
                skill: Skill::Staff(SRange),
                image: self.imgs.magic_radius_skill,
                position: MidTopWithMarginOn(state.ids.skills_bot_l[4], 3.0),
                id: state.ids.skill_staff_shockwave_4,
            },
        ];

        self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
        events
    }

    fn handle_mining_skills_window(
        &mut self,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
        ui: &mut UiCell,
        mut events: Vec<Event>,
    ) -> Vec<Event> {
        // Title text
        let tree_title = &self.localized_strings.get_msg("common-tool-mining");

        Text::new(tree_title)
            .mid_top_with_margin_on(state.ids.content_align, 2.0)
            .font_id(self.fonts.cyri.conrod_id)
            .font_size(self.fonts.cyri.scale(34))
            .color(TEXT_COLOR)
            .set(state.ids.tree_title_txt, ui);

        // Number of skills per rectangle per weapon, start counting at 0
        // Maximum of 9 skills/8 indices
        let skills_top_l = 4;
        let skills_top_r = 0;
        let skills_bot_l = 0;
        let skills_bot_r = 0;

        self.setup_state_for_skill_icons(
            state,
            ui,
            skills_top_l,
            skills_top_r,
            skills_bot_l,
            skills_bot_r,
        );

        // Skill icons and buttons
        use skills::MiningSkill::*;
        // Mining
        Image::new(animate_by_pulse(
            &self
                .item_imgs
                .img_ids_or_not_found_img(ItemKey::Simple("example_pick".to_string())),
            self.pulse,
        ))
        .wh(ART_SIZE)
        .middle_of(state.ids.content_align)
        .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
        .set(state.ids.pick_render, ui);

        use PositionSpecifier::MidTopWithMarginOn;
        let skill_buttons = &[
            // Top Left skills
            //        5 1 6
            //        3 0 4
            //        8 2 7
            SkillIcon::Descriptive {
                title: "hud-skill-pick_strike_title",
                desc: "hud-skill-pick_strike",
                image: self.imgs.pickaxe,
                position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
                id: state.ids.skill_pick_m1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Pick(Speed),
                image: self.imgs.pickaxe_speed_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
                id: state.ids.skill_pick_m1_0,
            },
            SkillIcon::Unlockable {
                skill: Skill::Pick(OreGain),
                image: self.imgs.pickaxe_oregain_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
                id: state.ids.skill_pick_m1_1,
            },
            SkillIcon::Unlockable {
                skill: Skill::Pick(GemGain),
                image: self.imgs.pickaxe_gemgain_skill,
                position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
                id: state.ids.skill_pick_m1_2,
            },
        ];

        self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
        events
    }

    fn handle_skill_buttons(
        &mut self,
        icons: &[SkillIcon],
        ui: &mut UiCell,
        events: &mut Vec<Event>,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
    ) {
        for (i, icon) in icons.iter().enumerate() {
            match icon {
                SkillIcon::Descriptive {
                    title,
                    desc,
                    image,
                    position,
                    id,
                } => {
                    // TODO: shouldn't this be a `Image::new`?
                    Button::image(*image)
                        .w_h(74.0, 74.0)
                        .position(*position)
                        .with_tooltip(
                            self.tooltip_manager,
                            &self.localized_strings.get_msg(title),
                            &self.localized_strings.get_msg(desc),
                            diary_tooltip,
                            TEXT_COLOR,
                        )
                        .set(*id, ui);
                },
                SkillIcon::Unlockable {
                    skill,
                    image,
                    position,
                    id,
                } => self.create_unlock_skill_button(
                    *skill,
                    *image,
                    *position,
                    *id,
                    ui,
                    events,
                    diary_tooltip,
                ),
                SkillIcon::Ability {
                    skill,
                    ability_id,
                    position,
                } => self.create_unlock_ability_button(
                    *skill,
                    ability_id,
                    *position,
                    i,
                    ui,
                    events,
                    diary_tooltip,
                    state,
                ),
            }
        }
    }

    fn setup_state_for_skill_icons(
        &mut self,
        state: &mut State<DiaryState>,
        ui: &mut UiCell,
        skills_top_l: usize,
        skills_top_r: usize,
        skills_bot_l: usize,
        skills_bot_r: usize,
    ) {
        // Update widget id array len
        state.update(|s| {
            s.ids
                .skills_top_l
                .resize(skills_top_l, &mut ui.widget_id_generator())
        });
        state.update(|s| {
            s.ids
                .skills_top_r
                .resize(skills_top_r, &mut ui.widget_id_generator())
        });
        state.update(|s| {
            s.ids
                .skills_bot_l
                .resize(skills_bot_l, &mut ui.widget_id_generator())
        });
        state.update(|s| {
            s.ids
                .skills_bot_r
                .resize(skills_bot_r, &mut ui.widget_id_generator())
        });

        // Create Background Images to place skill icons on them later
        // Create central skill first, others around it:
        //
        //        5 1 6
        //        3 0 4
        //        8 2 7
        //
        //
        let offset_0 = 22.0;
        let offset_1 = -122.0;
        let offset_2 = offset_1 - -20.0;

        let skill_pos = |idx, align, central_skill| {
            use PositionSpecifier::*;
            match idx {
                // Central skill
                0 => MiddleOf(align),
                // 12:00
                1 => UpFrom(central_skill, offset_0),
                // 6:00
                2 => DownFrom(central_skill, offset_0),
                // 3:00
                3 => LeftFrom(central_skill, offset_0),
                // 9:00
                4 => RightFrom(central_skill, offset_0),
                // 10:30
                5 => TopLeftWithMarginsOn(central_skill, offset_1, offset_2),
                // 1:30
                6 => TopRightWithMarginsOn(central_skill, offset_1, offset_2),
                // 4:30
                7 => BottomLeftWithMarginsOn(central_skill, offset_1, offset_2),
                // 7:30
                8 => BottomRightWithMarginsOn(central_skill, offset_1, offset_2),
                buttons => {
                    panic!("{} > 8 position number", buttons);
                },
            }
        };

        // TOP-LEFT Skills
        //
        // TODO: Why this uses while loop on field of struct and not just
        // `for i in 0..skils_top_l`?
        while self.created_btns_top_l < skills_top_l {
            let pos = skill_pos(
                self.created_btns_top_l,
                state.ids.skills_top_l_align,
                state.ids.skills_top_l[0],
            );
            Button::image(self.imgs.wpn_icon_border_skills)
                .w_h(80.0, 100.0)
                .position(pos)
                .set(state.ids.skills_top_l[self.created_btns_top_l], ui);
            self.created_btns_top_l += 1;
        }
        // TOP-RIGHT Skills
        while self.created_btns_top_r < skills_top_r {
            let pos = skill_pos(
                self.created_btns_top_r,
                state.ids.skills_top_r_align,
                state.ids.skills_top_r[0],
            );
            Button::image(self.imgs.wpn_icon_border_skills)
                .w_h(80.0, 100.0)
                .position(pos)
                .set(state.ids.skills_top_r[self.created_btns_top_r], ui);
            self.created_btns_top_r += 1;
        }
        // BOTTOM-LEFT Skills
        while self.created_btns_bot_l < skills_bot_l {
            let pos = skill_pos(
                self.created_btns_bot_l,
                state.ids.skills_bot_l_align,
                state.ids.skills_bot_l[0],
            );
            Button::image(self.imgs.wpn_icon_border_skills)
                .w_h(80.0, 100.0)
                .position(pos)
                .set(state.ids.skills_bot_l[self.created_btns_bot_l], ui);
            self.created_btns_bot_l += 1;
        }
        // BOTTOM-RIGHT Skills
        while self.created_btns_bot_r < skills_bot_r {
            let pos = skill_pos(
                self.created_btns_bot_r,
                state.ids.skills_bot_r_align,
                state.ids.skills_bot_r[0],
            );
            Button::image(self.imgs.wpn_icon_border_skills)
                .w_h(80.0, 100.0)
                .position(pos)
                .set(state.ids.skills_bot_r[self.created_btns_bot_r], ui);
            self.created_btns_bot_r += 1;
        }
    }

    fn create_unlock_skill_button(
        &mut self,
        skill: Skill,
        skill_image: image::Id,
        position: PositionSpecifier,
        widget_id: widget::Id,
        ui: &mut UiCell,
        events: &mut Vec<Event>,
        diary_tooltip: &Tooltip,
    ) {
        let label = if self.skill_set.prerequisites_met(skill) {
            let current = self.skill_set.skill_level(skill).unwrap_or(0);
            let max = skill.max_level();
            format!("{}/{}", current, max)
        } else {
            "".to_owned()
        };

        let label_color = if self.skill_set.is_at_max_level(skill) {
            TEXT_COLOR
        } else if self.skill_set.sufficient_skill_points(skill) {
            HP_COLOR
        } else {
            CRITICAL_HP_COLOR
        };

        let image_color = if self.skill_set.prerequisites_met(skill) {
            TEXT_COLOR
        } else {
            Color::Rgba(0.41, 0.41, 0.41, 0.7)
        };

        let skill_strings = skill_strings(skill);
        let (title, description) =
            skill_strings.localize(self.localized_strings, self.skill_set, skill);

        let button = Button::image(skill_image)
            .w_h(74.0, 74.0)
            .position(position)
            .label(&label)
            .label_y(conrod_core::position::Relative::Scalar(-47.0))
            .label_x(conrod_core::position::Relative::Scalar(0.0))
            .label_color(label_color)
            .label_font_size(self.fonts.cyri.scale(15))
            .label_font_id(self.fonts.cyri.conrod_id)
            .image_color(image_color)
            .with_tooltip(
                self.tooltip_manager,
                &title,
                &description,
                diary_tooltip,
                TEXT_COLOR,
            )
            .set(widget_id, ui);

        if button.was_clicked() {
            events.push(Event::UnlockSkill(skill));
        };
    }

    fn create_unlock_ability_button(
        &mut self,
        skill: Skill,
        ability_id: &str,
        position: PositionSpecifier,
        widget_index: usize,
        ui: &mut UiCell,
        events: &mut Vec<Event>,
        diary_tooltip: &Tooltip,
        state: &mut State<DiaryState>,
    ) {
        let locked = !self.skill_set.prerequisites_met(skill);
        let owned = self.skill_set.has_skill(skill);
        let image_color = if owned {
            TEXT_COLOR
        } else {
            Color::Rgba(0.41, 0.41, 0.41, 0.7)
        };

        let (title, description) = util::ability_description(ability_id, self.localized_strings);

        let sp_cost = sp(self.localized_strings, self.skill_set, skill);

        let description = format!("{description}\n{sp_cost}");

        let button = Button::image(util::ability_image(self.imgs, ability_id))
            .w_h(76.0, 76.0)
            .position(position)
            .image_color(image_color)
            .with_tooltip(
                self.tooltip_manager,
                &title,
                &description,
                diary_tooltip,
                TEXT_COLOR,
            )
            .set(state.ids.skills[widget_index], ui);

        // Lock Image
        if locked {
            Image::new(self.imgs.lock)
                .w_h(76.0, 76.0)
                .middle_of(state.ids.skills[widget_index])
                .graphics_for(state.ids.skills[widget_index])
                .color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.8)))
                .set(state.ids.skill_lock_imgs[widget_index], ui);
        }

        if button.was_clicked() {
            events.push(Event::UnlockSkill(skill));
        };
    }
}

/// Returns skill info as a tuple of title and description.
///
/// If you want to get localized version, use `SkillStrings::localize` method
fn skill_strings(skill: Skill) -> SkillStrings<'static> {
    match skill {
        // general tree
        Skill::General(s) => general_skill_strings(s),
        Skill::UnlockGroup(s) => unlock_skill_strings(s),
        // weapon trees
        Skill::Axe(s) => axe_skill_strings(s),
        Skill::Hammer(s) => hammer_skill_strings(s),
        Skill::Bow(s) => bow_skill_strings(s),
        Skill::Staff(s) => staff_skill_strings(s),
        Skill::Sceptre(s) => sceptre_skill_strings(s),
        // movement trees
        Skill::Roll(s) => roll_skill_strings(s),
        Skill::Climb(s) => climb_skill_strings(s),
        Skill::Swim(s) => swim_skill_strings(s),
        // mining
        Skill::Pick(s) => mining_skill_strings(s),
        _ => SkillStrings::plain("", ""),
    }
}

fn general_skill_strings(skill: GeneralSkill) -> SkillStrings<'static> {
    match skill {
        GeneralSkill::HealthIncrease => SkillStrings::with_const(
            "hud-skill-inc_health_title",
            "hud-skill-inc_health",
            u32::from(HP_PER_LEVEL),
        ),
        GeneralSkill::EnergyIncrease => SkillStrings::with_const(
            "hud-skill-inc_energy_title",
            "hud-skill-inc_energy",
            u32::from(ENERGY_PER_LEVEL),
        ),
    }
}

fn unlock_skill_strings(group: SkillGroupKind) -> SkillStrings<'static> {
    match group {
        SkillGroupKind::Weapon(ToolKind::Sword) => {
            SkillStrings::plain("hud-skill-unlck_sword_title", "hud-skill-unlck_sword")
        },
        SkillGroupKind::Weapon(ToolKind::Axe) => {
            SkillStrings::plain("hud-skill-unlck_axe_title", "hud-skill-unlck_axe")
        },
        SkillGroupKind::Weapon(ToolKind::Hammer) => {
            SkillStrings::plain("hud-skill-unlck_hammer_title", "hud-skill-unlck_hammer")
        },
        SkillGroupKind::Weapon(ToolKind::Bow) => {
            SkillStrings::plain("hud-skill-unlck_bow_title", "hud-skill-unlck_bow")
        },
        SkillGroupKind::Weapon(ToolKind::Staff) => {
            SkillStrings::plain("hud-skill-unlck_staff_title", "hud-skill-unlck_staff")
        },
        SkillGroupKind::Weapon(ToolKind::Sceptre) => {
            SkillStrings::plain("hud-skill-unlck_sceptre_title", "hud-skill-unlck_sceptre")
        },
        SkillGroupKind::General
        | SkillGroupKind::Weapon(
            ToolKind::Dagger
            | ToolKind::Shield
            | ToolKind::Spear
            | ToolKind::Blowgun
            | ToolKind::Debug
            | ToolKind::Farming
            | ToolKind::Instrument
            | ToolKind::Pick
            | ToolKind::Natural
            | ToolKind::Empty,
        ) => {
            tracing::warn!("Requesting title for unlocking unexpected skill group");
            SkillStrings::Empty
        },
    }
}

fn axe_skill_strings(skill: AxeSkill) -> SkillStrings<'static> {
    let modifiers = SKILL_MODIFIERS.axe_tree;
    match skill {
        // Double strike upgrades
        AxeSkill::DsCombo => SkillStrings::plain(
            "hud-skill-axe_double_strike_combo_title",
            "hud-skill-axe_double_strike_combo",
        ),
        AxeSkill::DsDamage => SkillStrings::plain(
            "hud-skill-axe_double_strike_damage_title",
            "hud-skill-axe_double_strike_damage",
        ),
        AxeSkill::DsSpeed => SkillStrings::plain(
            "hud-skill-axe_double_strike_speed_title",
            "hud-skill-axe_double_strike_speed",
        ),
        AxeSkill::DsRegen => SkillStrings::plain(
            "hud-skill-axe_double_strike_regen_title",
            "hud-skill-axe_double_strike_regen",
        ),
        // Spin upgrades
        AxeSkill::SInfinite => SkillStrings::plain(
            "hud-skill-axe_infinite_axe_spin_title",
            "hud-skill-axe_infinite_axe_spin",
        ),
        AxeSkill::SHelicopter => SkillStrings::plain(
            "hud-skill-axe_spin_helicopter_title",
            "hud-skill-axe_spin_helicopter",
        ),
        AxeSkill::SDamage => SkillStrings::with_mult(
            "hud-skill-axe_spin_damage_title",
            "hud-skill-axe_spin_damage",
            modifiers.spin.base_damage,
        ),
        AxeSkill::SSpeed => SkillStrings::with_mult(
            "hud-skill-axe_spin_speed_title",
            "hud-skill-axe_spin_speed",
            modifiers.spin.swing_duration,
        ),
        AxeSkill::SCost => SkillStrings::with_mult(
            "hud-skill-axe_spin_cost_title",
            "hud-skill-axe_spin_cost",
            modifiers.spin.energy_cost,
        ),
        // Leap upgrades
        AxeSkill::UnlockLeap => SkillStrings::plain(
            "hud-skill-axe_unlock_leap_title",
            "hud-skill-axe_unlock_leap",
        ),
        AxeSkill::LDamage => SkillStrings::with_mult(
            "hud-skill-axe_leap_damage_title",
            "hud-skill-axe_leap_damage",
            modifiers.leap.base_damage,
        ),
        AxeSkill::LKnockback => SkillStrings::with_mult(
            "hud-skill-axe_leap_knockback_title",
            "hud-skill-axe_leap_knockback",
            modifiers.leap.knockback,
        ),
        AxeSkill::LCost => SkillStrings::with_mult(
            "hud-skill-axe_leap_cost_title",
            "hud-skill-axe_leap_cost",
            modifiers.leap.energy_cost,
        ),
        AxeSkill::LDistance => SkillStrings::with_mult(
            "hud-skill-axe_leap_distance_title",
            "hud-skill-axe_leap_distance",
            modifiers.leap.leap_strength,
        ),
    }
}

fn hammer_skill_strings(skill: HammerSkill) -> SkillStrings<'static> {
    let modifiers = SKILL_MODIFIERS.hammer_tree;
    // Single strike upgrades
    match skill {
        HammerSkill::SsKnockback => SkillStrings::with_mult(
            "hud-skill-hmr_single_strike_knockback_title",
            "hud-skill-hmr_single_strike_knockback",
            modifiers.single_strike.knockback,
        ),
        HammerSkill::SsDamage => SkillStrings::plain(
            "hud-skill-hmr_single_strike_damage_title",
            "hud-skill-hmr_single_strike_damage",
        ),
        HammerSkill::SsSpeed => SkillStrings::plain(
            "hud-skill-hmr_single_strike_speed_title",
            "hud-skill-hmr_single_strike_speed",
        ),
        HammerSkill::SsRegen => SkillStrings::plain(
            "hud-skill-hmr_single_strike_regen_title",
            "hud-skill-hmr_single_strike_regen",
        ),
        // Charged melee upgrades
        HammerSkill::CDamage => SkillStrings::with_mult(
            "hud-skill-hmr_charged_melee_damage_title",
            "hud-skill-hmr_charged_melee_damage",
            modifiers.charged.scaled_damage,
        ),
        HammerSkill::CKnockback => SkillStrings::with_mult(
            "hud-skill-hmr_charged_melee_knockback_title",
            "hud-skill-hmr_charged_melee_knockback",
            modifiers.charged.scaled_knockback,
        ),
        HammerSkill::CDrain => SkillStrings::with_mult(
            "hud-skill-hmr_charged_melee_nrg_drain_title",
            "hud-skill-hmr_charged_melee_nrg_drain",
            modifiers.charged.energy_drain,
        ),
        HammerSkill::CSpeed => SkillStrings::with_mult(
            "hud-skill-hmr_charged_rate_title",
            "hud-skill-hmr_charged_rate",
            modifiers.charged.charge_rate,
        ),
        // Leap upgrades
        HammerSkill::UnlockLeap => SkillStrings::plain(
            "hud-skill-hmr_unlock_leap_title",
            "hud-skill-hmr_unlock_leap",
        ),
        HammerSkill::LDamage => SkillStrings::with_mult(
            "hud-skill-hmr_leap_damage_title",
            "hud-skill-hmr_leap_damage",
            modifiers.leap.base_damage,
        ),
        HammerSkill::LCost => SkillStrings::with_mult(
            "hud-skill-hmr_leap_cost_title",
            "hud-skill-hmr_leap_cost",
            modifiers.leap.energy_cost,
        ),
        HammerSkill::LDistance => SkillStrings::with_mult(
            "hud-skill-hmr_leap_distance_title",
            "hud-skill-hmr_leap_distance",
            modifiers.leap.leap_strength,
        ),
        HammerSkill::LKnockback => SkillStrings::with_mult(
            "hud-skill-hmr_leap_knockback_title",
            "hud-skill-hmr_leap_knockback",
            modifiers.leap.knockback,
        ),
        HammerSkill::LRange => SkillStrings::with_const_float(
            "hud-skill-hmr_leap_radius_title",
            "hud-skill-hmr_leap_radius",
            modifiers.leap.range,
        ),
    }
}

fn bow_skill_strings(skill: BowSkill) -> SkillStrings<'static> {
    let modifiers = SKILL_MODIFIERS.bow_tree;
    match skill {
        // Passives
        BowSkill::ProjSpeed => SkillStrings::with_mult(
            "hud-skill-bow_projectile_speed_title",
            "hud-skill-bow_projectile_speed",
            modifiers.universal.projectile_speed,
        ),
        // Charged upgrades
        BowSkill::CDamage => SkillStrings::with_mult(
            "hud-skill-bow_charged_damage_title",
            "hud-skill-bow_charged_damage",
            modifiers.charged.damage_scaling,
        ),
        BowSkill::CRegen => SkillStrings::with_mult(
            "hud-skill-bow_charged_energy_regen_title",
            "hud-skill-bow_charged_energy_regen",
            modifiers.charged.regen_scaling,
        ),
        BowSkill::CKnockback => SkillStrings::with_mult(
            "hud-skill-bow_charged_knockback_title",
            "hud-skill-bow_charged_knockback",
            modifiers.charged.knockback_scaling,
        ),
        BowSkill::CSpeed => SkillStrings::with_mult(
            "hud-skill-bow_charged_speed_title",
            "hud-skill-bow_charged_speed",
            modifiers.charged.charge_rate,
        ),
        BowSkill::CMove => SkillStrings::with_mult(
            "hud-skill-bow_charged_move_title",
            "hud-skill-bow_charged_move",
            modifiers.charged.move_speed,
        ),
        // Repeater upgrades
        BowSkill::RDamage => SkillStrings::with_mult(
            "hud-skill-bow_repeater_damage_title",
            "hud-skill-bow_repeater_damage",
            modifiers.repeater.power,
        ),
        BowSkill::RCost => SkillStrings::with_mult(
            "hud-skill-bow_repeater_cost_title",
            "hud-skill-bow_repeater_cost",
            modifiers.repeater.energy_cost,
        ),
        BowSkill::RSpeed => SkillStrings::with_mult(
            "hud-skill-bow_repeater_speed_title",
            "hud-skill-bow_repeater_speed",
            modifiers.repeater.max_speed,
        ),
        // Shotgun upgrades
        BowSkill::UnlockShotgun => SkillStrings::plain(
            "hud-skill-bow_shotgun_unlock_title",
            "hud-skill-bow_shotgun_unlock",
        ),
        BowSkill::SDamage => SkillStrings::with_mult(
            "hud-skill-bow_shotgun_damage_title",
            "hud-skill-bow_shotgun_damage",
            modifiers.shotgun.power,
        ),
        BowSkill::SCost => SkillStrings::with_mult(
            "hud-skill-bow_shotgun_cost_title",
            "hud-skill-bow_shotgun_cost",
            modifiers.shotgun.energy_cost,
        ),
        BowSkill::SArrows => SkillStrings::with_const(
            "hud-skill-bow_shotgun_arrow_count_title",
            "hud-skill-bow_shotgun_arrow_count",
            modifiers.shotgun.num_projectiles,
        ),
        BowSkill::SSpread => SkillStrings::with_mult(
            "hud-skill-bow_shotgun_spread_title",
            "hud-skill-bow_shotgun_spread",
            modifiers.shotgun.spread,
        ),
    }
}

fn staff_skill_strings(skill: StaffSkill) -> SkillStrings<'static> {
    let modifiers = SKILL_MODIFIERS.staff_tree;
    match skill {
        // Basic ranged upgrades
        StaffSkill::BDamage => SkillStrings::with_mult(
            "hud-skill-st_damage_title",
            "hud-skill-st_damage",
            modifiers.fireball.power,
        ),
        StaffSkill::BRegen => SkillStrings::with_mult(
            "hud-skill-st_energy_regen_title",
            "hud-skill-st_energy_regen",
            modifiers.fireball.regen,
        ),
        StaffSkill::BRadius => SkillStrings::with_mult(
            "hud-skill-st_explosion_radius_title",
            "hud-skill-st_explosion_radius",
            modifiers.fireball.range,
        ),
        // Flamethrower upgrades
        StaffSkill::FDamage => SkillStrings::with_mult(
            "hud-skill-st_flamethrower_damage_title",
            "hud-skill-st_flamethrower_damage",
            modifiers.flamethrower.damage,
        ),
        StaffSkill::FRange => SkillStrings::with_mult(
            "hud-skill-st_flamethrower_range_title",
            "hud-skill-st_flamethrower_range",
            modifiers.flamethrower.range,
        ),
        StaffSkill::FDrain => SkillStrings::with_mult(
            "hud-skill-st_energy_drain_title",
            "hud-skill-st_energy_drain",
            modifiers.flamethrower.energy_drain,
        ),
        StaffSkill::FVelocity => SkillStrings::with_mult(
            "hud-skill-st_flame_velocity_title",
            "hud-skill-st_flame_velocity",
            modifiers.flamethrower.velocity,
        ),
        // Shockwave upgrades
        StaffSkill::UnlockShockwave => SkillStrings::plain(
            "hud-skill-st_shockwave_unlock_title",
            "hud-skill-st_shockwave_unlock",
        ),
        StaffSkill::SDamage => SkillStrings::with_mult(
            "hud-skill-st_shockwave_damage_title",
            "hud-skill-st_shockwave_damage",
            modifiers.shockwave.damage,
        ),
        StaffSkill::SKnockback => SkillStrings::with_mult(
            "hud-skill-st_shockwave_knockback_title",
            "hud-skill-st_shockwave_knockback",
            modifiers.shockwave.knockback,
        ),
        StaffSkill::SRange => SkillStrings::with_mult(
            "hud-skill-st_shockwave_range_title",
            "hud-skill-st_shockwave_range",
            modifiers.shockwave.duration,
        ),
        StaffSkill::SCost => SkillStrings::with_mult(
            "hud-skill-st_shockwave_cost_title",
            "hud-skill-st_shockwave_cost",
            modifiers.shockwave.energy_cost,
        ),
    }
}

fn sceptre_skill_strings(skill: SceptreSkill) -> SkillStrings<'static> {
    let modifiers = SKILL_MODIFIERS.sceptre_tree;
    match skill {
        // Lifesteal beam upgrades
        SceptreSkill::LDamage => SkillStrings::with_mult(
            "hud-skill-sc_lifesteal_damage_title",
            "hud-skill-sc_lifesteal_damage",
            modifiers.beam.damage,
        ),
        SceptreSkill::LRange => SkillStrings::with_mult(
            "hud-skill-sc_lifesteal_range_title",
            "hud-skill-sc_lifesteal_range",
            modifiers.beam.range,
        ),
        SceptreSkill::LLifesteal => SkillStrings::with_mult(
            "hud-skill-sc_lifesteal_lifesteal_title",
            "hud-skill-sc_lifesteal_lifesteal",
            modifiers.beam.lifesteal,
        ),
        SceptreSkill::LRegen => SkillStrings::with_mult(
            "hud-skill-sc_lifesteal_regen_title",
            "hud-skill-sc_lifesteal_regen",
            modifiers.beam.energy_regen,
        ),
        // Healing aura upgrades
        SceptreSkill::HHeal => SkillStrings::with_mult(
            "hud-skill-sc_heal_heal_title",
            "hud-skill-sc_heal_heal",
            modifiers.healing_aura.strength,
        ),
        SceptreSkill::HRange => SkillStrings::with_mult(
            "hud-skill-sc_heal_range_title",
            "hud-skill-sc_heal_range",
            modifiers.healing_aura.range,
        ),
        SceptreSkill::HDuration => SkillStrings::with_mult(
            "hud-skill-sc_heal_duration_title",
            "hud-skill-sc_heal_duration",
            modifiers.healing_aura.duration,
        ),
        SceptreSkill::HCost => SkillStrings::with_mult(
            "hud-skill-sc_heal_cost_title",
            "hud-skill-sc_heal_cost",
            modifiers.healing_aura.energy_cost,
        ),
        // Warding aura upgrades
        SceptreSkill::UnlockAura => SkillStrings::plain(
            "hud-skill-sc_wardaura_unlock_title",
            "hud-skill-sc_wardaura_unlock",
        ),
        SceptreSkill::AStrength => SkillStrings::with_mult(
            "hud-skill-sc_wardaura_strength_title",
            "hud-skill-sc_wardaura_strength",
            modifiers.warding_aura.strength,
        ),
        SceptreSkill::ADuration => SkillStrings::with_mult(
            "hud-skill-sc_wardaura_duration_title",
            "hud-skill-sc_wardaura_duration",
            modifiers.warding_aura.duration,
        ),
        SceptreSkill::ARange => SkillStrings::with_mult(
            "hud-skill-sc_wardaura_range_title",
            "hud-skill-sc_wardaura_range",
            modifiers.warding_aura.range,
        ),
        SceptreSkill::ACost => SkillStrings::with_mult(
            "hud-skill-sc_wardaura_cost_title",
            "hud-skill-sc_wardaura_cost",
            modifiers.warding_aura.energy_cost,
        ),
    }
}

fn roll_skill_strings(skill: RollSkill) -> SkillStrings<'static> {
    let modifiers = SKILL_MODIFIERS.general_tree.roll;
    match skill {
        RollSkill::Cost => SkillStrings::with_mult(
            "hud-skill-roll_energy_title",
            "hud-skill-roll_energy",
            modifiers.energy_cost,
        ),
        RollSkill::Strength => SkillStrings::with_mult(
            "hud-skill-roll_speed_title",
            "hud-skill-roll_speed",
            modifiers.strength,
        ),
        RollSkill::Duration => SkillStrings::with_mult(
            "hud-skill-roll_dur_title",
            "hud-skill-roll_dur",
            modifiers.duration,
        ),
    }
}

fn climb_skill_strings(skill: ClimbSkill) -> SkillStrings<'static> {
    let modifiers = SKILL_MODIFIERS.general_tree.climb;
    match skill {
        ClimbSkill::Cost => SkillStrings::with_mult(
            "hud-skill-climbing_cost_title",
            "hud-skill-climbing_cost",
            modifiers.energy_cost,
        ),
        ClimbSkill::Speed => SkillStrings::with_mult(
            "hud-skill-climbing_speed_title",
            "hud-skill-climbing_speed",
            modifiers.speed,
        ),
    }
}

fn swim_skill_strings(skill: SwimSkill) -> SkillStrings<'static> {
    let modifiers = SKILL_MODIFIERS.general_tree.swim;
    match skill {
        SwimSkill::Speed => SkillStrings::with_mult(
            "hud-skill-swim_speed_title",
            "hud-skill-swim_speed",
            modifiers.speed,
        ),
    }
}

fn mining_skill_strings(skill: MiningSkill) -> SkillStrings<'static> {
    let modifiers = SKILL_MODIFIERS.mining_tree;
    match skill {
        MiningSkill::Speed => SkillStrings::with_mult(
            "hud-skill-pick_strike_speed_title",
            "hud-skill-pick_strike_speed",
            modifiers.speed,
        ),
        MiningSkill::OreGain => SkillStrings::with_const(
            "hud-skill-pick_strike_oregain_title",
            "hud-skill-pick_strike_oregain",
            (modifiers.ore_gain * 100.0).round() as u32,
        ),
        MiningSkill::GemGain => SkillStrings::with_const(
            "hud-skill-pick_strike_gemgain_title",
            "hud-skill-pick_strike_gemgain",
            (modifiers.gem_gain * 100.0).round() as u32,
        ),
    }
}

/// Helper object used returned by `skill_strings` as source for
/// later internationalization and formatting.
enum SkillStrings<'a> {
    Plain {
        title: &'a str,
        desc: &'a str,
    },
    WithConst {
        title: &'a str,
        desc: &'a str,
        constant: u32,
    },
    WithConstFloat {
        title: &'a str,
        desc: &'a str,
        constant: f32,
    },
    WithMult {
        title: &'a str,
        desc: &'a str,
        multiplier: f32,
    },
    Empty,
}

impl<'a> SkillStrings<'a> {
    fn plain(title: &'a str, desc: &'a str) -> Self { Self::Plain { title, desc } }

    fn with_const(title: &'a str, desc: &'a str, constant: u32) -> Self {
        Self::WithConst {
            title,
            desc,
            constant,
        }
    }

    fn with_const_float(title: &'a str, desc: &'a str, constant: f32) -> Self {
        Self::WithConstFloat {
            title,
            desc,
            constant,
        }
    }

    fn with_mult(title: &'a str, desc: &'a str, multiplier: f32) -> Self {
        Self::WithMult {
            title,
            desc,
            multiplier,
        }
    }

    fn localize<'loc>(
        &self,
        i18n: &'loc Localization,
        skill_set: &SkillSet,
        skill: Skill,
    ) -> (Cow<'loc, str>, Cow<'loc, str>) {
        match self {
            Self::Plain { title, desc } => {
                let title = i18n.get_msg(title);

                let args = i18n::fluent_args! {
                    "SP" => sp(i18n, skill_set, skill),
                };
                let desc = i18n.get_msg_ctx(desc, &args);

                (title, desc)
            },
            Self::WithConst {
                title,
                desc,
                constant,
            } => {
                let title = i18n.get_msg(title);
                let args = i18n::fluent_args! {
                    "boost" => constant,
                    "SP" => sp(i18n, skill_set, skill),
                };
                let desc = i18n.get_msg_ctx(desc, &args);

                (title, desc)
            },
            Self::WithConstFloat {
                title,
                desc,
                constant,
            } => {
                let title = i18n.get_msg(title);
                let args = i18n::fluent_args! {
                    "boost" => constant,
                    "SP" => sp(i18n, skill_set, skill),
                };
                let desc = i18n.get_msg_ctx(desc, &args);

                (title, desc)
            },
            Self::WithMult {
                title,
                desc,
                multiplier,
            } => {
                let percentage = hud::multiplier_to_percentage(*multiplier).abs();

                let title = i18n.get_msg(title);

                let args = i18n::fluent_args! {
                    "boost" => format!("{percentage:.0}"),
                    "SP" => sp(i18n, skill_set, skill),
                };
                let desc = i18n.get_msg_ctx(desc, &args);

                (title, desc)
            },
            Self::Empty => (Cow::Borrowed(""), Cow::Borrowed("")),
        }
    }
}

fn sp<'loc>(i18n: &'loc Localization, skill_set: &SkillSet, skill: Skill) -> Cow<'loc, str> {
    let current_level = skill_set.skill_level(skill);
    if matches!(current_level, Ok(level) if level == skill.max_level()) {
        Cow::Borrowed("")
    } else {
        i18n.get_msg_ctx("hud-skill-req_sp", &i18n::fluent_args! {
            "number" => skill_set.skill_cost(skill),
        })
    }
}