import { CourtBall } from "./CourtBall"

import {
    ReactElement,
    RefObject,
    useCallback,
    useLayoutEffect,
    useState,
} from "react"
import CourtPlayer from "./CourtPlayer"

import { Player } from "../../tactic/Player"
import { Action, ActionKind } from "../../tactic/Action"
import ArrowAction from "../actions/ArrowAction"
import { middlePos, ratioWithinBase } from "../arrows/Pos"
import BallAction from "../actions/BallAction"
import { CourtObject } from "../../tactic/CourtObjects"
import { contains } from "../arrows/Box"
import { CourtAction } from "../../views/editor/CourtAction"

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(origin: Player, arrowHead: DOMRect) {
        const originRef = document.getElementById(origin.id)!
        const courtBounds = courtRef.current!.getBoundingClientRect()
        const start = ratioWithinBase(
            middlePos(originRef.getBoundingClientRect()),
            courtBounds,
        )

        for (const player of players) {
            if (player.id == origin.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 end = ratioWithinBase(middlePos(targetPos), courtBounds)

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

        const action: Action = {
            fromPlayerId: originRef.id,
            type: origin.hasBall ? ActionKind.DRIBBLE : ActionKind.MOVE,
            moveFrom: ratioWithinBase(
                middlePos(originRef.getBoundingClientRect()),
                courtBounds,
            ),
            segments: [
                { next: ratioWithinBase(middlePos(arrowHead), courtBounds) },
            ],
        }
        setActions((actions) => [...actions, action])
    }

    const [previewAction, setPreviewAction] = useState<Action | null>(null)

    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}

            {players.map((player) => (
                <CourtPlayer
                    key={player.id}
                    player={player}
                    onDrag={() => updateActionsRelatedTo(player)}
                    onChange={onPlayerChange}
                    onRemove={() => onPlayerRemove(player)}
                    courtRef={courtRef}
                    availableActions={(pieceRef) => [
                        <ArrowAction
                            key={1}
                            onHeadMoved={(headPos) => {
                                const baseBounds =
                                    courtRef.current!.getBoundingClientRect()

                                const arrowHeadPos = middlePos(headPos)

                                const target = players.find(
                                    (p) =>
                                        p != player &&
                                        contains(
                                            document
                                                .getElementById(p.id)!
                                                .getBoundingClientRect(),
                                            arrowHeadPos,
                                        ),
                                )

                                setPreviewAction((action) => ({
                                    ...action!,
                                    segments: [
                                        {
                                            next: ratioWithinBase(
                                                arrowHeadPos,
                                                baseBounds,
                                            ),
                                        },
                                    ],
                                    type: player.hasBall
                                        ? target
                                            ? ActionKind.SHOOT
                                            : ActionKind.DRIBBLE
                                        : target
                                          ? ActionKind.SCREEN
                                          : ActionKind.MOVE,
                                }))
                            }}
                            onHeadPicked={(headPos) => {
                                ;(document.activeElement as HTMLElement).blur()
                                const baseBounds =
                                    courtRef.current!.getBoundingClientRect()

                                setPreviewAction({
                                    type: player.hasBall
                                        ? ActionKind.DRIBBLE
                                        : ActionKind.MOVE,
                                    fromPlayerId: player.id,
                                    toPlayerId: undefined,
                                    moveFrom: ratioWithinBase(
                                        middlePos(
                                            pieceRef.getBoundingClientRect(),
                                        ),
                                        baseBounds,
                                    ),
                                    segments: [
                                        {
                                            next: ratioWithinBase(
                                                middlePos(headPos),
                                                baseBounds,
                                            ),
                                        },
                                    ],
                                })
                            }}
                            onHeadDropped={(headRect) => {
                                placeArrow(player, headRect)
                                setPreviewAction(null)
                            }}
                        />,
                        player.hasBall && (
                            <BallAction
                                key={2}
                                onDrop={(ref) =>
                                    onBallMoved(ref.getBoundingClientRect())
                                }
                            />
                        ),
                    ]}
                />
            ))}

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

            {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)
            })}

            {previewAction && (
                <CourtAction
                    courtRef={courtRef}
                    action={previewAction}
                    //do nothing on change, not really possible as it's a preview arrow
                    onActionDeleted={() => {}}
                    onActionChanges={() => {}}
                />
            )}
        </div>
    )
}
