const { connect } = require('preact-redux');
const preact = require('preact');

const range = require('lodash/range');

const buttons = require('./buttons');
const shapes = require('./shapes');
const { STATS } = require('../utils');
const { ConstructAvatar } = require('./construct');
const actions = require('../actions');
const { removeTier } = require('../utils');
const { tutorialConstructDisplay, tutorialShouldDisableEquip } = require('../tutorial.utils.jsx');

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

        function sendVboxAcceptEquip(constructId) {
            return ws.sendVboxAcceptEquip(instance.id, vboxSelected.shopSelect[0][0], vboxSelected.shopSelect[0][1], constructId);
        }

        function sendVboxApply(constructId, i) {
            return ws.sendVboxApply(instance.id, constructId, i);
        }

        function sendVboxUnequipApply(targetConstructId) {
            return ws.sendVboxUnequipApply(instance.id, itemUnequip[0], itemUnequip[1], targetConstructId);
        }

        return {
            instance,
            player,
            account,
            sendVboxAcceptEquip,
            sendVboxUnequipApply,
            sendVboxApply,
            itemInfo,
            itemUnequip,
            vboxSelected,
            tutorial,
        };
    },

    function receiveDispatch(dispatch) {
        function quit() {
            dispatch(actions.setInstance(null));
        }

        function setInfo(item) {
            dispatch(actions.setInfo(item));
        }

        function setItemUnequip(v) {
            dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
            return dispatch(actions.setItemUnequip(v));
        }

        return { quit, setInfo, setItemUnequip };
    }

);

function Construct(props) {
    const {
        // Changing state variables
        construct,
        iter,
        itemUnequip,
        instance,
        player,
        vboxSelected,
        tutorial,
        // Static Info
        itemInfo,
        // Function Calls
        sendVboxApply,
        sendVboxAcceptEquip,
        sendVboxUnequipApply,
        setItemUnequip,
        setInfo,
    } = props;

    const { vbox } = player;

    const itemEquip = vboxSelected.shopSelect.length === 0 && vboxSelected.stashSelect.length === 1
        ? vboxSelected.stashSelect[0]
        : -1;

    const duplicateSkill = construct.skills.length !== 0 && construct.skills.every(sk => {
        if (!itemEquip && itemEquip !== 0) return false;
        if (!sk) return false;
        return sk.skill === vbox.bound[itemEquip];
    });
    const tutorialDisableEquip = tutorialShouldDisableEquip(tutorial, iter, instance, construct);
    function onClick(e) {
        e.stopPropagation();
        e.preventDefault();
        if (duplicateSkill || tutorialDisableEquip) return true;
        if (itemEquip !== -1) return sendVboxApply(construct.id, itemEquip);
        if (vboxSelected.shopSelect.length === 1) return sendVboxAcceptEquip(construct.id);
        if (itemUnequip.length && itemUnequip[0] !== construct.id) return sendVboxUnequipApply(construct.id);
        setItemUnequip([]);
        return true;
    }
    function hoverInfo(e, info) {
        e.stopPropagation();
        if (!info) return false;
        if (vboxSelected.shopSelect.length || vboxSelected.stashSelect.length) return false;
        return setInfo(info);
    }

    const skillList = itemInfo.items.filter(v => v.skill).map(v => v.item);
    const specList = itemInfo.items.filter(v => v.spec).map(v => v.item);

    const skills = range(0, 3).map(i => {
        const skill = construct.skills[i];
        const s = skill
            ? skill.skill
            : (<span class="gray">SKILL</span>);

        function skillClick(e) {
            if (!skill) return false;
            setItemUnequip([construct.id, skill.skill, i]);
            e.stopPropagation();
            return true;
        }

        const equipping = skillList.includes(vbox.bound[itemEquip]) && !skill
            && !tutorialDisableEquip && !duplicateSkill && i === construct.skills.length;
        const border = () => {
            if (!skill) return '';
            const borderFn = buttons[removeTier(skill.skill)];
            if (!borderFn) return '';
            return borderFn();
        };

        const highlight = itemUnequip[0] === construct.id && itemUnequip[1] === s ? 'highlight' : '';

        const classes = `${highlight} ${equipping ? 'equipping' : ''} ${!skill ? 'empty' : ''} ${border()}`;
        return (
            <label onDragStart={ev => {
                ev.dataTransfer.setData('text', '');
                skillClick(ev);
            }} key={i} draggable={skill} onDragEnd={() => setItemUnequip([])}>
                <button
                    key={i}
                    disabled={!skill && !equipping}
                    class={classes}
                    onClick={skillClick}
                    onMouseOver={e => hoverInfo(e, skill && skill.skill)} >
                    {s}
                </button>
            </label>
        );
    });

    const specs = range(0, 3).map(i => {
        const s = construct.specs[i];

        if (!s) {
            const equipping = specList.includes(vbox.bound[itemEquip]) && i === construct.specs.length;
            const classes = `${equipping ? 'equipping' : 'gray'} empty`;
            return (
                <button key={i} class={classes} disabled={!equipping} >
                    {shapes.None()}
                </button>
            );
        }

        function specClick(e) {
            e.stopPropagation();
            setItemUnequip([construct.id, s, i]);
        }

        const highlight = itemUnequip[0] === construct.id && itemUnequip[1] === s && i === itemUnequip[2];
        return (
            <label onDragStart={ev => {
                ev.dataTransfer.setData('text', '');
                specClick(ev);
            }} key={i} draggable="true" onDragEnd={() => setItemUnequip([])}>
                <button
                    key={i}
                    class={`${highlight ? 'highlight' : ''}`}
                    onClick={specClick}
                    onMouseOver={e => hoverInfo(e, s)} >
                    {shapes[s]()}
                </button>
            </label>
        );
    });

    const stats = Object.keys(STATS).map(s => {
        const stat = STATS[s];

        const info = (s === 'SpeedStat' && 'speedStat')
            || (s.includes('Power') && 'powerStat')
            || (s.includes('Life') && 'lifeStat');
        return <div key={stat.stat}
            alt={stat.stat}
            class={stat.stat}
            onMouseOver={e => hoverInfo(e, info)} >
            {shapes[s]()}
            <div>{construct[stat.stat].value}</div>
        </div>;
    });

    const classes = 'instance-construct';
    const avatarMouseOver = e => hoverInfo(e, `constructAvatar ${construct.name}`);
    return (
        <div key={construct.id} class={classes} onClick={onClick} onDragOver={ev => ev.preventDefault()} onDrop={onClick}>
            <ConstructAvatar construct={construct} mouseOver={avatarMouseOver}/>
            <h2 class="name" onMouseOver={e => hoverInfo(e, `constructName ${construct.name}`)}>{construct.name}</h2>
            <div class="skills" onMouseOver={e => hoverInfo(e, 'constructSkills')} >
                {skills}
            </div>
            <div class="specs" onMouseOver={e => hoverInfo(e, 'constructSpecs')} >
                {specs}
            </div>
            <div class="stats">
                {stats}
            </div>
        </div>
    );
}

