import React from 'react';
import * as typestyle from 'typestyle';
import { SizingProp, StylesProp, SpacingProp, VerticalsAlignProps, HorizontalsAlignProps, PaddingProp, SizeProps, ScrollProp, StyleProp, ClassNameProp, TagProps, CrossAxisAlignProp } from '../common';
import { createBaseTag, useGLSDefaults, _processSizing, _processPadding, cssLengthToString, _processScroll, _processCrossAxisAlign } from '../internal/utils';
import { vertical, horizontal, centerJustified, endJustified, end, center, start, baseline } from '../styles/flex';
import { verticallySpaced, horizontallySpaced } from '../styles/spacing';

/** 
 * Props that can be specified at
 * - root of `Responsive` 
 * - and overridden for `vertical`/`horizontal` modes
 */
export interface ResponsiveOverridableProps extends
  ScrollProp,
  PaddingProp,
  SizeProps,
  SizingProp,
  SpacingProp,
  CrossAxisAlignProp {
}

export interface BreakpointProp {
  /** 
   * windowWidth <= breakpoint : it is vertical (mobile)
   * else                      : it is horizontal (desktop)
   **/
  breakpoint?: number;
}

/** 
 * Props for Vertical mode
 */
export interface ResponsiveVerticalModeProps extends
  VerticalsAlignProps,
  StylesProp,
  ResponsiveOverridableProps {
}

/** 
 * Props for Horizontal mode
 */
export interface ResponsiveHorizontalModeProps extends
  HorizontalsAlignProps,
  StylesProp,
  ResponsiveOverridableProps {
}

export interface ResponsiveModesProps {
  /** Vertical mode configuration */
  vertical?: ResponsiveVerticalModeProps;
  /** Horizontal mode configuration */
  horizontal?: ResponsiveHorizontalModeProps;
}

/** 
 * Props that can only be specified at the root of the `Responsive` 
 */
export interface ResponsiveRootOnlyProps extends
  StylesProp,
  StyleProp,
  ClassNameProp,
  TagProps,
  BreakpointProp {
}

/** 
 * Props for the Responsive component
 */
export interface ResponsiveProps extends
  ResponsiveRootOnlyProps,
  ResponsiveModesProps,
  ResponsiveOverridableProps {
}

/**
 * Layout out children
 * - vertically till breakpoint
 * - horizontally above breakpoint
 */
