const preact = require('preact');
const { connect } = require('preact-redux');
const reactStringReplace = require('react-string-replace');
const countBy = require('lodash/countBy');

const specThresholds = require('./vbox.info.thresholds');
const { INFO } = require('./../constants');
const { convertItem, removeTier } = require('../utils');
const { tutorialStage } = require('../tutorial.utils');
const shapes = require('./shapes');

const actions = require('../actions');

const addState = connect(
    function receiveState(state) {
        const {
            ws,
            info,
            itemInfo,
            instance,
            player,
            account,
            tutorial,
            vboxSelected,
        } = state;

        function sendVboxAccept(group, index) {
            document.activeElement.blur();
            return ws.sendVboxAccept(instance.id, group, index);
        }

        function sendVboxCombine() {
            return ws.sendVboxCombine(instance.id, vboxSelected.stashSelect, vboxSelected.storeSelect);
        }

        return {
            ws,
            info,
            itemInfo,
            instance,
            player,
            account,
            tutorial,
            vboxSelected,
            sendVboxAccept,
            sendVboxCombine,
        };
    },

    function receiveDispatch(dispatch) {
        function setTutorialNull() {
            dispatch(actions.setTutorial(null));
        }

        function setInfo(info) {
            dispatch(actions.setInfo(info));
        }
        return { setTutorialNull, setInfo };
    }
);

class InfoComponent extends preact.Component {
    shouldComponentUpdate(newProps, newState) {
        if (newProps.vboxSelected !== this.props.vboxSelected) return true;
        if (newProps.vboxHighlight !== this.props.vboxHighlight) return true;
        if (newProps.tutorial !== this.props.tutorial) return true;
        // We don't care about info during tutorial
        if (newProps.tutorial && this.props.instance.time_control === 'Practice'
            && this.props.instance.rounds.length === 1) return false;
        if (newProps.info !== this.props.info) return true;
        if (newState.comboItem !== this.state.comboItem) return true;

        return false;
    }

    componentDidUpdate(prevProps) {
        // Catch case where mouse events don't properly clear state and info changed
        if (prevProps.info !== this.props.info && this.state.comboItem) this.setState({ comboItem: null });
    }

