// Copyright © 2024 650 Industries.

'use client';

import { Component } from 'react';
import { ColorValue, Platform, processColor, ViewProps } from 'react-native';

import NativeLinearGradient from './NativeLinearGradient';
import { NativeLinearGradientPoint } from './NativeLinearGradient.types';

// @needsAudit
/**
 * An object `{ x: number; y: number }` or array `[x, y]` that represents the point
 * at which the gradient starts or ends, as a fraction of the overall size of the gradient ranging
 * from `0` to `1`, inclusive.
 */
export type LinearGradientPoint =
  | {
      /**
       * A number ranging from `0` to `1`, representing the position of gradient transformation.
       */
      x: number;
      /**
       * A number ranging from `0` to `1`, representing the position of gradient transformation.
       */
      y: number;
    }
  | NativeLinearGradientPoint;

// @needsAudit
export type LinearGradientProps = ViewProps & {
  /**
   * A readonly array of colors that represent stops in the gradient. At least two colors are required
   * (for a single-color background, use the `style.backgroundColor` prop on a `View` component).
   *
   * For TypeScript to know the provided array has 2 or more values, it should be provided "inline" or typed `as const`.
   */
  colors: readonly [ColorValue, ColorValue, ...ColorValue[]];
  /**
   * A readonly array that contains `number`s ranging from `0` to `1`, inclusive, and is the same length as the `colors` property.
   * Each number indicates a color-stop location where each respective color should be located.
   * If not specified, the colors will be distributed evenly across the gradient.
   *
   * For example, `[0.5, 0.8]` would render:
   * - the first color, solid, from the beginning of the gradient view to 50% through (the middle);
   * - a gradient from the first color to the second from the 50% point to the 80% point; and
   * - the second color, solid, from the 80% point to the end of the gradient view.
   *
   * > The color-stop locations must be ascending from least to greatest.
   * @default []
   */
  locations?: readonly [number, number, ...number[]] | null;
  /**
   * For example, `{ x: 0.1, y: 0.2 }` means that the gradient will start `10%` from the left and `20%` from the top.
   *
   * **On web**, this only changes the angle of the gradient because CSS gradients don't support changing the starting position.
   * @default { x: 0.5, y: 0.0 }
   */
  start?: LinearGradientPoint | null;
  /**
   * For example, `{ x: 0.1, y: 0.2 }` means that the gradient will end `10%` from the left and `20%` from the bottom.
   *
   * **On web**, this only changes the angle of the gradient because CSS gradients don't support changing the end position.
   * @default { x: 0.5, y: 1.0 }
   */
  end?: LinearGradientPoint | null;

  /**
   * Enables or disables paint dithering. Dithering can reduce the gradient color banding issue.
   * Setting `false` may improve gradient rendering performance.
   * @default true
   * @platform android
   */
  dither?: boolean;
};

/**
 * Renders a native view that transitions between multiple colors in a linear direction.
 */
export class LinearGradient extends Component<LinearGradientProps> {
  render() {
    const { colors, locations, start, end, dither, ...props } = this.props;
    let resolvedLocations: readonly number[] | null | undefined = locations;
    if (locations && colors.length !== locations.length) {
      console.warn('LinearGradient colors and locations props should be arrays of the same length');
      resolvedLocations = locations.slice(0, colors.length);
    }

    return (
      <NativeLinearGradient
        {...props}
        colors={Platform.select({
          web: colors as any,
          default: colors.map(processColor),
        })}
        dither={Platform.select({ android: dither })}
        locations={resolvedLocations}
        startPoint={_normalizePoint(start)}
        endPoint={_normalizePoint(end)}
      />
    );
  }
}

function _normalizePoint(
  point?: LinearGradientPoint | null
): NativeLinearGradientPoint | undefined {
  if (!point) {
    return undefined;
  }

  if (Array.isArray(point) && point.length !== 2) {
    console.warn('start and end props for LinearGradient must be of the format [x,y] or {x, y}');
    return undefined;
  }

  return Array.isArray(point) ? point : [point.x, point.y];
}

export { NativeLinearGradientPoint };