class InstanceConstructs extends preact.Component {
    shouldComponentUpdate(newProps) {
        if (newProps.itemUnequip !== this.props.itemUnequip) return true;
        if (newProps.tutorial !== this.props.tutorial) return true;
        // JSON or Array objects
        if (newProps.player !== this.props.player) return true;
        if (newProps.instance !== this.props.instance) return true;
        if (newProps.vboxSelected !== this.props.vboxSelected) return true;
        return false;
    }

    render(props) {
        const {
            // Changing state variables
            itemUnequip,
            instance,
            player,
            tutorial,
            vboxSelected,
            // Static data
            itemInfo,
            // Function calls
            setInfo,
            sendVboxApply,
            sendVboxAcceptEquip,
            sendVboxUnequipApply,
            setVboxHighlight,
            setItemUnequip,
        } = props;

        if (!player) return false;
        if (instance.phase === 'Lobby') return false;

        const constructs = range(0, 3).map(i => {
            const tutorialConstruct = tutorialConstructDisplay(player, instance, tutorial, i);
            if (tutorialConstruct) return (tutorialConstruct);

            return Construct({
                iter: i,
                construct: player.constructs[i],
                itemUnequip,
                instance,
                setItemUnequip,
                player,
                sendVboxApply,
                sendVboxAcceptEquip,
                sendVboxUnequipApply,
                setInfo,
                itemInfo,
                setVboxHighlight,
                vboxSelected,
                tutorial,
            });
        });

        return (
            <div class='construct-list'>
                {constructs}
            </div>
        );
    }
}

module.exports = addState(InstanceConstructs);
