import * as React from "react";

import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";
import Popover from "@material-ui/core/Popover";
import LinearProgress from "@material-ui/core/LinearProgress";
import CircularProgress from "@material-ui/core/CircularProgress";
import Paper from "@material-ui/core/Paper";
import Tooltip from "@material-ui/core/Tooltip";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";

import CloseIcon from "@material-ui/icons/Close";

import { withRouter } from "react-router-dom";

import { IVocab, IReviewCard, vocabToReviewCard, reviewQTypeToStr } from "../models/vocab";
import { ReviewType, IReviewMetadata } from "../models/review";

import { levW } from "../algorithms/levenshtein";
import { LEVENSHTEIN_MAX_DISTANCE } from "../config";

import { Queue } from "../utils/queue";

interface IProps {
    levelId?: number;
    vocabByLevel?: (level: number) => Promise<IVocab[]>;
    vocabByQueue?: () => Promise<IVocab[]>;
    setLastReview: (meta: IReviewMetadata, sm2: any) => void;
    reviewType: ReviewType;

    history: any;

    dialogOpen: boolean;
    loading: boolean;
    vocab: IVocab[];
    current: IReviewCard;
    metadata: IReviewMetadata;
    popoverOpen: boolean;
    popoverText: string;
    popoverColor: string;
    popoverTextColor: string;

    setReviewDialog: (state: boolean) => void;
    setSummary: (state: boolean) => void;
    setPopover: (state: boolean, text: string, color: string, textColor: string) => void;
    drawerButtonState: (state: boolean) => void;
    setReview: (curent: IReviewCard, meta: IReviewMetadata) => void;
    setLoading: (state: boolean) => void;
}