    render(args) {
        const {
            // Variables that will change
            info,
            tutorial,
            vboxHighlight,
            vboxSelected,
            // Static
            player, // Only used for colour calcs which will be update if info changes
            ws,
            itemInfo,
            instance, // Only used for instance id
            // functions
            sendVboxAccept,
            sendVboxCombine,
            setTutorialNull,
        } = args;
        const { comboItem } = this.state;

        const { vbox } = player;
        const { stashSelect, storeSelect } = vboxSelected;

        const vboxCombo = () => {
            if (!(vboxHighlight && vboxHighlight.length === 0)) return false;
            // The selected items can't be combined with additional items therefore valid combo
            const stashItems = stashSelect.map(j => vbox.bound[j]);
            const shopItems = storeSelect.map(j => vbox.free[j[0]][j[1]]);
            const selectedItems = stashItems.concat(shopItems);
            const combinerCount = countBy(selectedItems, co => co);

            const comboItemObj = itemInfo.combos.find(combo => selectedItems.every(c => {
                if (!combo.components.includes(c)) return false;
                const comboCount = countBy(combo.components, co => co);
                if (combinerCount[c] > comboCount[c]) return false;
                return true;
            }));
            return comboItemObj.item;
        };

        const combinerCombo = vboxCombo();
        const checkVboxInfo = () => {
            if (combinerCombo) return combinerCombo;
            const stashBase = stashSelect.find(i => !(['Red', 'Blue', 'Green'].includes(vbox.bound[i])));
            if (stashBase > -1) return vbox.bound[stashBase];
            const storeBase = storeSelect.find(j => !(['Red', 'Blue', 'Green'].includes(vbox.free[j[0]][j[1]])));
            if (storeBase) return vbox.free[storeBase[0]][storeBase[1]];
            if (stashSelect.length > 0) return vbox.bound[stashSelect[0]];
            if (storeSelect.length > 0) return vbox.free[storeSelect[0][0]][storeSelect[0][1]];
            return false;
        };

        const vboxInfo = (stashSelect.length > 0 || storeSelect.length > 0) ? checkVboxInfo() : false;

        function Info() {
            if (tutorial) {
                const tutorialStageInfo = tutorialStage(tutorial, ws, setTutorialNull, instance);
                if (tutorialStageInfo) return tutorialStageInfo;
            }

            function genItemInfo(item) {
                const fullInfo = itemInfo.items.find(i => i.item === item) || INFO[item];
                const isSkill = fullInfo.skill;
                const isSpec = fullInfo.spec;
                const itemDescription = () => {
                    const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat|LIFE|SPEED|POWER)/;
                    const infoDescription = reactStringReplace(fullInfo.description, regEx, m => shapes[m]());
                    return <div>{reactStringReplace(infoDescription, '\n', () => <br />)}</div>;
                };
                if (isSkill || isSpec) {
                    let infoName = fullInfo.item;
                    while (infoName.includes('Plus')) infoName = infoName.replace('Plus', '+');

                    const itemSource = itemInfo.combos.filter(c => c.item === removeTier(fullInfo.item));

                    let itemSourceInfo = itemSource.length && !isSpec
                        ? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}`
                        : false;

                    let header = null;
                    if (!itemSource.length) header = isSkill ? <h3> SKILL </h3> : <h3> SPEC </h3>;
                    if (itemSourceInfo) {
                        while (itemSourceInfo.includes('Plus')) itemSourceInfo = itemSourceInfo.replace('Plus', '+');
                        const itemRegEx = /(Red|Blue|Green)/;
                        itemSourceInfo = reactStringReplace(itemSourceInfo, itemRegEx, match => shapes[match]());
                    }

                    const cooldown = isSkill && fullInfo.cooldown ? <div>{fullInfo.cooldown} Turn delay</div> : null;

                    const speed =  isSkill
                        ? <div> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </div>
                        : null;

                    const thresholds = isSpec ? specThresholds(player, fullInfo, info) : null;

                    return (
                        <div class={isSkill ? 'info-item' : 'info-item'}>
                            <h2>{infoName}</h2>
                            {header}
                            {itemSourceInfo}
                            {cooldown}
                            {itemDescription()}
                            {speed}
                            {thresholds}
                        </div>
                    );
                }
                return (
                    <div class="info-item">
                        <h2>{fullInfo.item}</h2>
                        {itemDescription()}
                    </div>
                );
            }
            const stateFullInfo = comboItem || vboxInfo;
            if (stateFullInfo) return genItemInfo(stateFullInfo);
            if (!info) return false;
            if (info.includes('constructName')) {
                return (
                    <div class='info-item'>
                        <h2> {info.replace('constructName ', '')} </h2>
                        <p> This is the name of your construct. <br />
                            Names are randomly generated and are purely cosmetic. <br />
                            You can change change your construct name in the <b>RESHAPE</b> tab outside of games.
                        </p>
                    </div>
                );
            }

            if (info.includes('constructAvatar')) {
                return (
                    <div class='info-item'>
                        <h2> {info.replace('constructAvatar ', '')} </h2>
                        <p> This is your construct avatar. <br />
                            Avatars are randomly generated and are purely cosmetic. <br />
                            You can change your construct avatar in the <b>RESHAPE</b> tab outside of games.
                        </p>
                    </div>
                );
            }

            return genItemInfo(info);
        }

        const Combos = () => {
            if (tutorial && instance.time_control === 'Practice' && instance.rounds.length === 1) return false;
            if (!info && !vboxInfo) return false;
            const comboInfo = vboxInfo || info;
            const vboxCombos = itemInfo.combos.filter(c => c.components.includes(comboInfo));
            if (vboxCombos.length > 6 || vboxCombos.length === 0) return false;

            const comboTable = vboxCombos.map((c, i) => {
                const mouseOver = e => {
                    e.stopPropagation();
                    this.setState({ comboItem: c.item });
                };
                const componentTable = (c.components.some(ci => ['Red', 'Blue', 'Green'].includes(ci)))
                    ? [<div key="0">{convertItem(c.components[0])}&nbsp;{convertItem(c.components[1])}</div>,
                        <div key="1">{convertItem(c.components[2])}</div>]
                    : c.components.map((u, j) => <div key={j} >{convertItem(u)}</div>);
                return (
                    <div key={i} onMouseOver={mouseOver} class="table-button">
                        <div class="item">
                            {convertItem(c.item)}
                        </div>
                        {componentTable}
                    </div>
                );
            });
            const comboList = comboTable.length > 0 ? <div class="combo-list">{comboTable}</div> : false;
            return (
                <div class="combos">
                    <div class="combo-header">
                        <h2>COMBOS</h2>
                        Combine colours and items.
                    </div>
                    {comboList}
                </div>
            );
        };

        const Combiner = () => {
            if (comboItem) return false;
            function vboxBuySelected() {
                if (!(storeSelect.length === 1 && stashSelect.length === 0)) return false;
                document.activeElement.blur();
                sendVboxAccept(storeSelect[0][0], storeSelect[0][1]);
                return true;
            }
            let text = '';
            let mouseEvent = false;
            if (combinerCombo) {
                const combinerComboText = combinerCombo.replace('Plus', '+');
                let bits = 0;
                storeSelect.forEach(item => bits += item[0] + 1);
                text = bits
                    ? `Buy ${combinerComboText} ${bits}b`
                    : `Combine ${combinerComboText}`;
                if (vbox.bits >= bits) mouseEvent = sendVboxCombine;
            } else if (stashSelect.length === 0 && storeSelect.length === 1) {
                const item = storeSelect[0];
                text = `Buy ${vbox.free[item[0]][item[1]]} ${item[0] + 1}b`;
                mouseEvent = vboxBuySelected;
            } else {
                return false;
            }
            return (
                <div class='combiner'>
                    <button
                        class='vbox-btn'
                        disabled={!mouseEvent}
                        onClick={e => e.stopPropagation()}
                        onMouseDown={() => mouseEvent()}>
                        {text}
                    </button>
                </div>
            );
        };

        return (
            <div class='info'>
                <div onMouseOver={() => this.setState({ comboItem: null })}>
                    <Info />
                    <Combiner />
                </div>
                <Combos />
            </div>
        );
    }
}

module.exports = addState(InfoComponent);
