import { CommonMancalaGame, MancalaGame, Pit } from 'mancala.js';
import * as React from 'react';
import { FunctionComponent, useState } from 'react';
import { useNavigate, useParams } from 'react-router';
import { Link } from 'react-router-dom';
import { v4 } from 'uuid';
import PitAnimator from '../animation/PitAnimator';
import BoardToolbar from '../components/board/BoardToolbar';
import BoardView from '../components/board/BoardView';
import Button from '../components/Button';
import HeaderBar from '../components/headerbar/HeaderBar';
import HeaderbarIcon from '../components/headerbar/HeaderbarIcon';
import HeaderbarTitle from '../components/headerbar/HeaderbarTitle';
import ThemeSwitchMenu from '../components/headerbar/ThemeSwitchMenu';
import InfoPanel from '../components/InfoPanel';
import LoadingComponent from '../components/LoadingComponent';
import PageContainer from '../components/PageContainer';
import Row from '../components/Row';
import UserStatus from '../components/UserStatus';
import { channel_on_game_update, channel_on_game_crashed, channel_on_game_user_leave, channel_on_user_connection_change, channel_leave_game, channel_game_move } from '../const/channel_names';
import { Context } from '../context/context';
import useWindowDimensions from '../hooks/useWindowDimensions';
import { ConnectionState } from '../models/ConnectionState';
import { GameMove } from '../models/GameMove';
import { LoadingState } from '../models/LoadingState';
import { UserConnectionInfo } from '../models/UserConnectionInfo';
import { Theme } from '../theme/Theme';
import { getColorByBrightness } from '../util/ColorUtil';
import BoardViewModel from '../viewmodel/BoardViewModel';
import Center from '../components/Center';

