import React from 'react';
import MGrid from '@material-ui/core/Grid';
import { Theme, withStyles, StyleRules } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableRow from '@material-ui/core/TableRow';
import TableCell from '@material-ui/core/TableCell';
import Button from '@material-ui/core/Button';
import Slider from '@material-ui/lab/Slider';
import Grid from './Grid';

const styles = (theme: Theme): StyleRules => ({
    inputLabel: {
        fontSize: 16,
        paddingRight: 0,
        textAlign: 'right'
    },
    inputValue: {
        textAlign: 'left'
    },
    buttonSpacer: {
        marginBottom: theme.spacing.unit * 4,
    },
    bottomButtons: {
        marginTop: 10,
        textAlign: 'center',
        minWidth: 650
    },
    slider: {
        padding: '22px 0px',
    }
});

interface SnowProps {
    classes: {
        inputLabel: string,
        inputValue: string,
        buttonSpacer: string,
        bottomButtons: string,
        slider: string
    }
}

function getRandomInt(max: number) {
    return Math.floor(Math.random() * Math.floor(max));
}

function getRandomSubarray(arr: number[], size: number) {
    let shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
    while (i-- > min) {
                index = Math.floor((i + 1) * Math.random());
                temp = shuffled[index];
                shuffled[index] = shuffled[i];
                shuffled[i] = temp;
            }
    return shuffled.slice(min);
}

class Snow extends React.Component<SnowProps> {

    static genMatrix(n = 20) {
        let d = [];
        for (let i = 0; i < n; i++)
        {
            let r = [];
            for (let j = 0; j < n; j++)
                r.push({d: [0, 0], col: getRandomInt(2)});
            d.push(r);
        }
        return d;
    }

    state = {
        colorMatrix: Snow.genMatrix(),
        k: 10,
        alpha: 8,
        nodesPerTick: 10,
        maxInactiveTicks: 200,
        ticking: false,
        simulationSpeed: 100
    };

    config = {
        k: 10,
        alpha: 8,
        nodesPerTick: 10,
        maxInactiveTicks: 200,
        inactiveTicks: 0
    };

    getNodeState(n: number, u: number) {
        let r = Math.floor(u / n);
        let c = u % n;
        return this.state.colorMatrix[r][c];
    }

    setNodeState(n: number, u: number, s: {d: number[], col: number}) {
        let r = Math.floor(u / n);
        let c = u % n;
        let m = [...this.state.colorMatrix];
        m[r][c] = s;
        this.setState({colorMatrix: m});
    }

    tick(n: number, m: number) {
        let N = n * n;
        let active = false;
        for (let i = 0; i < m; i++)
        {
            let u = getRandomInt(N);
            let peers = [];
            for (let j = 0; j < N; j++)
                if (j != u) peers.push(j);
            let sample = getRandomSubarray(peers, this.config.k);
            let cnt = [0, 0];
            sample.forEach((v) => {
                let ss = this.getNodeState(n, v);
                cnt[ss.col]++;
            });
            let s = this.getNodeState(n, u);
            for (let c = 0; c < 2; c++)
            {
                if (cnt[c] > this.config.alpha)
                {
                    s.d[c]++;
                    if (s.d[c] > s.d[s.col])
                    {
                        if (s.col != c) active = true;
                        s.col = c;
                        this.setNodeState(n, u, s);
                    }
                }
            }
        }
        return active;
    }

    pauseTick() {
        this.setState({ticking: false});
    }

    startTick() {
        this.config.alpha = this.state.alpha;
        this.config.k = this.state.k;
        this.config.nodesPerTick = this.state.nodesPerTick;
        this.config.inactiveTicks = 0;
        this.config.maxInactiveTicks = this.state.maxInactiveTicks;
        this.autoTick();
    }