const ReviewPageWithRouter = withRouter(
    class ReviewPage extends React.Component<IProps> {
        private vocab: IVocab[] = [];
        private reviewQueue: Queue<IReviewCard> = new Queue();
        // Used for positioning the popover
        private buttonRef: HTMLButtonElement;
        private inputRef: HTMLInputElement;
        // Mapping: Vocab Id -> Correctly answered
        private sm2_metadata: any = {};

        constructor(props: any) {
            super(props);

            // Hide the drawer button
            this.props.drawerButtonState(false);

            const vocToQueue = () => {
                this.vocab.forEach((vocab) => {
                    vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
                });
                this.props.setReview(this.reviewQueue.dequeue(), {
                    correct: 0,
                    wrong: 0,
                });
                this.props.setLoading(false);
            };

            // Get the correct vocabulary
            const { reviewType, vocabByLevel, levelId, vocabByQueue } = this.props;

            // Just to make TSC shut up
            const noopPromise = () => {
                return new Promise<IVocab[]>((res, rej) => {
                    rej([]);
                });
            };
            const vocabByLevelW = vocabByLevel || noopPromise;
            const vocabByQueueW = vocabByQueue || noopPromise;
            const getVocab = {
                [ReviewType.LEVEL]: () => vocabByLevelW(levelId),
                [ReviewType.QUEUE]: () => vocabByQueueW(),
            }[reviewType];

            getVocab().then((res: IVocab[]) => {
                this.vocab = res;
                vocToQueue();
            });
        }

        openDialog = () => {
            this.props.setReviewDialog(true);
        }
        closeDialog = () => {
            this.props.setReviewDialog(false);
        }

        cancelReview = () => {
            this.closeDialog();

            // Show the drawer button again
            this.props.drawerButtonState(true);

            this.props.history.push("/dashboard");
        }

        increaseMeta = (correct: number, wrong: number): IReviewMetadata => {
            const { metadata } = this.props;

            return {
                wrong: metadata.wrong + wrong,
                correct: metadata.correct + correct,
            };
        }

        vocabFromId = (id: number): IVocab | {} => {
            return this.vocab.find((el) => el.id === this.props.current.id);
        }

        // When a vocabulary item has been answered, we need to update
        // the group's SuperMemo2-data.
        // We update based on the following State-Machine: (C = Correct; W = Wrong)
        // Answer | Group | New group
        //        |       |   state
        // -------+-------+----------
        //    C   |   C   |  C
        //    C   |   W   |  W
        //    W   |   C   |  W
        //    W   |   W   |  W
        // (1)C   |   -   |  C
        // (2)W   |   -   |  W
        // @correct: Was the answer given correct?
        updateGroupSM2 = (correct: boolean) => {
            const { id } = this.props.current;
            switch (correct) {
                case true:
                    // Case (1)
                    if (!(id in this.sm2_metadata)) {
                        this.sm2_metadata[id] = true;
                        break;
                    }

                    switch (this.sm2_metadata[id]) {
                        case true:
                            this.sm2_metadata[id] = true;
                            break;
                        case false:
                            this.sm2_metadata[id] = false;
                            break;
                    }
                    break;
                case false:
                    // We don't need to explicitly catch case (2), as we set
                    // anything, that was incorrectly answered to false.
                    this.sm2_metadata[id] = false;
                    break;
            }
        }

        checkInput = () => {
            // Check if the given answer is somewhere in the german words
            const input = this.inputRef.value || "";

            // Map all possible answers to lowercase ( => ignore casing)
            const answers = this.props.current.answers.map((el) => el.toLowerCase());
            // Calculate the distances to all possible answers
            const dists = answers.map((el) => levW(input.toLowerCase(), el));
            // Find the lowest distance
            const minDist = Math.min(...dists);

            // Check if the user's answer was correct
            if (minDist === 0) {
                this.updateGroupSM2(true);

                // TODO: Show it's correct?
                // Show the next vocab word
                if (this.reviewQueue.size() === 0) {
                    // Update the metadata
                    this.props.setReview(this.props.current, this.increaseMeta(1, 0));
                    this.props.setLastReview(this.props.metadata, this.sm2_metadata);
                    this.props.setLoading(true);

                    // Show the drawer button again
                    this.props.drawerButtonState(true);

                    // Go to the summary screen
                    this.props.history.push("/review/summary");
                } else {
                    // Increase the vocab
                    this.props.setReview(this.reviewQueue.dequeue(), this.increaseMeta(1, 0));
                    this.inputRef.value = "";

                    // TODO(?): Show a snackbar for showing the updated score
                }
            } else if (minDist <= LEVENSHTEIN_MAX_DISTANCE) {
                this.props.setPopover(true, "Das war fast richtig", "yellow", "black");
            } else {
                // Find the IVocab item
                /* const vocab = this.vocabFromId(this.props.current.id);
                 * if (vocab) {
                 *     // Re-Add the vocabulary item to the review queue
                 *     // TODO: Only re-add when it when it's not re-queued
                 *     // vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
                 * } else {
                 *     console.log("[ReviewPage::checkInput] Could not find IVocab item for wrong IReviewCard");
                 * }
                 */
                // Update the metadata
                this.updateGroupSM2(false);
                this.props.setReview(this.props.current, this.increaseMeta(0, 1));
                this.props.setPopover(true, "Das war nicht richtig", "red", "white");
            }
        }

        render() {
            if (this.props.loading) {
                return <div>
                    <Grid
                        container
                        spacing={0}
                        direction="column"
                        alignItems="center"
                        justify="center"
                        style={{ minHeight: '100vh' }}>
                        <Grid item xs={12}>
                            <Paper className="paper">
                                <Grid container direction="column" spacing={8}>
                                    <CircularProgress />
                                </Grid>
                            </Paper>
                        </Grid>
                    </Grid>

                </div>;
            }


            const { question, qtype } = this.props.current;
            const questionTitle = `${question} (${reviewQTypeToStr(qtype)})`;
            // TODO:
            const progress = 50;

            return <div>
                <Grid container justify="center">
                    <Grid item style={{ width: "100%" }}>
                        <Card>
                            <CardContent>
                                <Grid container direction="column">
                                    <center>
                                        <Typography variant="display2">
                                            {questionTitle}
                                        </Typography>
                                    </center>
                                    <TextField
                                        margin="normal"
                                        fullWidth={true}
                                        inputRef={node => this.inputRef = node}
                                        onKeyPress={(ev) => {
                                            // Allow checking of the answer by pressing Enter
                                            if (ev.key === "Enter")
                                                this.checkInput();
                                        }} />
                                    <LinearProgress
                                        variant="determinate"
                                        value={progress} />
                                    <Popover
                                        open={this.props.popoverOpen}
                                        anchorOrigin={{
                                            vertical: "center",
                                            horizontal: "center"
                                        }}
                                        transformOrigin={{
                                            vertical: "bottom",
                                            horizontal: "center"
                                        }}
                                        anchorEl={this.buttonRef}
                                        onClose={() => this.props.setPopover(false, "", "", "")}
                                        PaperProps={{
                                            style: {
                                                backgroundColor: this.props.popoverColor,
                                                padding: 10,
                                                color: this.props.popoverTextColor,
                                            }
                                        }}>
                                        <Typography
                                            variant="button"
                                            color="inherit">
                                            {this.props.popoverText}
                                        </Typography>
                                    </Popover>
                                    <Button
                                        onClick={this.checkInput}
                                        buttonRef={node => this.buttonRef = node}>
                                        Prüfen
                                    </Button>
                                </Grid>
                            </CardContent>
                        </Card>
                    </Grid>
                </Grid>
                <Tooltip
                    title="Abbrechen"
                    placement="top">
                    <Button
                        variant="fab"
                        color="primary"
                        className="review-fab"
                        onClick={this.openDialog}>
                        <CloseIcon />
                    </Button>
                </Tooltip>
                <Dialog
                    open={this.props.dialogOpen}
                    onClose={this.closeDialog}>
                    <DialogTitle>Willst du die Wiederholung abbrechen?</DialogTitle>
                    <DialogContent>
                        <DialogContentText>
                            Wenn du jetzt abbricht, dann geht dein in dieser Wiederholung gesammelte Fortschritt
                            verloren.
                        </DialogContentText>
                    </DialogContent>
                    <DialogActions>
                        <Button
                            onClick={this.closeDialog}>
                            Zurück zur Wiederholung
                        </Button>
                        <Button
                            onClick={this.cancelReview}>
                            Abbrechen
                        </Button>
                    </DialogActions>
                </Dialog>
            </div>;
        }
    }
);
export default ReviewPageWithRouter;