import React from 'react';
import GridField from './grid-field';
import UserControl from '../user/user-control';

class GridControl extends React.Component {
    constructor(props) {
        super(props);

        let click = new Howl({src: ['/sound/click.mp3']}),
            bomb = new Howl({src: ['/sound/bomb.mp3']}),
            mine = new Howl({src: ['/sound/mine.mp3']}),
            warning = new Howl({src: ['/sound/warning.mp3']}),
            won = new Howl({src: ['/sound/won.mp3']});

        this.state = {
            env: props.env,
            webPlayer: null,
            grid: null,
            desc: null,
            renderGridFields: false,
            gridFields: [],
            updatedFieldCache: [],
            bombFieldCache: [],
            foundUserMineCache: 0,
            playBomb: false,
            overlay: false,
            overlayTitle: "",
            overlaySubTitle: "",
            sound: {
                click: click,
                bomb: bomb,
                mine: mine,
                warning: warning,
                won: won
            },
            lastClicked: {
                red: null,
                blue: null
            }
        };
    }

    refString(row, col) {
        return 'gridField_' + row + '_' + col;
    }

    checkMine(row, col) {
        return typeof this.state.grid[row] !== 'undefined' && typeof this.state.grid[row][col] !== 'undefined' && this.state.grid[row][col] !== 'm';
    }

    checkFieldHasBeenNeverClicked(row, col) {
        return this.state.updatedFieldCache.indexOf(this.refString(row, col)) < 0 && !this.refs[this.refString(row, col)].state.active;
    }

    getBombRadius(row, col) {
        let isBombTargetCenter = row > 1 && row < this.state.grid.length - 2 && col > 1 && col < this.state.grid[row].length - 2;

        /** if the (5x5) target not fits the grid */
        if (!isBombTargetCenter) {
            col = col < 2 ? 2 : col;
            row = row < 2 ? 2 : row;
            row = row > this.state.grid.length - 3 ? this.state.grid.length - 3 : row;
            col = col > this.state.grid[0].length - 3 ? this.state.grid[0].length - 3 : col;
        }

        return [
            [row, col], [row - 2, col - 2], [row - 2, col], [row - 2, col + 2], [row, col - 2], [row, col + 2],
            [row + 2, col - 2], [row + 2, col], [row + 2, col + 2], [row - 2, col + 1], [row - 2, col - 1],
            [row - 1, col - 2], [row - 1, col - 1], [row - 1, col], [row - 1, col + 1], [row - 1, col + 2],
            [row, col - 1], [row, col + 1], [row + 1, col - 2], [row + 1, col - 1], [row + 1, col],
            [row + 1, col + 1], [row + 1, col + 2], [row + 2, col - 1], [row + 2, col + 1]
        ];
    }

