import { Property } from "csstype";
import { FAIdentifier } from "./fa-icons";
import { EmbeddedTooltip } from "./props";
import { MouseEventHandler } from "react";

/**
 * Customization options for icons.
 *
 * Feel free to use this as a portable configuration object for icons wherever they are used.
 *
 * @public
 */
export interface Icon extends EmbeddedTooltip {
  className?: string;

  /**
   * Identifier to choose specific FontAwesome icon to display.
   *
   * @see https://fontawesome.com/search
   */
  identifier: FAIdentifier;
  /**
   * Colour options for icon.
   *
   * Duotone icons can override individual colours and opacity via {@link options}.
   * @example
   * "var(--colour-corporate-blue)"
   * ""
   */
  colour?: string;

  /**
   * Opacity of icon.
   *
   * Duotone icons colour opacity is affected by this.
   *
   * `opacity` style class will also still be applied, possibly multiplying the effect.
   */
  opacity?: number | undefined;

  /** Integer value for rotation of icon. Both positive and negative supported.
   * @default 0
   */
  rotation?: number | undefined;
  /**
   * Transformation options for icon, to flip image.
   * @default "none"
   */
  flip?: IconFlip | undefined;
  /**
   * Font icon pack used.
   * @default "classic"
   */
  pack?: IconPack | undefined;
  /**
   * Icon weighting used.
   *
   * Often referred to as 'style' by FontAwesome documentation.
   * @default "light"
   */
  weight?: IconWeight | undefined;
  /**
   * Sizing of FontAwesome icon. Can be FontAwesome specific sizing, or generic `fontSize`.
   *
   * Alternatively, sizing can be set via `fontSize` CSS property.
   * @default "1x"
   */
  size?: IconSize | Property.FontSize<string | number> | undefined;
  /**
   * Animation options for icon. Accepts either {@link IconAnimationType} slug or options object.
   *
   * @default -
   */
  animation?: IconAnimationType | IconAnimationOptions | undefined;

  /**
   * Further options for specific icon settings.
   * Most of these icons require specific conditions to be met for the option to take effect.
   *
   * Read each option itself for more information.
   * @see {@link IconOptions}
   */
  options?: IconOptions | undefined;

  /**
   * Standard HTMLElement onClick
   */
  onClick?: MouseEventHandler<HTMLElement> | undefined;
}

/** Available icon styling looks */
export type IconPack = "classic" | "duotone" | "sharp" | "sharp-duotone" | "brands";
/** Available icon weights */
export type IconWeight =
  | "solid" //900
  | "regular" //400
  | "light" //300
  | "thin"; //100
/** Available icon transformation options */
export type IconFlip = "horizontal" | "vertical" | "both" | "none";

/**
 * Additional configuration options for icons.
 * Most of these options require specific conditions to be met for the option to take effect.
 */
export interface IconOptions {
  /** Requires {pack: "duotone" | "sharp-duotone"} */
  primaryColour?: string | undefined;
  /** Requires {pack: "duotone" | "sharp-duotone"} */
  secondaryColour?: string | undefined;
  /** Requires {pack: "duotone" | "sharp-duotone"} */
  primaryOpacity?: number | undefined;
  /** Requires {pack: "duotone" | "sharp-duotone"} */
  secondaryOpacity?: number | undefined;
}

/**
 * Sizes available for icons.
 *
 * Note that only `1x` and `2x` are acceptable values for OSIcons within an OSIconStack
 */
export type IconSize =
  //Relative sizes
  | "2xs"
  | "xs"
  | "sm"
  | "lg"
  | "xl"
  | "2xl"
  //Literal sizes
  | "1x"
  | "2x"
  | "3x"
  | "4x"
  | "5x"
  | "6x"
  | "7x"
  | "8x"
  | "9x"
  | "10x";

/**
 * Animations available for icons.
 */
export type IconAnimationType =
  | "beat"
  | "fade"
  | "beat-fade"
  | "flip"
  | "bounce"
  | "shake"
  | "spin"
  | "spin-pulse";

/**
 * All available animation configuration types.
 */
export type IconAnimationOptions =
  | OSIconBeatAnimation
  | OSIconBounceAnimation
  | OSIconFadeAnimation
  | OSIconBeatFadeAnimation
  | OSIconBounceAnimation
  | OSIconFlipAnimation
  | OSIconShakeAnimation
  | OSIconSpinAnimation
  | OSIconSpinPulseAnimation;

/**
 * Common configuration options for icon animations.
 */
