import React, { useEffect, useMemo, useState } from 'react';
import { ColorFormats } from 'tinycolor2';

import styles from './color-picker.scss';

import { rgbToHex, rgbToHsv, hsvToRgb, hsvToHslString, rgbaToString } from './color';
import { Mouse, useKey } from '@vev/utils';

type Props = {
  rgba: ColorFormats.RGBA;
  hue: number;
  dragging: boolean;
  onDrag: (dragging: boolean) => void;
  onChange: (value: string) => void;
};

export function ColorSpace({ rgba, hue, dragging, onDrag, onChange }: Props) {
  // Convert to hsv
  const hsv = useMemo(() => {
    const value = rgbToHsv(
      {
        h: hue,
      },
      rgba,
    );
    value.h = hue;
    return value;
  }, [rgba, hue]);

  // Colordot position
  const [pos, setPos] = useState({ left: hsv.s * 100 + '%', bottom: hsv.v * 100 + '%' });
  // Background color
  const [hsl, setHsl] = useState(hsvToHslString(hsv));
  // Colordot color
  const hex = rgbToHex(rgba);
  // Force re-render
  const [, forceUpdate] = useState(0);
  // Container cl
  const cl = dragging ? `${styles.colorBox} ${styles.dragging}` : styles.colorBox;
  const boxRect = React.useRef<Partial<DOMRect>>({ top: 0, left: 0, width: 0, height: 0 });

  /** Change between controlled and uncontrolled component based on dragging */
  useEffect(() => {
    if (!dragging) {
      setPos({ left: hsv.s * 100 + '%', bottom: hsv.v * 100 + '%' });
      setHsl(hsvToHslString(hsv));
    }
  }, [hsv]);

  const handleChange = (e: MouseEvent | React.MouseEvent) => {
    const { top = 0, left = 0, width = 1, height = 1 } = boxRect.current;
    const s = Math.max(0, Math.min(1, (e.pageX - left) / width));
    const v = 1 - Math.max(0, Math.min(1, (e.pageY - top) / height));

    const nextRgba = hsvToRgb({ h: hsv.h, s, v, a: rgba.a || 1 } as ColorFormats.HSVA);
    setPos({ left: s * 100 + '%', bottom: v * 100 + '%' });
    onChange(rgbaToString(nextRgba));
  };

  const handleMouseDown = (e: React.MouseEvent) => {
    onDrag(true);
    Mouse.on('mousemove', handleChange);
    Mouse.on('mouseup', handleMouseUp);
    boxRect.current = e.currentTarget.getBoundingClientRect();
    handleChange(e);
  };

  const handleMouseUp = () => {
    onDrag(false);
    Mouse.off('mousemove', handleChange);
    Mouse.off('mouseup', handleMouseUp);
    // force re-render to remove cursor style
    forceUpdate(Date.now());
  };

  useKey('Escape', handleMouseUp);

  return (
    <div
      data-editor-field="silke-color-space"
      className={cl}
      style={{ backgroundColor: hsl }}
      onMouseDown={handleMouseDown}
    >
      <div className={styles.whiteGrad}>
        <div className={styles.blackGrad} />
      </div>
      <div className={styles.colorDot} style={{ ...pos, background: '#' + hex }} />
    </div>
  );
}