    autoTick() {
        this.setState({ticking: true});
        setTimeout(() => {
            let active = this.tick(20, this.config.nodesPerTick);
            console.log(active);
            if (!active)
            {
                if (++this.config.inactiveTicks > this.config.maxInactiveTicks)
                {
                    this.pauseTick();
                    return;
                }
            }
            else
                this.config.inactiveTicks = 0;
            if (this.state.ticking) this.autoTick();
        }, 1000 / this.state.simulationSpeed);
    }

    reset() {
        this.setState({
            colorMatrix: Snow.genMatrix(),
            ticking: false
        });
    }

    render() {
        const { classes } = this.props;

        return (
            <MGrid container spacing={16} style={{minWidth: 600, maxWidth: 1600}}>
                <MGrid item lg={6} xs={12}>
                    <Grid data={this.state.colorMatrix} />
                </MGrid>
                <MGrid item lg={6} xs={12}>
                    <Table>
                    <TableBody>
                    <TableRow>
                        <TableCell className={classes.inputLabel}>
                        k =
                        </TableCell>
                        <TableCell>
                        <TextField
                            inputProps={{ className: classes.inputValue } as React.CSSProperties}
                            value={this.state.k}
                            disabled={this.state.ticking}
                            onChange={event => this.setState({k: event.target.value})}/>
                        </TableCell>
                    </TableRow>
                    <TableRow>
                        <TableCell className={classes.inputLabel}>
                        alpha =
                        </TableCell>
                        <TableCell>
                        <TextField
                            inputProps={{ className: classes.inputValue } as React.CSSProperties}
                            value={this.state.alpha}
                            disabled={this.state.ticking}
                            onChange={event => this.setState({alpha: event.target.value})}/>
                        </TableCell>
                    </TableRow>
                    <TableRow>
                        <TableCell className={classes.inputLabel}>
                        nodesPerTick =
                        </TableCell>
                        <TableCell>
                        <TextField
                            inputProps={{ className: classes.inputValue } as React.CSSProperties}
                            value={this.state.nodesPerTick}
                            disabled={this.state.ticking}
                            onChange={event => this.setState({nodesPerTick: event.target.value})}/>
                        </TableCell>
                    </TableRow>
                    <TableRow>
                        <TableCell className={classes.inputLabel}>
                        maxInactiveTicks =
                        </TableCell>
                        <TableCell>
                        <TextField
                            inputProps={{ className: classes.inputValue } as React.CSSProperties}
                            value={this.state.maxInactiveTicks}
                            disabled={this.state.ticking}
                            onChange={event => this.setState({maxInactiveTicks: event.target.value})}/>
                        </TableCell>
                    </TableRow>
                    <TableRow>
                    <TableCell className={classes.inputLabel}>
                        simulationSpeed
                    </TableCell>
                    <TableCell>
                    <Slider
                        classes={{ container: classes.slider }}
                        value={this.state.simulationSpeed}
                        min={1}
                        max={1000}
                        onChange={(_, value) => this.setState({simulationSpeed: value})} />
                    </TableCell>
                    </TableRow>
                    </TableBody>
                    </Table>
                    <div className={classes.buttonSpacer} />
                    <div className={classes.bottomButtons}>
                    <MGrid container spacing={16}>
                        <MGrid item lg={4} xs={12}>
                            <Button
                                variant="contained" color="primary"
                                onClick={event => this.startTick()}
                                disabled={this.state.ticking}>Start</Button>
                        </MGrid>
                        <MGrid item lg={4} xs={12}>
                            <Button
                                variant="contained" color="primary"
                                onClick={event => this.pauseTick()}
                                disabled={!this.state.ticking}>Pause</Button>
                        </MGrid>
                        <MGrid item lg={4} xs={12}>
                            <Button
                                variant="contained" color="primary"
                                onClick={event => this.reset()}>Reset</Button>
                        </MGrid>
                    </MGrid>
                    </div>
                </MGrid>
            </MGrid>
        );
    }
}

export default withStyles(styles)(Snow);
