/**
 * Capitalize Helper
 **/
let capitalize = str => str ? str.charAt(0).toUpperCase() + str.slice(1) : '';

/**
 * Tooltip Counter
 */
let tooltipCount = 1;
let tooltipCounter = () => tooltipCount++;

/**
 * Discover Classes
 */
let classes = [];
const DISCOVERCLASSES = spells => {
    let itemsFound = [];
    spells.forEach(spell => {
        if (!spell.classes) return;
        spell.classes.forEach(current => {
            if (!itemsFound.includes(current)) itemsFound.push(current);
        });
    });
    return itemsFound = itemsFound.sort((a, b) => a > b);
};

/**
 * Dom Helper
 */
const DOMHELPER = {
    emphasis (str) {
        let keywords = ['intelligence', 'int', 'wisdom', 'wis', 'strength', 'str', 'dexterity', 'dex', 'charisma', 'cha', 'comeliness', 'com', 'saving throw', 'ability check', 'skill check'];
        keywords.forEach(word => {
            let r = new RegExp(` ${word} `, 'gi');
            str = str.replace(r, o => `<em class="mdl-color-text--teal-600">${o}</em>`);
        });

        str = str.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => `<strong>${o}</strong>`);
        return str;
    },
    level (lvl) {
        let cantrip = lvl.toLowerCase() === 'cantrip';
        let invalid = isNaN(lvl) && !cantrip;
        if (invalid) return '?';
        else return cantrip ? 'C' : lvl;
    },
    components (components) {
        let dom = '';
        let types = ['verbal', 'somatic', 'material'];
        let typesDescriptions = [
            'Spoken word/prayer. Mouth must be free and not mute.',
            'Physical gestures, usually hand movements. Must not be bound.',
            'Items consumed upon casting spell.'
        ];

        types.forEach((type, i) => {
            if (!!components[type]) {
                let currentTooltip = tooltipCounter();
                dom += `
                    <li class="left has-tooltip" id="tooltip-${currentTooltip}">${types[i].charAt(0).toUpperCase()}</li>
                    <label class="mdl-tooltip" for="tooltip-${currentTooltip}">
                        <strong>${capitalize(type)}</strong><br />
                        ${typesDescriptions[i]}
                    </label>
                `;
            }
        });
        return dom;
    },
    classes (tags) {
        let dom = '';
        classes.forEach(x => {
            dom += `<a>${capitalize(x)}</a>&nbsp;`;
        });
        return dom;
    }
};

/**
 * List of Selected Spells
 */
const SELECTEDSPELLS = () => $('#form-spells').serializeArray().map(spell => parseInt(spell.value));

/**
 * Add Spells to Page
 */
const RENDERSPELLS = items => {
    let dom = '';
    let spells = items.slice(0);
    spells = spells.sort((a, b) => b.ranking - a.ranking);
    spells.forEach(spell => {
        let el = $('#spell-item').html();
        let $el = $(el);

        $('[data-id-spellitem=level]', $el)[0].innerHTML
            = DOMHELPER.level(spell.level);
        $('[data-id-spellitem=name]', $el)[0].innerHTML
            = spell.name;
        $('[data-id-spellitem=school]', $el)[0].innerHTML
            = capitalize(spell.school);
        $('[data-id-spellitem=checkbox]', $el).attr('value', spell.id).attr('id', `sel-${spell.id}`);
        $('[data-id-spellitem=checklabel]', $el).attr('for', `sel-${spell.id}`);
        $el.attr('data-action-dialog', spell.id);

        dom += $el.prop('outerHTML');
    });
    $('#spell-list').html(dom);
    componentHandler.upgradeDom();
};

/**
 * Render Spell Details to Dialog
 */