const GamePage: FunctionComponent<{
    context: Context,
    userKey?: string,
    theme: Theme,
    connectionState: ConnectionState
}> = ({ context, userKey, theme, connectionState }) => {
    let params = useParams<{ gameId: string }>();

    const [game, setGame] = useState<MancalaGame | undefined>(undefined);

    const [crashMessage, setCrashMessage] = useState<string | undefined>(undefined);

    const [userKeyWhoLeave, setUserKeyWhoLeave] = useState<string | undefined>(undefined);

    const [boardViewModel, setBoardViewModel] = useState<BoardViewModel | undefined>(undefined);

    const [boardId, setBoardId] = useState<string>("-1");

    const [pitAnimator, setPitAnimator] = useState<PitAnimator | undefined>(undefined);

    // It is a flag for ongoing action such as send game move.
    // We have to block future actions if there is an ongoing action.
    const [hasOngoingAction, setHasOngoingAction] = useState<boolean>(false);

    const [isOpponentOnline, setIsOpponentOnline] = useState<boolean>(false);

    const { height, width } = useWindowDimensions();

    const navigate = useNavigate();

    const [gameLoadingState, setLoadingStateGame] = useState<LoadingState<MancalaGame>>(LoadingState.Unset());

    const onGameUpdate = (pitAnimator: PitAnimator, message: Object) => {
        const newGame: CommonMancalaGame = message as CommonMancalaGame;
        const mancalaGame = MancalaGame.createFromMancalaGame(newGame);
        setGame(mancalaGame);
        pitAnimator.setUpdatedGame(mancalaGame);
        setHasOngoingAction(false);
    }
    const onGameCrashed = (message: any) => {
        const newCrashMessage = message as string;
        console.error("on_game_crash");
        console.error(newCrashMessage);
        setCrashMessage(newCrashMessage);
    }
    const onGameUserLeave = (message: any) => {
        const userKeyWhoLeave = message;
        setUserKeyWhoLeave(userKeyWhoLeave);
        setHasOngoingAction(false);
    };
    const onUserConnectionChange = (message: any) => {
        const userConnectionInfo = message as UserConnectionInfo;
        //todo: change this when implementing watch the game feature
        setIsOpponentOnline(userConnectionInfo.isOnline);
    };

    const listenMessages = (pitAnimator: PitAnimator) : () => void => {
        const _onGameUpdate = (message: object) => onGameUpdate(pitAnimator, message);
        context.rtmt.listenMessage(channel_on_game_update, _onGameUpdate);
        context.rtmt.listenMessage(channel_on_game_crashed, onGameCrashed);
        context.rtmt.listenMessage(channel_on_game_user_leave, onGameUserLeave);
        context.rtmt.listenMessage(channel_on_user_connection_change, onUserConnectionChange)
        return () => {
            context.rtmt.unlistenMessage(channel_on_game_update, _onGameUpdate);
            context.rtmt.unlistenMessage(channel_on_game_crashed, onGameCrashed);
            context.rtmt.unlistenMessage(channel_on_game_user_leave, onGameUserLeave);
            context.rtmt.unlistenMessage(channel_on_user_connection_change, onUserConnectionChange);
        }
    };

    const updateBoardViewModel = (boardViewModel: BoardViewModel) => {
        boardViewModel.id = v4();
        setBoardId(boardViewModel.id);
        setBoardViewModel(boardViewModel);
    };

    const getBoardIndex = (index: number) => {
        if (!game) return -1;
        if (userKey === game.player2Id) return index + game.board.pits.length / 2;
        return index;
    };

    const getOpponentId = () => game?.player1Id === userKey ? game?.player2Id : game?.player1Id;

    const checkHasAnOngoingAction = () => hasOngoingAction;

    const onLeaveGameClick = () => {
        context.rtmt.sendMessage(channel_leave_game, {});
    };

    const onNewGameClick = () => {
        navigate("/loby")
    };

    const onPitSelect = (index: number, pit: Pit) => {
        if (checkHasAnOngoingAction()) {
            return;
        }
        setHasOngoingAction(true);
        if (!boardViewModel) return;
        //TODO : stoneCount comes from view model!
        if (pit.stoneCount === 0) {
            //TODO : warn user
            return;
        }
        boardViewModel.pits[getBoardIndex(index)].pitColor =
            context.themeManager.theme.pitSelectedColor;
        updateBoardViewModel(boardViewModel);
        const gameMove: GameMove = { index: index };
        context.rtmt.sendMessage(channel_game_move, gameMove);
    };

    React.useEffect(() => {
        let pitAnimator: PitAnimator | undefined;
        let unlistenMessages: ()=>void;
        setLoadingStateGame(LoadingState.Loading())
        context.gameStore.get(params.gameId!!).then((game) => {
            if (game) {
                setGame(game);
                setHasOngoingAction(false);
                pitAnimator = new PitAnimator(context, updateBoardViewModel);
                pitAnimator.setNewGame(game);
                setPitAnimator(pitAnimator);
                unlistenMessages = listenMessages(pitAnimator);
                setLoadingStateGame(LoadingState.Loaded({ value: game }))
            } else {
                setLoadingStateGame(LoadingState.Error({ errorMessage: context.texts.GameNotFound }))
            }
        })
        return () => {
            unlistenMessages?.();
            pitAnimator?.dispose();
        };
    }, []);

    const textColorOnAppBar = getColorByBrightness(
        context.themeManager.theme.appBarBgColor,
        context.themeManager.theme.textColor,
        context.themeManager.theme.textLightColor
    );
    const renderNewGameBtn = userKeyWhoLeave || !game || (game && game.state == "ended");
    const showBoardView = game && boardViewModel && userKey && true;
    const opponentUser = { id: getOpponentId() || "0", name: "Anonymous", isOnline: isOpponentOnline, isAnonymous: true };
    const user = { id: userKey || "1", name: "Anonymous", isOnline: connectionState === "connected", isAnonymous: true };

    const isMobile = width < 600;

    return (
        <PageContainer theme={theme!}>
            <HeaderBar color={theme?.appBarBgColor}>
                <Row>
                    <Link style={{ textDecoration: 'none' }} to={"/"}>
                        <HeaderbarIcon />
                    </Link>
                    <Link style={{ textDecoration: 'none' }} to={"/"}>
                        <HeaderbarTitle title={context.texts.Mancala} color={textColorOnAppBar} />
                    </Link>
                </Row>
                <Row>
                    <ThemeSwitchMenu context={context} textColor={textColorOnAppBar} />
                    <Button
                        context={context}
                        color={context.themeManager.theme.pitColor}
                        text={renderNewGameBtn ? context.texts.NewGame : context.texts.Leave}
                        onClick={renderNewGameBtn ? onNewGameClick : onLeaveGameClick} />
                </Row>
            </HeaderBar>
            <BoardToolbar style={{ justifyContent: "center" }} visible={showBoardView && isMobile || false}>
                <InfoPanel
                    style={{ marginTop: "0.5rem", marginBottom: "0.5rem" }}
                    context={context}
                    game={game}
                    crashMessage={crashMessage}
                    userKey={userKey}
                    userKeyWhoLeave={userKeyWhoLeave} />
            </BoardToolbar>
            <BoardToolbar style={{ alignItems: "flex-end" }} visible={showBoardView || false}>
                <UserStatus style={{
                    marginBottom: "0.5rem", marginLeft: "6%", maxWidth: isMobile ? "40vw" : "30vw",
                    width: isMobile ? "40vw" : "30vw"
                }} context={context} layoutMode="left" user={opponentUser} visible={showBoardView || false} />
                <InfoPanel
                    style={{
                        marginTop: "0.5rem", marginBottom: "0.5rem",
                    }}
                    context={context}
                    game={game}
                    crashMessage={crashMessage}
                    userKey={userKey}
                    userKeyWhoLeave={userKeyWhoLeave}
                    visible={!isMobile} />
                <UserStatus style={{
                    marginBottom: "0.5rem", marginRight: "6%", maxWidth: isMobile ? "40vw" : "30vw",
                    width: isMobile ? "40vw" : "30vw"
                }} context={context} layoutMode="right" user={user} visible={showBoardView || false} />
            </BoardToolbar>
            {showBoardView && (
                <BoardView
                    userKey={userKey}
                    game={game}
                    boardId={boardId}
                    boardViewModel={boardViewModel}
                    context={context}
                    onPitSelect={onPitSelect} />
            )}
            <Center>
                <LoadingComponent context={context} loadingState={gameLoadingState}></LoadingComponent>
            </Center>
        </PageContainer>
    );
}

export default GamePage;