    getBombFieldRadius() {
        return [
            [null, null], [0, 0], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2], [2, 2],
            [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null],
            [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null],
            [null, null], [null, null]
        ];
    }

    getNeighbourRadius(row, col) {
        return [
            [row - 1, col], [row - 1, col - 1], [row - 1, col + 1], [row, col - 1], [row, col + 1], [row + 1, col],
            [row + 1, col + 1], [row + 1, col - 1]
        ];
    }

    checkNeighbourItem(row, col) {
        if (this.checkMine(row, col)) {
            var currentField = this.refs[this.refString(row, col)];

            /**
             * It must be cached because the GridField.state not updated until
             * all showAppropriateFields() method runned out!!
             */
            if (this.checkFieldHasBeenNeverClicked(row, col)) {
                this.state.updatedFieldCache.push(this.refString(row, col));

                currentField.setState({
                    currentImage: this.state.grid[row][col],
                    currentObj: this.state.grid[row][col],
                    active: true
                });

                if (this.state.grid[row][col] === 0) {
                    return {
                        row: row,
                        col: col
                    };
                }
            }
        }

        return false;
    }

    checkNeighbours(row, col) {
        let anotherFields = [],
            neighbours = this.getNeighbourRadius(row, col);

        for (let i = 0, j = neighbours.length; i < j; i++) {
            anotherFields.push(this.checkNeighbourItem(neighbours[i][0], neighbours[i][1]));
        }

        return anotherFields;
    }

    bombClear() {
        if (this.state.bombFieldCache.length) {
            for (var i = 0, j = this.state.bombFieldCache.length; i < j; i++) {
                var cacheItem = this.state.bombFieldCache[i];
                this.refs[this.refString(cacheItem[0], cacheItem[1])]
                    .setState({bombTargetArea: null});
            }
            this.state.bombFieldCache = [];
        }
    }

    bombCreate(row, col) {
        var bombFieldSymbols = this.getBombFieldRadius(),
            bombFields = this.getBombRadius(row, col);

        for (var i = 0, j = bombFields.length; i < j; i++) {
            this.state.bombFieldCache.push(bombFields[i]);

            this.refs[this.refString(bombFields[i][0], bombFields[i][1])]
                .setState({bombTargetArea: bombFieldSymbols[i]});
        }
    }

    showLeftMines() {
        for (let i = 0, j = this.state.grid.length; i < j; i++) {
            for (let k = 0, l = this.state.grid[i].length; k < l; k++) {
                let currentField = this.refs[this.refString(i, k)];

                if (this.state.grid[i][k] === 'm' && this.checkFieldHasBeenNeverClicked(i, k)) {
                    currentField.setState({
                        currentImage: currentField.state.icons.root + currentField.state.icons.left
                    });
                }
            }
        }
    }

    /** set __ACTIVE__ player in the UserControl !!!! */
    changePlayer(idx, max, currentObject) {
        var userControl = this.refs.userControl,
            activePlayer = userControl.state.activePlayer ? 'blue' : 'red',
            inactivePlayer = userControl.state.activePlayer ? 'red' : 'blue';

        if (
            userControl.state.bombSelected && idx === (max - 1) ||
            !idx && !userControl.state.bombSelected && currentObject !== 'm'
        ) {
            userControl.setState({
                activePlayer: userControl.state.activePlayer ? 0 : 1
            });

            /** the desc is inversely because the user.active is not changed yet !!! */
            userControl.refs[activePlayer].setState({
                active: false,
                desc: ""
            });

            userControl.refs[inactivePlayer].setState({
                active: true,
                desc: activePlayer === this.state.webPlayer
                    ? this.state.desc.buddy
                    : this.state.desc.you
            });
        }
    }

    /**
     * Show all fields that needed after click
     *
     * @param currentField
     * @param row
     * @param col
     */
    showAppropriateFields(currentField, row, col) {
        currentField.setState({
            currentObj: this.state.grid[row][col],
            active: true
        });

        if (this.checkFieldHasBeenNeverClicked(row, col)) {
            this.state.updatedFieldCache.push(this.refString(row, col));
        }

        if (this.state.grid[row][col] === 0) {
            var neighbours = this.checkNeighbours(row, col);

            neighbours
                .filter((i) => {
                    return i !== false;
                })
                .forEach((element, index, array) => {
                    var currentField = this.refs[this.refString(element.row, element.col)];
                    this.showAppropriateFields(currentField, element.row, element.col);
                });
        }
    }

    /**
     * Player control method
     *
     * @param currentObject {int|string} Current object from Grid class
     * @param row {int}
     * @param col {int}
     * @param justOnFirstIteration {int} When bomb is being used check the whole explosion area
     */
    handleGridField(currentObject, row, col, justOnFirstIteration = 0) {
        var userControl = this.refs.userControl,
            gridFieldControl = this.refs[this.refString(row, col)],
            activePlayer = userControl.state.activePlayer ? 'blue' : 'red',
            inactivePlayer = userControl.state.activePlayer ? 'red' : 'blue';

        /** if the clicked field is NEVER CLICKED */
        if (this.checkFieldHasBeenNeverClicked(row, col)) {
            /** update LAST CLICKED grid field */
            if (!justOnFirstIteration) {
                if (this.state.lastClicked[activePlayer] !== null) {
                    this.refs[this.refString(this.state.lastClicked[activePlayer][0], this.state.lastClicked[activePlayer][1])].setState({
                        lastClickedRed: false,
                        lastClickedBlue: false
                    });
                }
            }

            this.state.lastClicked[activePlayer] = [row, col];

            /** if you found mine */
            if (currentObject === 'm') {
                this.state.foundUserMineCache++;

                if (!justOnFirstIteration) {
                    /** set last clicked field w/ color */
                    this.state.lastClicked[activePlayer] = [row, col];

                    this.state.sound[
                        (userControl.refs[activePlayer].state.mines + this.state.foundUserMineCache) > 20
                            ? 'warning'
                            : 'mine'
                        ].play();
                }

                /** set current image in field */
                gridFieldControl.setState({
                    currentImage: gridFieldControl.state.icons.root + gridFieldControl.state.icons.flag[activePlayer]
                });
            } else {
                this.state.sound.click.play();

                /** set current image in field - WHEN it is a number */
                if (!isNaN(currentObject)) {
                    gridFieldControl.setState({
                        currentImage: currentObject
                    });
                }
            }

            /**
             * set bombs status - we must add one mine (currentObject === 'm' ? 1 : 0) to current mine
             * when it found NOW because the status is not refreshed unless the handleGridField() ends
             */
            userControl.refs[activePlayer].setState({
                enabledBomb: userControl.refs[activePlayer].state.mines + (currentObject === 'm' ? 1 : 0) <= userControl.refs[inactivePlayer].state.mines
            });

            userControl.refs[inactivePlayer].setState({
                enabledBomb: userControl.refs[activePlayer].state.mines + (currentObject === 'm' ? 1 : 0) >= userControl.refs[inactivePlayer].state.mines
            });

            /** set-up last clicked */
            if (!justOnFirstIteration) {
                gridFieldControl.setState({
                    lastClickedRed: activePlayer === 'red',
                    lastClickedBlue: activePlayer === 'blue'
                });
            }
        }
    }

    /**
     * Show elems w/ conditions
     *
     * @param row
     * @param col
     * @param idx
     * @param max
     */
    show(row, col, idx = 0, max = 0) {
        this.handleGridField(this.state.grid[row][col], row, col, idx);
        this.showAppropriateFields(this.refs[this.refString(row, col)], row, col);
        this.changePlayer(idx, max, this.state.grid[row][col]);
    }

    /**
     * STEP one
     *
     * @param coords
     */
    stepEvent(coords) {
        /** if the clicked field is NEVER CLICKED */
        if (this.checkFieldHasBeenNeverClicked(coords[0], coords[1])) {
            var activePlayer = this.refs.userControl.state.activePlayer ? 'blue' : 'red';

            this.state.foundUserMineCache = 0;
            this.state.playBomb = true;

            /** Show elements */
            if (this.refs.userControl.state.bombSelected) {
                this.state.sound.bomb.play();

                var bombRadius = this.getBombRadius(coords[0], coords[1]);
                for (var i = 0, j = bombRadius.length; i < j; i++) {
                    this.show(bombRadius[i][0], bombRadius[i][1], i, j);
                }

                /** remove BOMB from activePlayer */
                this.refs.userControl.refs[activePlayer].setState({
                    haveBomb: false
                });
            } else {
                this.show(coords[0], coords[1]);
            }

            /** Mine score handling */
            if (this.state.foundUserMineCache) {
                this.refs.userControl.setState({
                    mines: this.refs.userControl.state.mines - this.state.foundUserMineCache,
                    foundMines: true
                }, () => {
                    /** because of CSS animation in .found-mine */
                    setTimeout(() => this.refs.userControl.setState({foundMines: false}), 500);

                    /** add the found mines to the active Player */
                    this.refs.userControl.refs[activePlayer].setState({
                        mines: this.refs.userControl.refs[activePlayer].state.mines + this.state.foundUserMineCache
                    });
                });
            }

            /** Reset BOMB status */
            if (this.refs.userControl.state.bombSelected) {
                /** reset bomb selected status */
                this.refs.userControl.setState({bombSelected: false});

                /** clear cache, reset symbols */
                this.bombClear();
            }
        }
    }

    /**
     * On Hover when you want to drop BOMB
     * Target grid field
     * @param coords
     */
    onHoverGridField(coords) {
        if (this.refs.userControl.state.bombSelected) {
            var activePlayer = this.refs.userControl.state.activePlayer ? 'blue' : 'red';

            if (activePlayer === this.state.webPlayer) {
                /** clear cache, reset symbols */
                this.bombClear();

                /** new cache && field activate */
                this.bombCreate(coords[0], coords[1]);
            } else {
                this.refs.userControl.setState({bombSelected: false});
            }
        }
    }

    overlayClass() {
        return 'game-overlay' + ( this.state.overlay ? '' : ' hide' );
    }

    renderGridFields() {
        if (this.state.grid) {
            for (let i = 0, j = this.state.grid.length; i < j; i++) {
                for (let k = 0, l = this.state.grid[i].length; k < l; k++) {
                    this.state.gridFields.push(
                        <GridField row={i}
                                   col={k}
                                   ref={this.refString(i, k)}
                                   key={i + k * Math.random() * 0.5}
                                   handleHoverOn={this.onHoverGridField.bind(this, [i, k])}
                                   onClick={this.props.onClick.bind(null, [i, k])}/>
                    );
                }
            }
        }
    }

    render() {
        /** Render the grid fields just one time in one party #12 */
        this.state.renderGridFields && this.renderGridFields();
        this.state.renderGridFields = false;

        return (
            <div className="game-wrapper">
                <div className={this.overlayClass()}>
                    <div className="game-overlay-window">
                        <h1>{this.state.overlayTitle}</h1>
                        <h2>{this.state.overlaySubTitle}</h2>
                    </div>
                </div>
                <UserControl ref="userControl"
                             resign={this.props.resign}
                             webPlayer={this.state.webPlayer}
                             bombClear={this.bombClear.bind(this)}/>
                <div className="grid-container">
                    <div className="grid"> {this.state.gridFields} </div>
                </div>
            </div>
        );
    }
}

export default GridControl;
