import {
  court,
  darkLine,
  gray125,
  gray200,
  gray250,
  gray400,
  gray900,
  lavender,
  lightLine,
  moss,
  newBlue,
} from '@/styles/colors';
import { keyframes } from '@emotion/react';
import styled from '@emotion/styled';

const code = `
const LOOKAHEAD_MS = 4;
const LOOKAHEAD_SAMPLES = Math.floor(LOOKAHEAD_MS * (sampleRate / 1000));

const Gate = ({ input, output, detector, threshold, floor, attack, release, hold }) => {
  const appliedGainRef = useVal(0);
  const peakHoldRef = useVal(0);

  const delayedSignal = useDelayedStereo(input, LOOKAHEAD_SAMPLES);

  for (let i = 0; i < delayedSignal[0].length; i ++) {
    const thresholdDB = threshold[i];
    const floorDB = floor[i];
    const attackRate = 1/(attack[i] + LOOKAHEAD_MS);
    const releaseRate = 1/release[i];
    const holdRate = 1/hold[i];

    let rawAmplitude = 0;

    for (let c = 0; c < detector.length; c ++) {
      const channelAmplitude = Math.abs(detector[c][i]);
      if (channelAmplitude > rawAmplitude) {
        rawAmplitude = channelAmplitude;
      }
    }

    peakHoldRef.current = Math.max(peakHoldRef.current * (1 - holdRate), rawAmplitude);

    const amplitudeDB = multiplierToDb(peakHoldRef.current)

    let targetGainDB = amplitudeDB < thresholdDB
      ? floorDB
      : 0;

    const gainDelta = targetGainDB < appliedGainRef.current ? releaseRate : attackRate;

    appliedGainRef.current = Math.min(0, ((1 - gainDelta) * appliedGainRef.current) + (gainDelta * targetGainDB));

    const gain = dbToMultiplier(appliedGainRef.current);

    for (let c = 0; c < delayedSignal.length; c ++) {
      output[c][i] = delayedSignal[c][i] * gain;
    }
  }
}

this.constructDevice = () => constructPerBufferDevice({
  implementation: Gate,
  inputs: {
    input: stereo('In'),
    detector: stereo('Detector'),
    threshold: numberSetting('Threshold', { defaultValue: -20, min: -64, max: 0, unit: InterfaceUnit.LiteralDecibels }),
    floor: numberSetting('Floor', { defaultValue: -40, min: -80, max: 0, unit: InterfaceUnit.LiteralDecibels }),
    attack: adsrTimeSetting('Attack', 0.1),
    hold: adsrTimeSetting('Hold', 10),
    release: adsrTimeSetting('Release', 15),
  },
  outputs: {
    output: stereo('Out', LOOKAHEAD_SAMPLES),
  }
});
const LOOKAHEAD_MS = 4;
const LOOKAHEAD_SAMPLES = Math.floor(LOOKAHEAD_MS * (sampleRate / 1000));

const Gate = ({ input, output, detector, threshold, floor, attack, release, hold }) => {
  const appliedGainRef = useVal(0);
  const peakHoldRef = useVal(0);

  const delayedSignal = useDelayedStereo(input, LOOKAHEAD_SAMPLES);
`;

const GridAnimationWrapper = styled.div`
  background: ${lightLine};
  width: 100%;
  aspect-ratio: 1;
`;

const Container = styled.div`
  position: relative;
  height: 100%;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
`;

const DeviceNodeWrapper = styled.div``;

const JSDeviceWrapper = styled.div`
  height: auto;
  font-family: Architekt;
  width: 190px;
  border-radius: 12px;
  border: 1px solid ${gray400};
  background-color: ${gray125};
`;

const DeviceHeader = styled.header`
  position: relative;
  padding: 6px 12px 5px 12px;
  border-bottom: 1px solid ${gray400};
  font-weight: 600;
  border-top-right-radius: 12px;
  border-top-left-radius: 12px;
  transition: all 0.5s;
`;

const DeviceBody = styled.div<{ shade?: boolean }>`
  background-color: ${({ shade }) => (shade ? gray200 : 'transparent')};
  border-bottom-left-radius: 12px;
  border-bottom-right-radius: 12px;
  column-gap: 40px;
  padding-top: 4px;
  padding-bottom: 8px;
`;

const ConnectionListWrapper = styled.div<{ leftSide?: boolean }>`
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  align-items: ${({ leftSide }) => (leftSide ? 'flex-start' : 'flex-end')};
  margin-left: -6px;
  margin-right: -6px;
  > div:after {
    ${({ leftSide }) => (leftSide ? 'left: -8rem' : 'right: -8rem')};
  }
`;

const ConnectionDot = styled.div<{ color: string }>`
  width: 10px;
  height: 10px;
  border-radius: 12px;
  border: 1px solid ${({ color }) => color};
  background-color: ${({ color }) => color};
  position: relative;
  transition: all 0.5s;
`;

const ConnectionWrapper = styled.div`
  color: ${gray900};
  position: relative;
  padding: 5px 0;
  left: 1px;
  display: flex;
  align-items: center;
  > * + * {
    margin-left: 7px;
  }
`;

const Connections = ({
  input,
  children,
  color,
}: {
  input: boolean;
  children?: JSX.Element | string;
  color: string;
}) => {
  return (
    <ConnectionListWrapper leftSide={input}>
      <ConnectionWrapper>
        {input ? (
          <>
            <ConnectionDot color={color}></ConnectionDot>
            <div>{children}</div>
          </>
        ) : (
          <>
            <div>{children}</div>
            <ConnectionDot color={color}></ConnectionDot>
          </>
        )}
      </ConnectionWrapper>
    </ConnectionListWrapper>
  );
};

const Node = ({ children, color, background }: { children: string; color: string; background: string }) => {
  return (
    <DeviceNodeWrapper color={color}>
      <JSDeviceWrapper>
        <DeviceHeader style={{ backgroundColor: background, color }}>{children}</DeviceHeader>
        <DeviceBody>
          <Connections color={color} input={true}>
            Gain
          </Connections>
          <Connections color={color} input={true}>
            Balance
          </Connections>
        </DeviceBody>
      </JSDeviceWrapper>
    </DeviceNodeWrapper>
  );
};

const Cover = styled.div`
  position: absolute;
  height: 100%;
  width: 50%;
  background-color: #000000cc;
  top: 0;
  right: 0;
  overflow: hidden;
  color: white;
  font-family: Montreal;
  padding: 10px;
  font-size: 12px;
  backdrop-filter: blur(4px);
`;

const scroll = keyframes`
0% { transform: translateY(0); }
100% { transform: translateY(-80%); }
`;
const CodeContainer = styled.div`
  color: ${darkLine};
  white-space: pre;
  animation: ${scroll} 60s linear infinite;
`;

const devices = [
  { name: 'EQ', color: moss, background: '#464A41' },
  { name: 'Distortion', color: lavender, background: '#383544' },
  { name: 'Compressor', color: court, background: '#213938' },
  { name: 'Limiter', color: newBlue, background: '#263A64' },
];

const LiveCoding = () => {
  return (
    <GridAnimationWrapper>
      <Container>
        <Node background={gray250} color={darkLine}>
          Device
        </Node>
        <Cover>
          <CodeContainer>{code}</CodeContainer>
        </Cover>
      </Container>
    </GridAnimationWrapper>
  );
};

export default LiveCoding;
