import { CourtBall } from "./CourtBall"

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

import { Player } from "../../model/tactic/Player"
import { Action, ActionKind } from "../../model/tactic/Action"
import ArrowAction from "../actions/ArrowAction"
import { middlePos, ratioWithinBase } from "../../geo/Pos"
import BallAction from "../actions/BallAction"
import {BALL_ID} from "../../model/tactic/Ball"
import { contains, overlaps } from "../../geo/Box"

import { CourtAction } from "../../views/editor/CourtAction"
import { TacticComponent } from "../../model/tactic/Tactic"

export interface BasketCourtProps {
    components: TacticComponent[]
    actions: Action[]

    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({
    components,
    actions,
    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 component of components) {
            if (component.id == origin.id) {
                continue
            }

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

            if (overlaps(playerBounds, arrowHead)) {
                const targetPos = document
                    .getElementById(component.id)!
                    .getBoundingClientRect()

                const end = ratioWithinBase(middlePos(targetPos), courtBounds)

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

        const action: Action = {
            fromId: 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((comp: TacticComponent) => {
        const newPos = ratioWithinBase(
            middlePos(
                document.getElementById(comp.id)!.getBoundingClientRect(),
            ),
            courtRef.current!.getBoundingClientRect(),
        )
        setActions((actions) =>
            actions.map((a) => {
                if (a.fromId == comp.id) {
                    return { ...a, moveFrom: newPos }
                }

                if (a.toId == comp.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}

            {components.map((component) => {
                if (component.type == "player") {
                    const player = component
                    return (
                        <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 = components.find(
                                            (c) =>
                                                c.id != player.id &&
                                                contains(
                                                    document
                                                        .getElementById(c.id)!
                                                        .getBoundingClientRect(),
                                                    arrowHeadPos,
                                                ),
                                        )

                                        const type =
                                            target?.type == "player"
                                                ? player.hasBall
                                                    ? target
                                                        ? ActionKind.SHOOT
                                                        : ActionKind.DRIBBLE
                                                    : target
                                                      ? ActionKind.SCREEN
                                                      : ActionKind.MOVE
                                                : ActionKind.MOVE

                                        setPreviewAction((action) => ({
                                            ...action!,
                                            segments: [
                                                {
                                                    next: ratioWithinBase(
                                                        arrowHeadPos,
                                                        baseBounds,
                                                    ),
                                                },
                                            ],
                                            type,
                                        }))
                                    }}
                                    onHeadPicked={(headPos) => {
                                        ;(
                                            document.activeElement as HTMLElement
                                        ).blur()
                                        const baseBounds =
                                            courtRef.current!.getBoundingClientRect()

                                        setPreviewAction({
                                            type: player.hasBall
                                                ? ActionKind.DRIBBLE
                                                : ActionKind.MOVE,
                                            fromId: player.id,
                                            toId: 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(),
                                            )
                                        }
                                    />
                                ),
                            ]}
                        />
                    )
                }
                if (component.type == BALL_ID) {
                    return (
                        <CourtBall
                            onPosValidated={onBallMoved}
                            onMoves={() => updateActionsRelatedTo(component)}
                            ball={component}
                            onRemove={onBallRemove}
                            key="ball"
                        />
                    )
                }
                throw new Error("unknown tactic component " + component)
            })}

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

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