import * as React from 'react'

import type { PJ } from '@lastres/pj'
import type { Location } from '@lastres/location'
import type { LogLine } from '@lastres/log-line'
import type { ActionHash } from '@lastres/action'
import type { TalkNPCs } from '@lastres/talk-npc'

import UpperPanel from '@lastres/components/upper-panel'
import BottomPanel from '@lastres/components/bottom-panel'
import PJSelectionMenu from '@lastres/components/pj-selection-menu'
import OutputPacketInit from '@lastres/output-packet/init'
import OutputPacketPing from '@lastres/output-packet/ping'
import InputPackets from '@lastres/input-packets'

export type SetWebsocketCallback = (websocket: WebSocket | null) => WebSocket | null
export interface GameProps {
    setSelectedPJ: (set: PJ | null) => void
    selectedPJ: PJ | null
    userWantsToCreatePJ: boolean
    setUserWantsToCreatePJ: (set: boolean) => void
    error: string | null
    setError: (set: string | null) => void
    websocket: WebSocket | null
    setWebsocket: (websocket: WebSocket | null | SetWebsocketCallback) => void
}

export default function Game (props: GameProps): JSX.Element {
    const selectedPJ = props.selectedPJ
    if (selectedPJ === null) {
        return (
            <>
                <PJSelectionMenu
                    setSelectedPJ={props.setSelectedPJ}
                    userWantsToCreatePJ={props.userWantsToCreatePJ}
                    setUserWantsToCreatePJ={props.setUserWantsToCreatePJ}
                    error={props.error}
                    setError={props.setError}/>
            </>
        )
    }
    const [teamPJs, setTeamPJs] = React.useState<PJ[] | null>(null)
    const [enemyTeamPJs, setEnemyTeamPJs] = React.useState<PJ[] | null>(null)
    const [isBattling, setIsBattling] = React.useState<boolean | null>(null)
    const [currentLocation, setCurrentLocation] = React.useState<Location | null>(null)
    const [connectedLocations, setConnectedLocations] = React.useState<Location[] | null>(null)
    const [logLines, setLogLines] = React.useState<LogLine[] | null>(null)
    const [error, setError] = React.useState<string | null>(null)
    const [scrollLog, setScrollLog] = React.useState<number | null>(null)
    const [movingTo, setMovingTo] = React.useState<Location | null>(null)
    const [remainingFrames, setRemainingFrames] = React.useState<number | null>(null)
    const [actionHash, setActionHash] = React.useState<ActionHash | null>(null)
    const [talkNPCs, setTalkNPCs] = React.useState<TalkNPCs | null>(null)
    const logPresentationRef = React.useRef<HTMLDivElement>(null)
    const websocket = props.websocket
    const setWebsocket = props.setWebsocket
    window.setTimeout(() => {
        setWebsocket((websocket): WebSocket | null => {
            if (websocket === null) {
                console.log('Opening websocket')
                const locationProtocol = window.location.protocol
                if (locationProtocol == null) {
                    return null
                }
                const protocol = locationProtocol.match(/https:/) != null ? 'wss' : 'ws'
                const webSocket = new WebSocket(`${protocol}://${window.location.host}/ws`)
                webSocket.onopen = () => {
                    new OutputPacketInit(selectedPJ.uuid).send(webSocket)
                    let interval: number = 0
                    interval = window.setInterval(() => {
                        if (webSocket.readyState === WebSocket.CONNECTING) {
                            return
                        }
                        if (webSocket.readyState === WebSocket.OPEN) {
                            new OutputPacketPing().send(webSocket)
                            return
                        }
                        window.clearInterval(interval)
                    }, 50000)
                }
                const inputPackets = new InputPackets(setTeamPJs,
                    setEnemyTeamPJs, setIsBattling,
                    setCurrentLocation, setConnectedLocations,
                    logLines, setLogLines, setError,
                    setScrollLog, logPresentationRef,
                    setMovingTo, setRemainingFrames,
                    setActionHash, setTalkNPCs)
                webSocket.onmessage = (event) => {
                    const packet = JSON.parse(event.data)
                    inputPackets.handle(packet)
                }
                webSocket.onerror = (event) => {
                    console.log(event)
                }
                webSocket.onclose = (event) => {
                    console.log('Websocket closed')
                    setWebsocket(null)
                }
                return webSocket
            }
            return websocket
        })
    }, 300)
    return (
        <>
            <UpperPanel teamPJs={teamPJs}
                enemyTeamPJs={enemyTeamPJs}
                isBattling={isBattling}
                currentLocation={currentLocation}
                connectedLocations={connectedLocations}
                logLines={logLines}
                websocket={websocket}
                logPresentationRef={logPresentationRef}
                movingTo={movingTo}
                remainingFrames={remainingFrames}/>
            <BottomPanel actionHash={actionHash}
                websocket={websocket}
                talkNPCs={talkNPCs}/>
        </>
    )
}