const RENDERSPELLDIALOG = spell => {
    let el = $('#spell-detail').html();
    let $el = $(`<div>${el}</div>`);

    $('[data-id-spelldetail=name]', $el)[0].innerHTML
        = spell.name;
    $('[data-action-select]', $el).attr('data-action-select', spell.id);
    $('[data-action-select] input[type="checkbox"]', $el).attr('checked', SELECTEDSPELLS().includes(spell.id));
    if (spell.description) {
        $('[data-id-spelldetail=description]', $el)[0].innerHTML
            = DOMHELPER.emphasis(spell.description);
    }
    if (spell.tags) {
        $('[data-id-spelldetail=classes]', $el)[0].innerHTML
            = DOMHELPER.classes(spell.tags);
    }
    if (spell.components) {
        $('[data-id-spelldetail=components]', $el)[0].innerHTML
            = DOMHELPER.components(spell.components);
    }
    if (spell.components.materials_needed) {
        $('[data-id-spelldetail=materials]', $el)[0].innerHTML
            = spell.components.materials_needed.join(', ');
    }
    if (spell.level) {
        $('[data-id-spelldetail=level]', $el)[0].innerHTML
            = 'Level ' + spell.level;
    }
    if (spell.duration) {
        $('[data-id-spelldetail=duration]', $el)[0].innerHTML
            = spell.duration;
    }
    if (spell.casting_time) {
        $('[data-id-spelldetail=castingtime]', $el)[0].innerHTML
            = spell.casting_time;
    }
    if (spell.range) {
        $('[data-id-spelldetail=range]', $el)[0].innerHTML
            = spell.range;
    }

    $('#spell-detail-container').html($el.prop('outerHTML'));
    componentHandler.upgradeDom();
};


/**
 * Fetch Classes from Spells
 */
const RENDERCLASSES = (spells, classes = []) => {
    let dom = '';
    classes.forEach(item => {
        let el = $('#class-toggle').html();
        let $el = $(el);

        $('[data-id-classtoggle=name]', $el)[0].innerHTML = item;
        dom += $el.prop('outerHTML');
    });
    $('#class-list').html(dom);

    return spells;
};

/**
 * Retrieve Spells
 */
let data;

fetch('./spells.json')
    .then(response => response.json())
    .then(spells => spells.map((spell, i) => {
        spell.id = i;
        spell.ranking = 0; // Default to equal ranking
        return spell;
    }))
    .then(spells => data = spells)
    .then(spells => {
        classes = DISCOVERCLASSES(spells);
        return spells;
    })
    .then(spells => RENDERCLASSES(spells, classes))
    .then(spells => RENDERSPELLS(spells))
    .catch(reason => console.error('Unable to retrieve spells list:', reason));

/**
 * Details Popup
 */
let $dialog = $('dialog');

if (!$dialog[0].showModal) {
    dialogPolyfill.registerDialog($dialog);
}

$('#spell-list').on('click', '[data-action-dialog]', e => {
    let id = $(e.target).closest('tr').data('action-dialog');
    RENDERSPELLDIALOG(data[id]);
    $dialog[0].showModal();
}).on('click', '.dontprop', e => {
    e.stopPropagation();
});

$dialog.on('click', '[data-action-close]', () => {
    $dialog[0].close();
});

/**
 * Bind Checkbox Selection Action
 */
$('body').on('change', '[data-action-select] input', e => {
    let id = $(e.target).closest('label').attr('data-action-select');
    if (id) $('#sel-' + id).click();
});

/**
 * Bind Toggle All Checkbox
 */
$('label[for=table-header]').on('change', 'input[type="checkbox"]', e => {
    $(e.target).closest('form').find('[data-id-spellitem=checkbox]').each(function() {
        this.checked = e.target.checked;
        if(this.checked) $(this).closest('label').addClass('is-checked');
        else $(this).closest('label').removeClass('is-checked');
    })
});

/**
 * Search
 */
$('body').on('change keyup cut paste', '[data-action-search]', e => {
    setTimeout(function() {
        if (e.target.value.length >= 3) {
            let filteredData = data.filter(spell => {
                let search = e.target.value.toLowerCase();
                let regFind = new RegExp(search, 'gi');
                let spellName = spell.name.toLowerCase();
                let spellDescription = spell.description ? spell.description.toLowerCase() : '';
                let spellMaterials = spell.components && spell.components.materials_needed || [];

                let matches = 0
                    + spellName.indexOf(search) > -1 ? 20 : 0
                    + (spellDescription.match(regFind) || []).length
                    + (spellMaterials).filter(com => com.indexOf(search) > -1).length * 10;

                spell.ranking = matches;
                return spell.ranking > 0;
            });
            RENDERSPELLS(filteredData);
        } else RENDERSPELLS(data);
    }, 0);
});
