import { createContext, useCallback, useEffect, useMemo, useRef, } from "react";
import { useObservableState } from "../../../rx/hook";
import { cx } from "../../../utils";
import { DragHandelContext } from "../components/dragHandle";
import { ColDragHandlers, HiddenGridAreas, RowDragHandlers } from "../components/utils";
import { useGridResize } from "../hooks/useGridResize";
import { getGridColumn, getGridRow, getGridTemplateColumns, getGridTemplateRows, getInitialGridState, } from "../utils";
import * as styles from "./resizeable-grid.css";
/**
 * Hook to manage the state of the grid (columns and rows sizes).
 * Can persist the state to local storage.
 *
 * @param options - Configuration options.
 * @returns An observable state object containing the grid configuration.
 */
export function useGridState({ persist, ...gridConfig }) {
    return useObservableState(() => {
        return getInitialGridState(gridConfig);
    }, { persist });
}
/**
 * Context to share grid state and event handlers with child components (like drag handles).
 */
export let GridContext = createContext({
    columns: 0,
    rows: 0,
});
/**
 * A container component that provides a resizeable grid layout.
 * Supports dragging handles to resize columns and rows.
 *
 * @param props - Component props.
 * @returns The rendered grid container with drag handles and children.
 *
 * @example
 * ```tsx
 * <ResizeableGrid columns={2} rows={1}>
 *   <GridArea column={{ start: 1, end: 1 }} row={{ start: 1, end: 1 }}>
 *     Left
 *   </GridArea>
 *   <GridArea column={{ start: 2, end: 2 }} row={{ start: 1, end: 1 }}>
 *     Right
 *   </GridArea>
 * </ResizeableGrid>
 * ```
 */
export function ResizeableGrid({ children, onChange, dragHandleSize, persist, defaultValue, value, ...props }) {
    let [internalGridState, setInternalGridState] = useObservableState(() => {
        if (defaultValue || !value) {
            return getInitialGridState({
                defaultColumns: defaultValue?.columns,
                defaultRows: defaultValue?.rows,
            });
        }
        return value;
    }, { persist });
    let gridState = value ?? internalGridState;
    let gridStateRef = useRef(gridState);
    useEffect(() => {
        gridStateRef.current = gridState;
    });
    let setGridState = useCallback((partialState) => {
        if (onChange) {
            onChange({
                ...gridStateRef.current,
                ...partialState,
            });
        }
        else {
            setInternalGridState((current) => ({
                ...current,
                ...partialState,
            }));
        }
    }, [setInternalGridState, onChange]);
    let tempGridTemplateColumns = useRef(null);
    let tempGridTemplateRows = useRef(null);
    let { dragTarget, onPointerDown, onResize } = useGridResize({
        dragHandleSize,
        setGridState,
        gridState: gridStateRef,
        tempGridTemplateColumns,
        tempGridTemplateRows,
    });
    let gridTemplateColumns = tempGridTemplateColumns.current ?? getGridTemplateColumns(gridState.columns, dragHandleSize);
    let gridTemplateRows = tempGridTemplateRows.current ?? getGridTemplateRows(gridState.rows, dragHandleSize);
    let dragHandelContext = useMemo(() => {
        return {
            onPointerDown,
            onResize,
        };
    }, [onPointerDown, onResize]);
    let columns = gridState.columns.length;
    let rows = gridState.rows.length;
    return (<DragHandelContext.Provider value={dragHandelContext}>
			<div ref={dragTarget} {...props} className={cx(props.className, styles.grid)} style={{ gridTemplateColumns, gridTemplateRows }}>
				<HiddenGridAreas columns={columns} rows={rows}/>
				<ColDragHandlers columns={columns} rows={rows}/>
				<RowDragHandlers columns={columns} rows={rows}/>
				{children}
			</div>
		</DragHandelContext.Provider>);
}
/**
 * A component representing a specific area within the grid.
 * Automatically calculates `grid-column` and `grid-row` styles.
 *
 * @param props - Component props.
 * @returns The rendered section element.
 * ```tsx
 * <GridArea column={{ start: 3, end: 4 }} row={{ start: 1, end: 2 }} />
 * ```
 */
export function GridArea({ column, row, ...props }) {
    let style = {
        ...props.style,
        gridColumn: getGridColumn(column.start, column.end),
        gridRow: getGridRow(row.start, row.end),
    };
    return <section {...props} style={style}/>;
}
