import * as THREE from 'three';
import undoable from 'redux-undo';
import undoFilter from '../utils/undoFilter.js';
import * as actions from '../actions/index.js';
import * as d2Tools from '../constants/d2Tools.js';
import * as d3Tools from '../constants/d3Tools.js';
import * as contextTools from '../constants/contextTools.js';
import { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js';
import update from 'react-addons-update';
import { defaultCamera, cameraReducer } from './d3/tools/cameraReducer.js';
import d2AddImageReducer from './d2/addImageReducer.js';
import d2ToolReducer from './d2/toolReducer.js';
import d3ToolReducer from './d3/toolReducer.js';
import d2PinchZoomReducer from './d2/pinchZoomReducer.js';
import d2WheelZoomReducer from './d2/wheelZoomReducer.js';
import d2PanZoomReducer from './d2/panReducer.js';
import selectionReducer from './selectionReducer.js';
import selectionOperationReducer from './selectionOperationReducer.js';
import contextReducer from './contextReducer.js';
import { Matrix, Vector } from 'cal';
import {
  setActiveSpace, addSpaceActive, setActive2D, removeAllObjects, getActive2D, addObject
} from './objectReducers.js';
import menusReducer from './menusReducer.js';
// import createDebug from 'debug';
// const debug = createDebug('d3d:reducer:sketcher');

const initialState = {
  spaces: {
    world: {
      matrix: new THREE.Matrix4(),
      objectIds: []
    }
  },
  objectsById: {},
  activeSpace: 'world',
  objectIdCounter: 0,
  context: {
    color: 0x96cbef
  },
  selection: {
    transform: new Matrix(),
    // array of objects (one per selected object),
    // containing the object id and it's initialTransform (pre selection transform)
    objects: []
  },
  d2: {
    brush: {
      size: BRUSH_SIZES[contextTools.BRUSH_SIZE_MEDIUM]
    },
    eraser: {
      active: false,
      size: ERASER_SIZES[contextTools.ERASER_SIZE_MEDIUM]
    },
    transform: {
      active: false,
      handle: '',
      dragSelect: {
        start: new Vector(),
        end: new Vector()
      }
    },
    trace: {
      start: new Vector(),
      position: new Vector(),
      active: false,
      floodFillData: {
        edge: [],
        fill: []
      }
    },
    tool: d2Tools.FREE_HAND,
    toolbar: {
      open: []
    },
    canvasMatrix: new Matrix()
  },
  d3: {
    height: {
      handle: '',
      active: false
    },
    twist: {
      rotation: 0,
      active: false
    },
    sculpt: {
      handles: [],
      activeHandle: null
    },
    tool: d3Tools.HEIGHT,
    toolbar: {
      open: []
    },
    camera: defaultCamera
  },
  menus: menusReducer(undefined, {})
};

function sketcherReducer(state = initialState, action) {
  // if (action.log !== false) debug(action.type);

  switch (action.category) {
    case actions.CAT_SELECTION:
      const preSelectionState = state;
      state = selectionReducer(state, action);
      // if selection changed
      if (state.selection.objects !== preSelectionState.selection.objects) {
        state = d2ToolReducer(state, action);
        state = d3ToolReducer(state, action);
        state = updateMenus(state, action);
        state = contextReducer(state, action);
      }
      return state;

    default:
      break;
  }

  switch (action.type) {
    case actions.ADD_OBJECT:
      return addObject(state, action.objectData);

    case actions.D2_DRAG_START:
    case actions.D2_DRAG:
    case actions.D2_DRAG_END:
    case actions.D2_TAP:
    case actions.TRACE_DRAG:
    case actions.TRACE_DRAG_END:
    case actions.TRACE_TAP:
    case `${actions.FLOOD_FILL}_PENDING`:
    case `${actions.FLOOD_FILL}_FULFILLED`:
    case `${actions.FLOOD_FILL}_REJECTED`:
    case `${actions.TRACE_FLOOD_FILL}_PENDING`:
    case `${actions.TRACE_FLOOD_FILL}_FULFILLED`:
    case `${actions.TRACE_FLOOD_FILL}_REJECTED`:
    case actions.TRANSFORM_START:
    case actions.TRANSFORM:
    case actions.TRANSFORM_END:
    case actions.MULTITOUCH_TRANSFORM_START:
    case actions.MULTITOUCH_TRANSFORM:
    case actions.MULTITOUCH_TRANSFORM_END:
    case actions.D2_TEXT_INIT:
    case actions.D2_TEXT_INPUT_CHANGE:
    case actions.D2_TEXT_ADD:
    case actions.MOVE_SELECTION:
      return d2ToolReducer(state, action);

    case `${actions.ADD_IMAGE}_FULFILLED`:
      return d2AddImageReducer(state, action);

    case actions.D2_MOUSE_WHEEL:
      return d2WheelZoomReducer(state, action);

    case actions.D2_SECOND_DRAG:
      return d2PanZoomReducer(state, action);

    case actions.D2_MULTITOUCH_START:
    case actions.D2_MULTITOUCH:
    case actions.D2_MULTITOUCH_END:
      state = d2ToolReducer(state, action);
      state = d2PinchZoomReducer(state, action);
      return state;

    case actions.D3_DRAG_START:
    case actions.D3_DRAG:
    case actions.D3_DRAG_END:
    case actions.D3_SECOND_DRAG_START:
    case actions.D3_SECOND_DRAG:
    case actions.D3_SECOND_DRAG_END:
    case actions.D3_TAP:
    case actions.D3_MOUSE_WHEEL:
    case actions.D3_MULTITOUCH_START:
    case actions.D3_MULTITOUCH:
    case actions.D3_MULTITOUCH_END:
    case actions.HEIGHT_START:
    case actions.HEIGHT:
    case actions.HEIGHT_END:
    case actions.TWIST_START:
    case actions.TWIST:
    case actions.TWIST_END:
    case actions.SCULPT_START:
    case actions.SCULPT:
    case actions.SCULPT_END:
    case actions.ADD_SCULPT_HANDLE:
    case actions.REMOVE_SCULPT_HANDLE:
    case actions.STAMP:
      return d3ToolReducer(state, action);

    case actions.D2_CHANGE_TOOL:
      state = setActive2D(state, null);
      state = selectionReducer(state, action);
      state = d2ToolReducer(state, action); // switch and initialize tool
      state = updateMenus(state, action);
      state = contextReducer(state, action);
      return state;

    case actions.D3_CHANGE_TOOL:
      state = d3ToolReducer(state, action); // switch and initialize tool
      state = updateMenus(state, action);
      return state;

    case actions.CONTEXT_CHANGE_TOOL:
      state = contextReducer(state, action);
      state = updateMenus(state, action);
      return state;

    case actions.CLEAR:
      return initialState;

    case actions.OPEN_SKETCH:
      let first = true;
      for (const space of action.data.data.spaces) {
        if (first) {
          state = addSpaceActive(state, space.matrix, 'world');
        } else {
          state = addSpaceActive(state, space.matrix);
        }

        for (const object of space.objects) {
          if (first) {
            state = addObject(state, { ...object, space: 'world' });
          } else {
            state = addObject(state, object);
          }
        }

        first = false;
      }

      return setActiveSpace(state, 'world');

    case actions.DUPLICATE_SELECTION:
    case actions.DELETE_SELECTION:
    case actions.UNION:
    case actions.INTERSECT:
      return selectionOperationReducer(state, action);

    case actions.MENU_OPEN:
    case actions.MENU_CLOSE:
      return updateMenus(state, action);

    case actions.UPDATE_MATRIX:
      return update(state, {
        objectsById: { [action.id]: { transform: { $set: action.transform } } }
      });

    default:
      return state;
  }
}

export default undoable(sketcherReducer, {
  filter: undoFilter,
  initTypes: []
  // debug: true
});


function updateMenus(state, action) {
  return {
    ...state,
    menus: menusReducer(state.menus, action)
  };
}

export function getD2ActiveShape(state) {
  return getActive2D(state);
}

export function isEmpty(state) {
  return Object.keys(state.sketcher.present.objectsById).length === 0;
}
