/** @jsx jsx */
import { jsx } from '@emotion/core';
import styled from '@emotion/styled';
import { gray800, gray300, gray400, blue500 } from '../../styles/colors_v2';
import { useState, useCallback, useRef, useEffect } from 'react';
import { Curve } from '../../types';

const Wrapper = styled.div`
  display: grid;
  grid-column-gap: 10px;
  grid-template-columns: 1fr 75px;
`;

const SliderContainer = styled.div`
  background-color: ${gray300};
  height: 10px;
  border-radius: 5px;
  position: relative;
  cursor: pointer;
  margin: 10px 0;
  &:before {
    content: '';
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    top: -10px;
    bottom: -10px;
  }
`;

const SliderBar = styled.div<{ position: number }>`
  position: absolute;
  height: 4px;
  border-radius: 2px;
  background-color: ${blue500};
  left: 3px;
  top: 3px;
  width: ${({ position }) => (100 * position).toFixed(2)}%;
`;

const SliderDot = styled.div`
  position: absolute;
  right: -3px;
  top: -3px;
  height: 10px;
  width: 10px;
  border-radius: 5px;
  background-color: inherit;
`;

const Input = styled.input`
  background-color: transparent;
  color: ${gray800};
  height: 30px;
  padding: 0 0 0 6px;
  font-family: inherit;
  border: 1px solid ${gray400};
  border-radius: 2px;
  &:focus {
    border-color: ${blue500};
  }
`;

const SliderControl = ({ min, max, value, setValue, curve }: { min: number, max: number, value: number, setValue: (value: number) => void, curve: Curve }) => {
  const manualUpdating = useRef(false);
  const [internalValue, setInternalValue] = useState(String(value));
  const receiveEvent = useCallback(
    (event) => {
      manualUpdating.current = true;
      setInternalValue(event.currentTarget.value);
      setValue(Number(event.currentTarget.value));
    },
    [setInternalValue, setValue]
  );
  useEffect(() => {
    if (manualUpdating.current) {
      manualUpdating.current = false;
    } else {
      setInternalValue(value.toFixed(2));
    }
  }, [setInternalValue, value]);

  const toRange = useCallback(
    (input: number) => (
      curve === Curve.Exponential
        ? min * Math.pow(2, input * (Math.log(max / min) / Math.log(2)))
        : min + (input * (max - min))
    ),
    [curve, max, min]
  );

  const fromRange = useCallback(
    (input: number) => (
      curve === Curve.Exponential
        ? (Math.log((input / min)) / Math.log(2)) / (Math.log(max / min) / Math.log(2))
        : (input - min) / (max - min)
    ),
    [curve, max, min]
  );

  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(
    () => {
      const ref = containerRef.current;
      if (ref) {
        const rect = ref.getBoundingClientRect();

        const computeValue = (clientX: number) => {
          const linearInput = Math.min(1, Math.max(0, ((clientX - rect.left) / (rect.width))));
          return toRange(linearInput);
        }
        const handleMouseDown = (e: MouseEvent) => {
          document.body.classList.add('no-select');
          setValue(computeValue(e.clientX));
          const handleMove = (e: MouseEvent) => {
            setValue(computeValue(e.clientX));
          }
          const handleUp = () => {
            document.body.classList.remove('no-select');
            window.removeEventListener('mouseup', handleUp);
            window.removeEventListener('mousemove', handleMove);
          }
          window.addEventListener('mousemove', handleMove);
          window.addEventListener('mouseup', handleUp);
        };
        ref.addEventListener('mousedown', handleMouseDown);
        return () => {
          ref.removeEventListener('mousedown', handleMouseDown);
        }
      }
    },
    [setValue, toRange]
  );

  return (
    <Wrapper>
      <SliderContainer ref={containerRef}>
        <SliderBar position={fromRange(value)}>
          <SliderDot />
        </SliderBar>
      </SliderContainer>
      <Input type="number" value={internalValue} onChange={receiveEvent} min={min} max={max} />
    </Wrapper>
  );
};

export default SliderControl;
