import * as typestyle from 'typestyle';
import { CSSLength, BaseProps, GLSDefaults, CSSBox, Scroll, Sizing } from "../common";
import * as scrollHelpers from "../styles/scroll";
import { content, stretch, crossAxisStart, crossAxisCenter, crossAxisEnd, crossAxisStretch } from '../styles/flex';
import React, { useContext } from 'react';

/**
 * For `number` we assume pixels e.g. 5 => '5px'
 * For `string` *you* should provide the unit e.g. '5px'
 */
export function cssLengthToString(value: CSSLength): string {
  return typeof value === 'number' ? value + 'px' : value;
}

/** 
 * Ensures all four members of box are present
 */
function boxToFullBox(box: CSSBox, name: string): typestyle.types.NestedCSSProperties {
  if (typeof box == 'number' || typeof box == 'string') {
    const value = cssLengthToString(box);
    return {
      [name + 'Top']: value,
      [name + 'Right']: value,
      [name + 'Bottom']: value,
      [name + 'Left']: value,
    };
  } else if (!Array.isArray(box)) {
    const toReturn: any = {};
    if (box.top != null) toReturn[name + 'Top'] = cssLengthToString(box.top);
    if (box.right != null) toReturn[name + 'Right'] = cssLengthToString(box.right);
    if (box.bottom != null) toReturn[name + 'Bottom'] = cssLengthToString(box.bottom);
    if (box.left != null) toReturn[name + 'Left'] = cssLengthToString(box.left);
    return toReturn;
  } else if (box.length == 2) {
    const [topBottom, leftRight] = box.map(cssLengthToString);
    return {
      [name + 'Top']: topBottom,
      [name + 'Right']: leftRight,
      [name + 'Bottom']: topBottom,
      [name + 'Left']: leftRight,
    };
  } else {
    const [top, right, bottom, left] = box.map(cssLengthToString);
    return {
      [name + 'Top']: top,
      [name + 'Right']: right,
      [name + 'Bottom']: bottom,
      [name + 'Left']: left,
    };
  }
}

/** 
 * Utility to unwrap the three ways a padding might be provided 
 */
export function _processPadding(box: CSSBox)
  : typestyle.types.NestedCSSProperties {
  return boxToFullBox(box, 'padding');
}

export function _processScroll(scroll: Scroll) {
  return (
    scroll == 'hidden' ? scrollHelpers.scrollHidden
      : scroll == 'horizontal' ? scrollHelpers.scrollHorizontal
        : scroll == 'vertical' ? scrollHelpers.scrollVertical
          : scrollHelpers.scrollBoth
  );
}

/** 
 * Creates the React tag
 * - Does any processing of `BaseProps` if required
 * - Then creates the appropriate tag
 */
export function createBaseTag<T extends BaseProps>(
  props: T,

  /** Any additional customizations done by the component */
  klass: string,

  /** Comment to help with debugging */
  comment: string,

  /** Ref to allow you to use dom element if you want */
  ref: React.LegacyRef<HTMLDivElement>
) {
  const {
    className,
    padding,
    height,
    minHeight,
    maxHeight,
    width,
    minWidth,
    maxWidth,
    scroll,
    styles = [],
    tag,
    ...otherProps } = props;

  return React.createElement(
    (tag != null) ? tag : 'div',
    {
      ref,
      className: typestyle.classes(
        className,
        klass,
        typestyle.style(
          /** Scroll */
          scroll != null && _processScroll(scroll),
          /** Padding */
          padding != null && _processPadding(padding),
          /** Size props */
          height != null && { height: cssLengthToString(height) },
          minHeight != null && { minHeight: cssLengthToString(minHeight) },
          maxHeight != null && { maxHeight: cssLengthToString(maxHeight) },
          width != null && { width: cssLengthToString(width) },
          minWidth != null && { minWidth: cssLengthToString(minWidth) },
          maxWidth != null && { maxWidth: cssLengthToString(maxWidth) },
          /** Any user customizations */
          ...styles,
        )
      ),
      'data-comment': comment,
      ...otherProps,
    }
  )
}

/** 
 * Generates the appropriate styles to handle the sizing interaction 
 */
export function _processSizing(fx: Sizing = 'content'): typestyle.types.CSSProperties {
  return fx == 'content' ? content
    : fx == 'stretch' ? stretch(1)
      : stretch(fx);
}

export function _processCrossAxisAlign(align: 'start' | 'center' | 'end' | 'stretch'): typestyle.types.CSSProperties {
  return align == 'start' ? crossAxisStart
    : align == 'center' ? crossAxisCenter
      : align == 'end' ? crossAxisEnd
        : crossAxisStretch;
}

/** 
 * Uses the defaults safely 
 */
export function useGLSDefaults() {
  const {
    verticalSpacing = 24,
    horizontalSpacing = 24,
    breakpoint = 480
  } = useContext(GLSDefaults);
  return {
    verticalSpacing,
    horizontalSpacing,
    breakpoint,
  }
}
