import update from 'react-addons-update';
import ClipperShape from 'clipper-js';
import { Matrix } from 'cal';
import { addObject, removeObject } from './objectReducers.js';
import { recursiveClone } from '../utils/clone.js';
import { shapeToPoints } from '../shape/shapeToPoints.js';
import { applyMatrixOnShape, pathToVectorPath } from '../utils/vectorUtils.js';
import subtractShapeFromState from '../utils/subtractShapeFromState.js';
import * as d2Tools from '../constants/d2Tools';
import { CLIPPER_PRECISION } from '../constants/d2Constants.js';
import * as actions from '../actions/index.js';

const LINE_WIDTH = 0.5;

export default function (state, action) {
  switch (action.type) {
    case actions.DELETE_SELECTION:
      for (const { id } of state.selection.objects) {
        state = removeObject(state, id);
      }

      state = update(state, {
        selection: {
          objects: { $set: [] }
        }
      });

      return state;

    case actions.DUPLICATE_SELECTION:
      for (const { id } of state.selection.objects) {
        const shapeData = state.objectsById[id];
        state = addObject(state, recursiveClone(shapeData));
      }

      // force update selection so alpha is redrawn
      state = update(state, {
        selection: {
          objects: { $set: [...state.selection.objects] }
        }
      });

      return state;

    case actions.UNION: {
      let unionShape = new ClipperShape([], true);

      const shapeDataDictation = state.objectsById[state.selection.objects[0].id];

      for (const { id } of state.selection.objects) {
        const shapeData = state.objectsById[id];

        if (!shapeData.fill) continue;

        state = removeObject(state, id);

        const shapes = applyMatrixOnShape(
          shapeToPoints(shapeData).reduce((a, { points, holes }) => a.concat([points, ...holes]), []),
          shapeData.transform
        );

        const shape = new ClipperShape(shapes, shapeData.fill, true, false, false)
          .scaleUp(CLIPPER_PRECISION)
          .round();
        unionShape = unionShape.union(shape);
      }

      const unionShapes = unionShape.scaleDown(CLIPPER_PRECISION).seperateShapes().map(shape => {
        shape = shape
          .removeDuplicates()
          .mapToLower()
          .map(pathToVectorPath);
        shape.forEach(path => path.push(path[0].clone()));
        return shape;
      });

      for (const shape of unionShapes) {
        const [points, ...holes] = shape;

        state = addObject(state, {
          ...recursiveClone(shapeDataDictation),
          type: 'COMPOUND_PATH',
          transform: new Matrix(),
          points,
          holes
        });
      }

      state = update(state, {
        selection: {
          objects: { $set: [] }
        }
      });

      return state;
    }

    case actions.INTERSECT: {
      let unionShape = new ClipperShape([], true);

      for (const { id } of state.selection.objects) {
        const shapeData = state.objectsById[id];

        const shapes = applyMatrixOnShape(
          shapeToPoints(shapeData).reduce((a, { points, holes }) => a.concat([points, ...holes]), []),
          shapeData.transform
        );

        let shape = new ClipperShape(shapes, shapeData.fill, true, false, false)
          .scaleUp(CLIPPER_PRECISION)
          .round();
        if (!shapeData.fill) {
          shape = shape
            .offset(LINE_WIDTH * CLIPPER_PRECISION, { jointType: 'jtSquare', endType: 'etOpenButt' })
            .simplify('pftNonZero');
        }
        unionShape = unionShape.union(shape);
      }

      unionShape = unionShape.scaleDown(CLIPPER_PRECISION);

      state = subtractShapeFromState(state, unionShape, d2Tools.ERASER, {
        scale: CLIPPER_PRECISION,
        skip: state.selection.objects.map(({ id }) => id)
      });

      // force update selection so alpha is redrawn
      state = update(state, {
        selection: {
          objects: { $set: [...state.selection.objects] }
        }
      });

      return state;
    }

    default:
      return state;
  }
}
