import * as React from "react";
import {
    GameWasm,
    Letter as LetterData,
    MyResult,
    PlayedTile,
    PlayerAndScore,
    ScoreResult,
    Tray, TurnAction, TurnAdvanceResult
} from "../../pkg/word_grid";
import {
    HighlightableLetterData,
    LocationType,
    matchCoordinate,
    mergeTrays,
    PlayableLetterData,
    Settings,
    TileDispatchAction,
    TileDispatchActionType
} from "./utils";
import {useEffect, useMemo, useReducer, useRef, useState} from "react";
import {TileExchangeModal} from "./TileExchange";
import {Grid, Scores, TileTray} from "./UI";

function addLogInfo(existingLog: React.JSX.Element[], newItem: React.JSX.Element) {
    newItem = React.cloneElement(newItem, { key:  existingLog.length })
    existingLog.push(newItem);
    return existingLog.slice();
}

export function Game(props: {
    wasm: GameWasm,
    settings: Settings,
    end_game_fn: () => void,
}) {

    const cellTypes = useMemo(() => {
        return props.wasm.get_board_cell_types();
    }, []);

    const [confirmedScorePoints, setConfirmedScorePoints] = useState<number>(-1);

    function movePlayableLetters(playerLetters: PlayableLetterData[], update: TileDispatchAction) {

        if(update.action === TileDispatchActionType.RETRIEVE) {

            let tray: Tray = props.wasm.get_tray("Player");

            return mergeTrays(playerLetters, tray.letters);
        } else if (update.action === TileDispatchActionType.MOVE) {

            let startIndex = matchCoordinate(playerLetters, update.start);
            let endIndex = matchCoordinate(playerLetters, update.end);

            if(startIndex != null) {
                let startLetter = playerLetters[startIndex];
                startLetter.location = update.end.location;
                startLetter.index = update.end.index;
            }

            if(endIndex != null) {
                let endLetter = playerLetters[endIndex];
                endLetter.location = update.start.location;
                endLetter.index = update.start.index;
            }

            setConfirmedScorePoints(-1);

            return playerLetters.slice();
        } if (update.action === TileDispatchActionType.SET_BLANK) {
            const blankLetter = playerLetters[update.blankIndex];
            if(blankLetter.text !== update.newBlankValue) {
                blankLetter.text = update.newBlankValue;
                if (blankLetter.location == LocationType.GRID) {
                    setConfirmedScorePoints(-1);
                }
            }
            return playerLetters.slice();
        } else if (update.action === TileDispatchActionType.RETURN) {
            return mergeTrays(playerLetters, playerLetters);
        } else {
            console.error("Unknown tray update");
            console.error({update});
        }

    }


    function exchangeFunction(selectedArray: Array<boolean>) {

        const result: MyResult<TurnAction | string> = props.wasm.exchange_tiles(selectedArray);
        console.log({result});

        if(result.response_type === "ERR") {
            logDispatch(<div><em>{(result.value as string)}</em></div>);
        } else {
            const action = result.value as {type: "ExchangeTiles"; tiles_exchanged: number;};
            logDispatch(<div><em>You exchanged {action.tiles_exchanged} tiles.</em></div>);
            setTurnCount(turnCount + 1);
        }

    }

    function addWordFn(word: string) {
        props.wasm.add_word(word);
        logDispatch(<div><em>{word} was added to dictionary.</em></div>);
    }

    function runAI() {
        const result: TurnAdvanceResult = props.wasm.advance_turn();
        console.info({result});
        setTurnCount(turnCount + 1);
    }


    const [playerLetters, trayDispatch] = useReducer(movePlayableLetters, []);
    const [logInfo, logDispatch] = useReducer(addLogInfo, []);

    const [turnCount, setTurnCount] = useState<number>(1);
    const playerAndScores: PlayerAndScore[] = useMemo(() => {
        return props.wasm.get_scores();
    }, [turnCount]);

    const [boardLetters, setBoardLetters] = useState<HighlightableLetterData[]>(() => {
        const newLetterData = [] as HighlightableLetterData[];
        for(let i=0; i<15*15; i++) {
            newLetterData.push(undefined);
        }
        return newLetterData;

    });

    useEffect(() => {
        const newLetterData = props.wasm.get_board_letters() as HighlightableLetterData[];

        // we need to go through and set 'highlight' field to either true or false
        // it will always be false if the player that just went was the AI
        // TODO - build in support for multiple other players
        for(let i=0; i<newLetterData.length; i++) {
            const newLetter = newLetterData[i];
            if(boardLetters[i] === undefined && newLetter !== undefined && playerTurnName == props.settings.playerName) {
                newLetter.highlight = true;
            } else if(newLetter !== undefined) {
                newLetter.highlight = false;
            }
        }
        setBoardLetters(newLetterData);
    }, [turnCount]);

    const playerTurnName = useMemo(() => {
        return props.wasm.get_current_player();
    }, [turnCount]);


    useEffect(() => {
        logDispatch(<h4>Turn {turnCount}</h4>);
        logDispatch(<div>{playerTurnName}'s turn</div>)
        setConfirmedScorePoints(-1);
        trayDispatch({action: TileDispatchActionType.RETRIEVE});
        if(playerTurnName != props.settings.playerName) {
            runAI();
        }

    }, [turnCount]);

    const logDivRef = useRef(null);

    const [isTileExchangeOpen, setIsTileExchangeOpen] = useState<boolean>(false);

    useEffect(() => {
        // Effect is to keep the log window scrolled down
        if (logDivRef.current != null) {
            logDivRef.current.scrollTo(0, logDivRef.current.scrollHeight); // scroll down
        }
    }, [logInfo])


    return <>
        <TileExchangeModal
            playerLetters={playerLetters}
            isOpen={isTileExchangeOpen}
            setOpen={setIsTileExchangeOpen}
            exchangeFunction={exchangeFunction}
        />
        <div className="board-log">
            <Grid cellTypes={cellTypes} playerLetters={playerLetters} boardLetters={boardLetters} dispatch={trayDispatch}/>
            <div className="message-log">
                <Scores playerScores={playerAndScores}/>
                <div className="log" ref={logDivRef}>
                    {logInfo}
                </div>
            </div>
        </div>

        <TileTray letters={playerLetters} trayLength={props.settings.trayLength} dispatch={trayDispatch}/>
        <button onClick={() => {
            const playedTiles = playerLetters.map((i) => {
                if (i === undefined) {
                    return null;
                }

                if (i.location === LocationType.GRID) {
                    let result: PlayedTile = {
                        index: i.index,
                        character: undefined
                    };
                    if (i.is_blank) {
                        result.character = i.text;
                    }

                    return result;
                }

                return null;
            });

            const result: MyResult<{ type: "PlayTiles"; result: ScoreResult } | string> = props.wasm.receive_play(playedTiles, confirmedScorePoints > -1);
            console.log({result});

            if(result.response_type === "ERR") {
                const message = result.value as string;
                if (message.endsWith("is not a valid word")) {
                    // extract out word
                    const word = message.split(" ")[0];
                    logDispatch(<div><em>{message}</em><AddWordButton word={word} addWordFn={addWordFn} /></div>);
                } else {
                    logDispatch(<div><em>{message}</em></div>);
                }

            } else {

                const score_result = (result.value as { type: "PlayTiles"; result: ScoreResult }).result;
                const total_points = score_result.total;

                let msg: string;
                if (confirmedScorePoints > -1) {
                    msg = `You scored ${total_points} points.`;
                    setTurnCount(turnCount + 1);
                }


                logDispatch(<div><em>{msg}</em></div>);

                setConfirmedScorePoints(total_points);
            }



        }}>{confirmedScorePoints > -1 ? `Score ${confirmedScorePoints} points ✅` : "Check"}</button>
        <button
            onClick={() => {
                trayDispatch({action: TileDispatchActionType.RETURN}); // want all tiles back on tray for tile exchange
                setIsTileExchangeOpen(true);
            }}
        >Open Tile Exchange</button>
        <button onClick={() => {
            trayDispatch({action: TileDispatchActionType.RETURN});
        }}>Return Tiles</button>
        <button onClick={() => {
            props.wasm.skip_turn();
            logDispatch(<div><em>Player skipped their turn</em></div>);
            setTurnCount(turnCount + 1);
        }}>Skip Turn</button>
    </>;

}

function AddWordButton(props: {word: string, addWordFn: (x: string) => void}) {
    const [isClicked, setIsClicked] = useState<boolean>(false);

    return <button
        disabled={isClicked}
        onClick={() => {
            setIsClicked(true);
            props.addWordFn(props.word);
    }}>
        Add to dictionary
    </button>
}