interface OSIconAnimationBase {
  /**
   * Slug of animation to apply.
   */
  animation: IconAnimationType;

  /**
   * The **`animation-delay`** CSS property specifies the amount of time to wait from applying the animation to an element before beginning to perform the animation. The animation can start later, immediately from its beginning, or immediately and partway through the animation.
   *
   * **Syntax**: `<time>#`
   *
   * **Initial value**: `0s`
   *
   * | Chrome  | Firefox | Safari  |  Edge  |   IE   |
   * | :-----: | :-----: | :-----: | :----: | :----: |
   * | **43**  | **16**  |  **9**  | **12** | **10** |
   * | 3 _-x-_ | 5 _-x-_ | 4 _-x-_ |        |        |
   *
   * @see https://developer.mozilla.org/docs/Web/CSS/animation-delay
   */
  animationDelay?: Property.AnimationDelay | undefined;
  /**
   * The **`animation-direction`** CSS property sets whether an animation should play forward, backward, or alternate back and forth between playing the sequence forward and backward.
   *
   * **Syntax**: `<single-animation-direction>#`
   *
   * **Initial value**: `normal`
   *
   * | Chrome  | Firefox | Safari  |  Edge  |   IE   |
   * | :-----: | :-----: | :-----: | :----: | :----: |
   * | **43**  | **16**  |  **9**  | **12** | **10** |
   * | 3 _-x-_ | 5 _-x-_ | 4 _-x-_ |        |        |
   *
   * @see https://developer.mozilla.org/docs/Web/CSS/animation-direction
   */
  animationDirection?: Property.AnimationDirection | undefined;
  /**
   * The **`animation-duration`** CSS property sets the length of time that an animation takes to complete one cycle.
   *
   * **Syntax**: `<time>#`
   *
   * **Initial value**: `0s`
   *
   * | Chrome  | Firefox | Safari  |  Edge  |   IE   |
   * | :-----: | :-----: | :-----: | :----: | :----: |
   * | **43**  | **16**  |  **9**  | **12** | **10** |
   * | 3 _-x-_ | 5 _-x-_ | 4 _-x-_ |        |        |
   *
   * @see https://developer.mozilla.org/docs/Web/CSS/animation-duration
   */
  animationDuration?: Property.AnimationDuration | undefined;
  /**
   * The **`animation-iteration-count`** CSS property sets the number of times an animation sequence should be played before stopping.
   *
   * **Syntax**: `<single-animation-iteration-count>#`
   *
   * **Initial value**: `1`
   *
   * | Chrome  | Firefox | Safari  |  Edge  |   IE   |
   * | :-----: | :-----: | :-----: | :----: | :----: |
   * | **43**  | **16**  |  **9**  | **12** | **10** |
   * | 3 _-x-_ | 5 _-x-_ | 4 _-x-_ |        |        |
   *
   * @see https://developer.mozilla.org/docs/Web/CSS/animation-iteration-count
   */
  animationIterationCount?: Property.AnimationIterationCount | undefined;
  /**
   * The **`animation-timing-function`** CSS property sets how an animation progresses through the duration of each cycle.
   *
   * **Syntax**: `<easing-function>#`
   *
   * **Initial value**: `ease`
   *
   * | Chrome  | Firefox | Safari  |  Edge  |   IE   |
   * | :-----: | :-----: | :-----: | :----: | :----: |
   * | **43**  | **16**  |  **9**  | **12** | **10** |
   * | 3 _-x-_ | 5 _-x-_ | 4 _-x-_ |        |        |
   *
   * @see https://developer.mozilla.org/docs/Web/CSS/animation-timing-function
   */
  animationTimingFunction?: Property.AnimationTimingFunction | undefined;
}

/**
 * Configuration options for 'beat' icon animation.
 *
 * @see https://docs.fontawesome.com/web/style/animate#beat
 */
export interface OSIconBeatAnimation extends OSIconAnimationBase {
  /**
   * Use the `fa-beat` animation to scale an icon up or down.
   * This is useful for grabbing attention or for use with health/heart-centric icons.
   */
  animation: "beat";
  /** Set max value that an icon will scale. */
  scale?: number | undefined;
}

/**
 * Configuration options for 'fade' icon animation.
 *
 * @see https://docs.fontawesome.com/web/style/animate#fade
 */
export interface OSIconFadeAnimation extends OSIconAnimationBase {
  /**
   * Use the `fa-fade` animation to fade an icon in and out visually to grab attention in a subtle (or not so subtle) way.
   */
  animation: "fade";
  /** Set lowest opacity value an icon will fade to and from. */
  opacity?: number | undefined;
}

