"use strict";

const React = require("react");
const useMeasure = require("react-use-measure");
const syncpipe = require("syncpipe");
const defaultValue = require("default-value");
const clamp = require("clamp");

const useTheme = require("../../util/themeable");
const defaultStyle = require("./style.css");
const childrenWithProps = require("../../util/children-with-props");
const generateGridTemplateString = require("../../util/generate-grid-template-string");
const useIndexedState = require("../../util/use-indexed-state");
const sum = require("../../util/sum");

const MINIMUM_PANE_SIZE = 16;

// FIXME: Logic to constrain resizing when increasing a pane's size further would make the paneset exceed its size boundaries
// FIXME: Disable selection globally during dragging

function selectSmallestSize(sizes) {
	console.log("sizes", sizes);
	if (sizes.some((size) => size != null && isNaN(size))) {
		throw new Error(`NaN encountered!`);
	}
	
	return syncpipe(sizes, [
		(_) => _.filter((size) => size != null),
		(_) => (_.length > 0)
			? Math.min(... _)
			: undefined
	]);
}

module.exports = function PaneSet({ vertical, horizontal, children }) {
	let { withTheme } = useTheme({ control: "paneSet", defaultStyle });
	let [ boundsRef, bounds ] = useMeasure({ debounce: 10 });
	let [ newSizes, setNewSize, resetNewSizes ] = useIndexedState();
	// FIXME: How to deal with new children? Reset all newSizes?

	let direction = (vertical === true)
		? "vertical"
		: "horizontal";

	let ownSize = (direction === "vertical")
		? bounds.height
		: bounds.width;

	let childrenArray = React.Children.toArray(children);

	let minimumSizes = React.useMemo(() => {
		return childrenArray.map((child) => {
			return selectSmallestSize([ child.props.minimumSize, MINIMUM_PANE_SIZE ]);
		});
	});

	// FIXME: Detect when this fails
	let autoPaneMinimumSize = syncpipe(childrenArray, [
		(_) => _.findIndex((child) => child.resizable !== true),
		(_) => minimumSizes[_]
	]);

	let maximumSizes = React.useMemo(() => {
		let paneSizes = childrenArray.map((child, i) => {
			// FIXME: Require initialSize when resizable
			return defaultValue(newSizes[i], child.props.initialSize);
		});

		return childrenArray.map((_, ownIndex) => {
			let otherPaneSizes = syncpipe(paneSizes, [
				(_) => _.filter((size, i) => size != null && i !== ownIndex),
				(_) => sum(_)
			]);

			return clamp(ownSize - otherPaneSizes - autoPaneMinimumSize, 0, Infinity);
		});
	}, [ childrenArray, newSizes, ownSize ]);

	let autoSizeSeen = 0;

	// NOTE: Not using React.Children.map because it will ignore null return values
	let handleSides = childrenArray.map((child) => {
		if (child.props.resizable === true) {
			if (autoSizeSeen > 0) {
				return (direction === "vertical")
					? "n" // north, top
					: "w"; // west, left
			} else {
				return (direction === "vertical")
					? "s" // south, bottom
					: "e"; // east, right
			}
		} else {
			autoSizeSeen += 1;
			return null;
		}
	});

	let gridTemplateProperty = (direction === "vertical")
		? "gridTemplateRows"
		: "gridTemplateColumns";

	let gridTemplateString = syncpipe(handleSides, [
		(_) => _.map((handle) => handle != null ? "auto" : 1),
		(_) => generateGridTemplateString(_)
	]);

	let style = {
		[gridTemplateProperty]: gridTemplateString
	};

	if (autoSizeSeen !== 1) {
		// We don't allow *multiple* auto-sized panes, to avoid weird behaviour where the auto-layouted panes may not end up exactly where the resize handle is
		throw new Error(`Exactly one (center) pane must be non-resizable!`);
	}
	
	return (
		<div ref={boundsRef} className={withTheme("uilibComponent", "paneSet", `direction-${direction}`)} style={style}>
			{childrenWithProps(children, (child, i) => {
				let handleSide = handleSides[i];
				
				return {
					onResized: (newSize) => {
						setNewSize(i, newSize);
					},
					maximumSize: selectSmallestSize([ child.props.maximumSize, maximumSizes[i] ]),
					// Note that we shouldn't override an explicitly user-configured handleSide
					... (handleSide == null)
						? {}
						: {
							handleSide: defaultValue(child.props.handleSide, handleSide),
							direction: direction
						}
				};
			})}
		</div>
	);
};
