import { CourtBall } from "./CourtBall"

import {
    ReactElement,
    RefObject,
    useCallback,
    useLayoutEffect,
    useState,
} from "react"
import CourtPlayer from "./CourtPlayer"
import { Player } from "../../tactic/Player"
import { Action, MovementActionKind } from "../../tactic/Action"
import RemoveAction from "../actions/RemoveAction"
import ArrowAction from "../actions/ArrowAction"

import BendableArrow, { Segment } from "../arrows/BendableArrow"
import { middlePos, NULL_POS, Pos, ratioWithinBase } from "../arrows/Pos"
import BallAction from "../actions/BallAction"
import { CourtObject } from "../../tactic/CourtObjects"

export interface BasketCourtProps {
    players: Player[]
    actions: Action[]
    objects: CourtObject[]

    renderAction: (a: Action, key: number) => ReactElement
    setActions: (f: (a: Action[]) => Action[]) => void

    onPlayerRemove: (p: Player) => void
    onPlayerChange: (p: Player) => void

    onBallRemove: () => void
    onBallMoved: (ball: DOMRect) => void

    courtImage: () => ReactElement
    courtRef: RefObject<HTMLDivElement>
}

export function BasketCourt({
    players,
    actions,
    objects,
    renderAction,
    setActions,
    onPlayerRemove,
    onPlayerChange,

    onBallMoved,
    onBallRemove,

    courtImage,
    courtRef,
}: BasketCourtProps) {
    function placeArrow(originRef: HTMLElement, arrowHead: DOMRect) {
        for (const player of players) {
            if (player.id == originRef.id) {
                continue
            }

            const playerBounds = document
                .getElementById(player.id)!
                .getBoundingClientRect()

            if (
                !(
                    playerBounds.top > arrowHead.bottom ||
                    playerBounds.right < arrowHead.left ||
                    playerBounds.bottom < arrowHead.top ||
                    playerBounds.left > arrowHead.right
                )
            ) {
                const targetPos = document
                    .getElementById(player.id)!
                    .getBoundingClientRect()

                const courtBounds = courtRef.current!.getBoundingClientRect()

                const start = ratioWithinBase(
                    middlePos(originRef.getBoundingClientRect()),
                    courtBounds,
                )
                const end = ratioWithinBase(middlePos(targetPos), courtBounds)

                const action: Action = {
                    fromPlayerId: originRef.id,
                    toPlayerId: player.id,
                    type: MovementActionKind.SCREEN,
                    moveFrom: start,
                    segments: [{ next: end }],
                }
                setActions((actions) => [...actions, action])
                return
            }
        }

        const action: Action = {
            fromPlayerId: originRef.id,
            type: MovementActionKind.MOVE,
            moveFrom: middlePos(originRef.getBoundingClientRect()),
            segments: [{ next: middlePos(arrowHead) }],
        }
        setActions((actions) => [...actions, action])
    }

    const [previewArrowOriginPos, setPreviewArrowOriginPos] =
        useState<Pos>(NULL_POS)
    const [isPreviewArrowEnabled, setPreviewArrowEnabled] = useState(false)

    const [previewArrowEdges, setPreviewArrowEdges] = useState<Segment[]>([])

    const updateActionsRelatedTo = useCallback((player: Player) => {
        const newPos = ratioWithinBase(
            middlePos(
                document.getElementById(player.id)!.getBoundingClientRect(),
            ),
            courtRef.current!.getBoundingClientRect(),
        )
        setActions((actions) =>
            actions.map((a) => {
                if (a.fromPlayerId == player.id) {
                    return { ...a, moveFrom: newPos }
                }

                if (a.toPlayerId == player.id) {
                    const segments = a.segments.toSpliced(
                        a.segments.length - 1,
                        1,
                        {
                            ...a.segments[a.segments.length - 1],
                            next: newPos,
                        },
                    )
                    return { ...a, segments }
                }

                return a
            }),
        )
    }, [])

    const [internActions, setInternActions] = useState<Action[]>([])

    useLayoutEffect(() => setInternActions(actions), [actions])

    return (
        <div
            className="court-container"
            ref={courtRef}
            style={{ position: "relative" }}>
            {courtImage()}

            {internActions.map((action, idx) => renderAction(action, idx))}

            {players.map((player) => (
                <CourtPlayer
                    key={player.id}
                    player={player}
                    onDrag={() => updateActionsRelatedTo(player)}
                    onChange={onPlayerChange}
                    onRemove={() => onPlayerRemove(player)}
                    parentRef={courtRef}
                    availableActions={(pieceRef) => [
                        <RemoveAction
                            key={1}
                            onRemove={() => onPlayerRemove(player)}
                        />,
                        <ArrowAction
                            key={2}
                            onHeadMoved={(headPos) => {
                                const baseBounds =
                                    courtRef.current!.getBoundingClientRect()
                                setPreviewArrowEdges([
                                    {
                                        next: ratioWithinBase(
                                            middlePos(headPos),
                                            baseBounds,
                                        ),
                                    },
                                ])
                            }}
                            onHeadPicked={(headPos) => {
                                const baseBounds =
                                    courtRef.current!.getBoundingClientRect()

                                setPreviewArrowOriginPos(
                                    ratioWithinBase(
                                        middlePos(
                                            pieceRef.getBoundingClientRect(),
                                        ),
                                        baseBounds,
                                    ),
                                )
                                setPreviewArrowEdges([
                                    {
                                        next: ratioWithinBase(
                                            middlePos(headPos),
                                            baseBounds,
                                        ),
                                    },
                                ])
                                setPreviewArrowEnabled(true)
                            }}
                            onHeadDropped={(headRect) => {
                                placeArrow(pieceRef, headRect)
                                setPreviewArrowEnabled(false)
                            }}
                        />,
                        player.hasBall && (
                            <BallAction
                                key={3}
                                onDrop={(ref) =>
                                    onBallMoved(ref.getBoundingClientRect())
                                }
                            />
                        ),
                    ]}
                />
            ))}

            {objects.map((object) => {
                if (object.type == "ball") {
                    return (
                        <CourtBall
                            onMoved={onBallMoved}
                            ball={object}
                            onRemove={onBallRemove}
                            key="ball"
                        />
                    )
                }
                throw new Error("unknown court object", object.type)
            })}

            {isPreviewArrowEnabled && (
                <BendableArrow
                    area={courtRef}
                    startPos={previewArrowOriginPos}
                    segments={previewArrowEdges}
                    //do nothing on change, not really possible as it's a preview arrow
                    onSegmentsChanges={() => {}}
                    //TODO place those values in constants
                    endRadius={17}
                    startRadius={26}
                />
            )}
        </div>
    )
}