/**
 * Configuration options for 'beat-fade' icon animation.
 *
 * @see https://docs.fontawesome.com/web/style/animate#beat-fade
 */
export interface OSIconBeatFadeAnimation extends OSIconAnimationBase {
  /**
   * Use the `fa-beat-fade` animation to grab attention by visually scaling and pulsing an icon in and out.
   */
  animation: "beat-fade";
  /** Set lowest opacity value an icon will fade to and from. */
  opacity?: number | undefined;
  /** Set max value that an icon will scale. */
  scale?: number | undefined;
}

/**
 * Configuration options for 'bounce' icon animation.
 *
 * @see https://docs.fontawesome.com/web/style/animate#bounce
 */
export interface OSIconBounceAnimation extends OSIconAnimationBase {
  /**
   * Use the `fa-bounce` animation to grab attention by visually bouncing an icon up and down.
   */
  animation: "bounce";
  /** Set the amount of rebound an icon has when landing after the jump. */
  rebound?: number | undefined;
  /** Set the max height an icon will jump to when bouncing. */
  height?: number | undefined;
  /** Set the icon’s horizontal distortion (“squish”) when starting to bounce. */
  startScaleX?: number | undefined;
  /** Set the icon’s vertical distortion (“squish”) when starting to bounce */
  startScaleY?: number | undefined;
  /** Set the icon’s horizontal distortion (“squish”) at the top of the jump. */
  jumpScaleX?: number | undefined;
  /** Set the icon’s vertical distortion (“squish”) at the top of the jump. */
  jumpScaleY?: number | undefined;
  /** Set the icon’s horizontal distortion (“squish”) when landing after the jump. */
  landScaleX?: number | undefined;
  /** Set the icon’s vertical distortion (“squish”) when landing after the jump. */
  landScaleY?: number | undefined;
}

/**
 * Configuration options for 'flip' icon animation.
 *
 * @see https://docs.fontawesome.com/web/style/animate#flip
 */
export interface OSIconFlipAnimation extends OSIconAnimationBase {
  /**
   * Use the `fa-flip` animation to rotate an icon in 3D space.
   * By default, flip rotates an icon about the Y axis 180 degrees.
   * Flipping is helpful for transitions, processing states, or for using physical objects that one flips in the real world.
   *
   * @see https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate3d()
   */
  animation: "flip";
  /** Set x-coordinate of the vector denoting the axis of rotation (between `0` and `1`). */
  flipX?: number | undefined;
  /** Set y-coordinate of the vector denoting the axis of rotation (between `0` and `1`). */
  flipY?: number | undefined;
  /** Set z-coordinate of the vector denoting the axis of rotation (between `0` and `1`). */
  flipZ?: number | undefined;
  /** Set rotation angle of flip. A positive angle denotes a clockwise rotation, a negative angle a counter-clockwise one. */
  flipAngle?: number | undefined;
}

/**
 * Configuration options for 'shake' icon animation.
 *
 * @see https://docs.fontawesome.com/web/style/animate#shake
 */
export interface OSIconShakeAnimation extends OSIconAnimationBase {
  /**
   * Use the `fa-shake` animation to grab attention or note that something is not allowed by shaking an icon back and forth.
   */
  animation: "shake";
}

/**
 * Configuration options for 'spin' icon animation.
 *
 * @see https://docs.fontawesome.com/web/style/animate#spin
 */
export interface OSIconSpinAnimation extends OSIconAnimationBase {
  /**
   * Use the `fa-spin` class to get any icon to rotate.
   * This works especially well with fa-spinner and everything in the spinner icons category.
   */
  animation: "spin";
  /** When used in conjunction with fa-spin or fa-spin-pulse, makes an icon spin counter-clockwise. */
  reverse?: boolean | undefined;
}

/**
 * Configuration options for 'spin-pulse' icon animation.
 *
 * @see https://docs.fontawesome.com/web/style/animate#spin
 */
export interface OSIconSpinPulseAnimation extends OSIconAnimationBase {
  /**
   * Use the `fa-spin-pulse` class to get any icon to rotate with eight steps.
   * This works especially well with fa-spinner and everything in the spinner icons category.
   */
  animation: "spin-pulse";

  /** When used in conjunction with fa-spin or fa-spin-pulse, makes an icon spin counter-clockwise. */
  reverse?: boolean | undefined;
}