export const Responsive = React.forwardRef((props: ResponsiveProps, ref: React.LegacyRef<HTMLDivElement>) => {
  const {
    verticalSpacing: defaultVerticalSpacing,
    horizontalSpacing: defaultHorizontalSpacing,
    breakpoint: bp
  } = useGLSDefaults();

  const {
    breakpoint = bp,
    vertical: verticalOptions,
    horizontal: horizontalOptions,

    /** Overridable */
    scroll,
    padding,
    height,
    minHeight,
    maxHeight,
    width,
    minWidth,
    maxWidth,
    sizing,
    crossAxisAlign,
    spacing,

    ...otherProps
  } = props;

  /** Determine scroll */
  const verticalScroll = (verticalOptions && verticalOptions.scroll != null) ? verticalOptions.scroll
    : scroll;
  const horizontalScroll = (horizontalOptions && horizontalOptions.scroll != null) ? horizontalOptions.scroll
    : scroll;

  /** Determine paddings */
  const verticalPadding = (verticalOptions && verticalOptions.padding != null) ? verticalOptions.padding
    : padding;
  const horizontalPadding = (horizontalOptions && horizontalOptions.padding != null) ? horizontalOptions.padding
    : padding;

  /** Determine size */
  const verticalHeight = (verticalOptions && verticalOptions.height != null) ? verticalOptions.height
    : height;
  const horizontalHeight = (horizontalOptions && horizontalOptions.height != null) ? horizontalOptions.height
    : height;
  const verticalMinHeight = (verticalOptions && verticalOptions.minHeight != null) ? verticalOptions.minHeight
    : minHeight;
  const horizontalMinHeight = (horizontalOptions && horizontalOptions.minHeight != null) ? horizontalOptions.minHeight
    : minHeight;
  const verticalMaxHeight = (verticalOptions && verticalOptions.maxHeight != null) ? verticalOptions.maxHeight
    : maxHeight;
  const horizontalMaxHeight = (horizontalOptions && horizontalOptions.maxHeight != null) ? horizontalOptions.maxHeight
    : maxHeight;
  const verticalWidth = (verticalOptions && verticalOptions.width != null) ? verticalOptions.width
    : width;
  const horizontalWidth = (horizontalOptions && horizontalOptions.width != null) ? horizontalOptions.width
    : width;
  const verticalMinWidth = (verticalOptions && verticalOptions.minWidth != null) ? verticalOptions.minWidth
    : minWidth;
  const horizontalMinWidth = (horizontalOptions && horizontalOptions.minWidth != null) ? horizontalOptions.minWidth
    : minWidth;
  const verticalMaxWidth = (verticalOptions && verticalOptions.maxWidth != null) ? verticalOptions.maxWidth
    : maxWidth;
  const horizontalMaxWidth = (horizontalOptions && horizontalOptions.maxWidth != null) ? horizontalOptions.maxWidth
    : maxWidth;

  /** Determine sizings */
  const verticalSizing = (verticalOptions && verticalOptions.sizing != null)
    ? verticalOptions.sizing
    : sizing;
  const horizontalSizing = (horizontalOptions && horizontalOptions.sizing != null)
    ? horizontalOptions.sizing
    : sizing;

  const verticalCrossAxisAlign = (verticalOptions && verticalOptions.crossAxisAlign != null)
    ? verticalOptions.crossAxisAlign
    : crossAxisAlign;
  const horizontalCrossAxisAlign = (horizontalOptions && horizontalOptions.crossAxisAlign != null)
    ? horizontalOptions.crossAxisAlign
    : crossAxisAlign;

  /** Determine spacings  */
  const verticalSpacing = (verticalOptions && verticalOptions.spacing != null) ? verticalOptions.spacing
    : spacing != null ? spacing
      : defaultVerticalSpacing;
  const horizontalSpacing = (horizontalOptions && horizontalOptions.spacing != null) ? horizontalOptions.spacing
    : spacing != null ? spacing
      : defaultHorizontalSpacing;

  /** Determine alignments */
  const verticalModeVerticalAlign =
    (verticalOptions && verticalOptions.verticalAlign != null)
      ? verticalOptions.verticalAlign
      : null;
  const verticalModeHorizontalAlign =
    (verticalOptions && verticalOptions.horizontalAlign != null)
      ? verticalOptions.horizontalAlign
      : null;
  const horizontalModeVerticalAlign =
    (horizontalOptions && horizontalOptions.verticalAlign != null)
      ? horizontalOptions.verticalAlign
      : null;
  const horizontalModeHorizontalAlign =
    (horizontalOptions && horizontalOptions.horizontalAlign != null)
      ? horizontalOptions.horizontalAlign
      : null;

  const klass = typestyle.style(
    /** Till breakpoint: Vertical */
    typestyle.media({ minWidth: 0, maxWidth: breakpoint },
      _processSizing(verticalSizing),
      verticalCrossAxisAlign != null && _processCrossAxisAlign(verticalCrossAxisAlign),
      verticalPadding != null && _processPadding(verticalPadding),
      vertical,
      verticalSpacing !== 0 && verticallySpaced(verticalSpacing),
      verticalModeVerticalAlign == 'center' && centerJustified,
      verticalModeVerticalAlign == 'bottom' && endJustified,
      verticalModeHorizontalAlign == 'left' && start,
      verticalModeHorizontalAlign == 'right' && end,
      verticalModeHorizontalAlign == 'center' && center,
      verticalHeight != null && { height: cssLengthToString(verticalHeight) },
      verticalMinHeight != null && { minHeight: cssLengthToString(verticalMinHeight) },
      verticalMaxHeight != null && { maxHeight: cssLengthToString(verticalMaxHeight) },
      verticalWidth != null && { width: cssLengthToString(verticalWidth) },
      verticalMinWidth != null && { minWidth: cssLengthToString(verticalMinWidth) },
      verticalMaxWidth != null && { maxWidth: cssLengthToString(verticalMaxWidth) },
      verticalScroll != null && _processScroll(verticalScroll),
    ),
    verticalOptions && verticalOptions.styles && typestyle.media({ minWidth: 0, maxWidth: breakpoint },
      ...verticalOptions.styles),

    /** Bigger than breakpoint: Horizontal */
    typestyle.media({ minWidth: breakpoint + 1 },
      _processSizing(horizontalSizing),
      horizontalCrossAxisAlign != null && _processCrossAxisAlign(horizontalCrossAxisAlign),
      horizontalPadding != null && _processPadding(horizontalPadding),
      horizontal,
      horizontalSpacing !== 0 && horizontallySpaced(horizontalSpacing),
      horizontalModeHorizontalAlign == 'right' && endJustified,
      horizontalModeHorizontalAlign == 'center' && centerJustified,
      horizontalModeVerticalAlign == 'top' && start,
      horizontalModeVerticalAlign == 'center' && center,
      horizontalModeVerticalAlign == 'bottom' && end,
      horizontalModeVerticalAlign == 'baseline' && baseline,
      horizontalHeight != null && { height: cssLengthToString(horizontalHeight) },
      horizontalMinHeight != null && { minHeight: cssLengthToString(horizontalMinHeight) },
      horizontalMaxHeight != null && { maxHeight: cssLengthToString(horizontalMaxHeight) },
      horizontalWidth != null && { width: cssLengthToString(horizontalWidth) },
      horizontalMinWidth != null && { minWidth: cssLengthToString(horizontalMinWidth) },
      horizontalMaxWidth != null && { maxWidth: cssLengthToString(horizontalMaxWidth) },
      horizontalScroll != null && _processScroll(horizontalScroll),
    ),
    horizontalOptions && horizontalOptions.styles && typestyle.media({ minWidth: breakpoint + 1 },
      ...horizontalOptions.styles),
  );
  return createBaseTag(otherProps, klass, 'Responsive', ref);
});
Responsive.displayName = 'Responsive';
