import _ from 'lodash';
import React, { forwardRef } from 'react';
import { Group } from '../../../../components/Group';
import { Path } from '../../../../components/Path';
import { Mark, PlotContext, plotMarkReified } from '../Plot';
import { curveCatmullRom, line as d3Line } from 'd3-shape';
import { withBluefishFn, BBox } from '../../../../bluefish';
import { NewBBox } from '../../../../NewBBox';
import { PaperScope } from 'paper/dist/paper-core';

export const line = <T,>(data: T[], { x, y, color }: { x: string; y: string; color: string }): Mark => ({
  data,
  encodings: { x, y, color },
  scale: (
    channels: { X: number[]; Y: number[]; COLOR: string[] },
    scales: { xScale: (x: number) => number; yScale: (y: number) => number; colorScale: (color: string) => string },
    _dimensions: any,
  ) => {
    const { X, Y, COLOR } = channels;

    const { xScale, yScale, colorScale } = scales;

    const indices = _.range(X.length);
    return {
      // TODO: fix this!
      stroke: colorScale(COLOR[0]),
      points: indices.map((i) => ({
        x: xScale(X[i]),
        y: yScale(Y[i]),
      })),
    };
  },
  render: (data: { stroke: string; points: { x: number; y: number }[] }) => {
    return (
      <Path
        d={d3Line().curve(curveCatmullRom)(data.points.map(({ x, y }) => [x, y])) ?? ''}
        fill={'none'}
        stroke={data.stroke}
        strokeWidth={1.5}
        strokeLinecap={'round'}
        strokeLinejoin={'round'}
        strokeMiterlimit={1}
      />
    );
  },
});

export const Line: React.FC<{ data?: any[]; encodings: { x: string; y: string; color: string } }> = forwardRef(
  (props, ref) => {
    const context = React.useContext(PlotContext);

    const { encodings } = props;
    const data = props.data ?? context.data;
    const { x, y, color } = encodings;
    const mark = line(data, { x, y, color });
    return <Group ref={ref}>{plotMarkReified(mark, context.scales, context.dimensions)}</Group>;
  },
);

export type RectScaleProps = React.SVGProps<SVGRectElement> & {
  yScale: (d: any) => (y: number) => number;
};

export const RectScale = withBluefishFn(
  (props: RectScaleProps) => {
    const { x, y, width, height } = props;

    return (_, constraints) => {
      const scaledY = y !== undefined ? props.yScale(constraints.height)(+y) : undefined;
      const scaledHeight = height !== undefined ? props.yScale(constraints.height)(+height) : undefined;

      return {
        left: x !== undefined ? +x : undefined,
        top: scaledY,
        width: width !== undefined ? +width : undefined,
        height: scaledHeight,
      };
    };
  },
  forwardRef((props: RectScaleProps & { $bbox?: Partial<NewBBox> }, ref: any) => {
    const { $bbox, yScale, ...rest } = props;
    return (
      // translate and scale based on $bbox.coord
      <g
        ref={ref}
        transform={`translate(${$bbox?.coord?.translate?.x ?? 0} ${$bbox?.coord?.translate?.y ?? 0})
scale(${$bbox?.coord?.scale?.x ?? 1} ${$bbox?.coord?.scale?.y ?? 1})`}
      >
        <rect
          {...rest}
          x={$bbox?.left ?? 0}
          y={$bbox?.top ?? 0}
          width={$bbox?.width ?? 0}
          height={$bbox?.height ?? 0}
        />
      </g>
    );
  }),
);
RectScale.displayName = 'RectScale';

export type PathProps = React.SVGProps<SVGPathElement> & Partial<BBox>;

export const PathScale = withBluefishFn(
  ({ d }: PathProps) => {
    const canvas = document.createElement('canvas');
    const paperScope = new PaperScope();
    paperScope.setup(canvas);
    const path = new paperScope.Path(d!);
    const bounds = path.bounds;
    return () => {
      return {
        left: bounds.left,
        top: bounds.top,
        width: bounds.width,
        height: bounds.height,
      };
    };
  },
  (props: PathProps & { $bbox?: Partial<NewBBox> }) => {
    const { $bbox, ...rest } = props;
    return (
      <g transform={`translate(${$bbox!.coord?.translate?.x ?? 0}, ${$bbox!.coord?.translate?.y ?? 0})`}>
        <path {...rest} />
      </g>
    );
  },
);
