import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';

import { ReactSVGPanZoom, TOOL_NONE, TOOL_PAN, TOOL_ZOOM_IN, TOOL_ZOOM_OUT, TOOL_AUTO } from 'react-svg-pan-zoom';
import * as constants from '../../constants';
import State from './state';
import * as SharedStyle from '../../shared-style';
import { RulerX, RulerY } from './export';
import project from '../../class/project';
import { setMode } from '../../actions/project-actions';

function mode2Tool(mode) {
  switch (mode) {
    case constants.MODE_2D_PAN:
      return TOOL_PAN;
    case constants.MODE_2D_ZOOM_IN:
      return TOOL_ZOOM_IN;
    case constants.MODE_2D_ZOOM_OUT:
      return TOOL_ZOOM_OUT;
    case constants.MODE_IDLE:
      return TOOL_AUTO;
    default:
      return TOOL_NONE;
  }
}

function mode2PointerEvents(mode) {
  switch (mode) {
    case constants.MODE_DRAWING_LINE:
    case constants.MODE_DRAWING_HOLE:
    case constants.MODE_DRAWING_ITEM:
    case constants.MODE_DRAGGING_HOLE:
    case constants.MODE_DRAGGING_ITEM:
    case constants.MODE_DRAGGING_LINE:
    case constants.MODE_DRAGGING_VERTEX:
      return { pointerEvents: 'none' };

    default:
      return {};
  }
}

function mode2Cursor(mode) {
  switch (mode) {
    case constants.MODE_DRAGGING_HOLE:
    case constants.MODE_DRAGGING_LINE:
    case constants.MODE_DRAGGING_VERTEX:
    case constants.MODE_DRAGGING_ITEM:
      return { cursor: 'move' };

    case constants.MODE_ROTATING_ITEM:
      return { cursor: 'ew-resize' };

    case constants.MODE_WAITING_DRAWING_LINE:
    case constants.MODE_DRAWING_LINE:
      return { cursor: 'crosshair' };
    default:
      return { cursor: 'default' };
  }
}

function mode2DetectAutopan(mode) {
  switch (mode) {
    case constants.MODE_DRAWING_LINE:
    case constants.MODE_DRAGGING_LINE:
    case constants.MODE_DRAGGING_VERTEX:
    case constants.MODE_DRAGGING_HOLE:
    case constants.MODE_DRAGGING_ITEM:
    case constants.MODE_DRAWING_HOLE:
    case constants.MODE_DRAWING_ITEM:
      return true;

    default:
      return false;
  }
}

function extractElementData(node) {
  while (!node.attributes.getNamedItem('data-element-root') && node.tagName !== 'svg') {
    node = node.parentNode;
  }
  if (node.tagName === 'svg') return null;

  return {
    part: node.attributes.getNamedItem('data-part') ? node.attributes.getNamedItem('data-part').value : undefined,
    layer: node.attributes.getNamedItem('data-layer').value,
    prototype: node.attributes.getNamedItem('data-prototype').value,
    selected: node.attributes.getNamedItem('data-selected').value === 'true',
    id: node.attributes.getNamedItem('data-id').value
  }
}

export default function Viewer2D(
  { state, width, height, customContents, handleAreasChanged },
  { viewer2DActions, linesActions, holesActions, verticesActions, itemsActions, areaActions, projectActions, sceneActions, catalog }) {
  
  const [updateImage, setUpdateImage] = useState(false);
  const [updateDrawLine, setUpdateDrawLine] = useState(false);
  const [numberOfAreas, setNumberOfAreas] = useState(0);

  let { viewer2D, mode, scene } = state;
  
  useEffect(() => {
    if (updateDrawLine) {
      
      let areas = scene.getIn(['layers', 'layer-1', 'areas']);

      const newNumberOfAreas = areas.size;
      
      if (numberOfAreas !== newNumberOfAreas) {
        console.log("number of areas changed. deactivate drawing tool.")
        projectActions.unselectAll();
        // causes line get bug
        // projectActions.undo();
        projectActions.setMode(constants.MODE_IDLE);

        handleAreasChanged(true);
      }
      setNumberOfAreas(newNumberOfAreas);
      setUpdateDrawLine(false);
    }
  }, [updateDrawLine])

  // center the view
  // https://github.com/chrvadala/react-svg-pan-zoom/blob/main/docs/documentation.md#props
  const viewerRef = useRef();
  useEffect(() => {
    viewerRef.current.setPointOnViewerCenter(1500, 1500, customContents.viewerZoom ? customContents.viewerZoom : 1);
  }, [])

  // extra code to programmatically handle adding image
  const prevcustomContentsRef = useRef();
  useEffect(() => {
    prevcustomContentsRef.current = customContents.imageUri;
  }, [customContents.imageUri]);
  const prevcustomContents = prevcustomContentsRef.current;
  
  useEffect(() => {
    if (prevcustomContents !== customContents.imageUri && customContents.imageUri) {
      console.log('creating new image');
      
      // select layer-2
      let defaultlayerID = 'layer-1'; // image layer
      let imagelayerID = 'layer-2'; // image layer
      
      let items = scene.getIn(['layers', imagelayerID, 'items']); // get the items
      if (items.size < 1) {
        itemsActions.selectToolDrawingItem('image');
        sceneActions.selectLayer(imagelayerID);
        projectActions.setMode(constants.MODE_DRAWING_ITEM);
        itemsActions.updateDrawingItem(imagelayerID, 1500, 1500); // add new image to center
        projectActions.setMode(constants.MODE_DRAWING_ITEM);
        itemsActions.endDrawingItem(imagelayerID, 1500, 1500); // deselect create image mode
        // change to idle mode
        projectActions.setMode(constants.MODE_IDLE);
        sceneActions.selectLayer(defaultlayerID);
      } 
      
      setUpdateImage(true);
    }
  }, [customContents.imageUri])
  
  useEffect(() => {
    if (updateImage) {
      let defaultlayerID = 'layer-1'; // image layer
      let imagelayerID = 'layer-2'; // image layer
      
      // get newly created image item
      // https://github.com/cvdlab/react-planner/issues/147
      let items = scene.getIn(['layers', imagelayerID, 'items']); // get the items
      if (items.size == 1) {
        sceneActions.selectLayer(imagelayerID);
        let item = items.first();
        // console.log(item)
        itemsActions.selectItem(imagelayerID, item.id);
        let properties = item.properties;
        properties = properties.setIn(['imageUri'], customContents.imageUri);
        // scene = scene.setIn(['layers', imagelayerID, 'items', item.id, 'properties'], properties);
        projectActions.setProperties(properties);
        sceneActions.selectLayer(defaultlayerID);
      }
      
      setUpdateImage(false);
    }
  }, [updateImage])

  let layerID = scene.selectedLayer;
  
  let mapCursorPosition = ({ x, y }) => {
    return { x, y: -y + scene.height }
  };

  let onMouseMove = viewerEvent => {

    //workaround that allow imageful component to work
    let evt = new Event('mousemove-planner-event');
    evt.viewerEvent = viewerEvent;
    document.dispatchEvent(evt);

    let { x, y } = mapCursorPosition(viewerEvent);

    projectActions.updateMouseCoord({ x, y });

    switch (mode) {
      case constants.MODE_DRAWING_LINE:
        // console.log("onMouseMove constants.MODE_DRAWING_LINE");
        linesActions.updateDrawingLine(x, y, state.snapMask);
        break;

      case constants.MODE_DRAWING_HOLE:
        // console.log("onMouseMove constants.MODE_DRAWING_HOLE");
        holesActions.updateDrawingHole(layerID, x, y);
        break;

      case constants.MODE_DRAWING_ITEM:
        itemsActions.updateDrawingItem(layerID, x, y);
        break;

      case constants.MODE_DRAGGING_HOLE:
        holesActions.updateDraggingHole(x, y);
        break;

      case constants.MODE_DRAGGING_LINE:
        linesActions.updateDraggingLine(x, y, state.snapMask);
        break;

      case constants.MODE_DRAGGING_VERTEX:
        verticesActions.updateDraggingVertex(x, y, state.snapMask);
        break;

      case constants.MODE_DRAGGING_ITEM:
        itemsActions.updateDraggingItem(x, y);
        break;

      case constants.MODE_ROTATING_ITEM:
        itemsActions.updateRotatingItem(x, y);
        break;
    }

    viewerEvent.originalEvent.stopPropagation();
  };

  let onMouseDown = viewerEvent => {
    let event = viewerEvent.originalEvent;

    //workaround that allow imageful component to work
    let evt = new Event('mousedown-planner-event');
    evt.viewerEvent = viewerEvent;
    document.dispatchEvent(evt);

    let { x, y } = mapCursorPosition(viewerEvent);

    if (mode === constants.MODE_IDLE) {
      let elementData = extractElementData(event.target);
      if (!elementData || !elementData.selected) return;

      switch (elementData.prototype) {
        case 'lines':
          linesActions.beginDraggingLine(elementData.layer, elementData.id, x, y, state.snapMask);
          break;

        case 'vertices':
          verticesActions.beginDraggingVertex(elementData.layer, elementData.id, x, y, state.snapMask);
          break;

        case 'items':
          if (elementData.part === 'rotation-anchor')
            itemsActions.beginRotatingItem(elementData.layer, elementData.id, x, y);
          else
            itemsActions.beginDraggingItem(elementData.layer, elementData.id, x, y);
          break;

        case 'holes':
          holesActions.beginDraggingHole(elementData.layer, elementData.id, x, y);
          break;

        default: break;
      }
    }
    event.stopPropagation();
  };

  let onMouseUp = viewerEvent => {
    let event = viewerEvent.originalEvent;

    let evt = new Event('mouseup-planner-event');
    evt.viewerEvent = viewerEvent;
    document.dispatchEvent(evt);

    let { x, y } = mapCursorPosition(viewerEvent);

    switch (mode) {

      case constants.MODE_IDLE:
        let elementData = extractElementData(event.target);

        if (elementData && elementData.selected) return;

        switch (elementData ? elementData.prototype : 'none') {
          case 'areas':
            areaActions.selectArea(elementData.layer, elementData.id);
            break;

          case 'lines':
            linesActions.selectLine(elementData.layer, elementData.id);
            break;

          case 'holes':
            holesActions.selectHole(elementData.layer, elementData.id);
            break;

          case 'items':
            itemsActions.selectItem(elementData.layer, elementData.id);
            break;

          case 'none':
            projectActions.unselectAll();
            break;
        }
        break;

      case constants.MODE_WAITING_DRAWING_LINE:
        linesActions.beginDrawingLine(layerID, x, y, state.snapMask);
        break;

      case constants.MODE_DRAWING_LINE:
        // console.log("onMouseUp constants.MODE_DRAWING_LINE");
        // de-activate hole drawing after drawing
        
        // let areas1 = scene.getIn(['layers', 'layer-1', 'areas']);
        // console.log(areas1)

        linesActions.endDrawingLine(x, y, state.snapMask);
        // count number of new areas
        // if change then exit

        // let areas2 = scene.getIn(['layers', 'layer-1', 'areas']);
        // console.log(areas2)
        // if (areas1.size === areas2.size) {
        // }

        linesActions.beginDrawingLine(layerID, x, y, state.snapMask);
        setUpdateDrawLine(true);
        break;

      case constants.MODE_DRAWING_HOLE:
        // console.log("onMouseUp constants.MODE_DRAWING_HOLE");
        holesActions.endDrawingHole(layerID, x, y);
        projectActions.setMode(constants.MODE_IDLE);
        break;

      case constants.MODE_DRAWING_ITEM:
        // console.log("onMouseUp constants.MODE_DRAWING_ITEM");
        itemsActions.endDrawingItem(layerID, x, y);
        break;

      case constants.MODE_DRAGGING_LINE:
        linesActions.endDraggingLine(x, y, state.snapMask);
        break;

      case constants.MODE_DRAGGING_VERTEX:
        verticesActions.endDraggingVertex(x, y, state.snapMask);
        break;

      case constants.MODE_DRAGGING_ITEM:
        itemsActions.endDraggingItem(x, y);
        break;

      case constants.MODE_DRAGGING_HOLE:
        holesActions.endDraggingHole(x, y);
        break;

      case constants.MODE_ROTATING_ITEM:
        itemsActions.endRotatingItem(x, y);
        break;
    }

    event.stopPropagation();
  };

  let onChangeValue = (value) => {
    projectActions.updateZoomScale(value.a);
    return viewer2DActions.updateCameraView(value)
  };

  let onChangeTool = (tool) => {
    switch (tool) {
      case TOOL_NONE:
        projectActions.selectToolEdit();
        break;

      case TOOL_PAN:
        viewer2DActions.selectToolPan();
        break;

      case TOOL_ZOOM_IN:
        viewer2DActions.selectToolZoomIn();
        break;

      case TOOL_ZOOM_OUT:
        viewer2DActions.selectToolZoomOut();
        break;
    }
  };

  let { e, f, SVGWidth, SVGHeight } = state.get('viewer2D').toJS();

  let rulerSize = 0; //px
  let rulerUnitPixelSize = 100;
  let rulerBgColor = SharedStyle.PRIMARY_COLOR.main;
  let rulerFnColor = SharedStyle.COLORS.white;
  let rulerMkColor = SharedStyle.SECONDARY_COLOR.main;
  let sceneWidth = SVGWidth || state.getIn(['scene', 'width']);
  let sceneHeight = SVGHeight || state.getIn(['scene', 'height']);
  let sceneZoom = state.zoom || 1;
  let rulerXElements = Math.ceil( sceneWidth / rulerUnitPixelSize ) + 1;
  let rulerYElements = Math.ceil( sceneHeight / rulerUnitPixelSize ) + 1;

  return (
    <div style={{
      margin: 0,
      padding: 0,
      display: 'grid',
      gridRowGap: '0',
      gridColumnGap: '0',
      gridTemplateColumns: `${rulerSize}px ${width - rulerSize}px`,
      gridTemplateRows: `${rulerSize}px ${height - rulerSize}px`,
      position: 'relative'
    }}>
      <div style={{ gridColumn: 1, gridRow: 1, backgroundColor: rulerBgColor }}></div>
      <div style={{ gridRow: 1, gridColumn: 2, position: 'relative', overflow: 'hidden' }} id="rulerX">
      { sceneWidth ? <RulerX
          unitPixelSize={rulerUnitPixelSize}
          zoom={sceneZoom}
          mouseX={state.mouse.get('x')}
          width={width - rulerSize}
          zeroLeftPosition={e || 0}
          backgroundColor={rulerBgColor}
          fontColor={rulerFnColor}
          markerColor={rulerMkColor}
          positiveUnitsNumber={rulerXElements}
          negativeUnitsNumber={0}
        /> : null }
      </div>
      <div style={{ gridColumn: 1, gridRow: 2, position: 'relative', overflow: 'hidden' }} id="rulerY">
        { sceneHeight ? <RulerY
          unitPixelSize={rulerUnitPixelSize}
          zoom={sceneZoom}
          mouseY={state.mouse.get('y')}
          height={height - rulerSize}
          zeroTopPosition={((sceneHeight * sceneZoom) + f) || 0}
          backgroundColor={rulerBgColor}
          fontColor={rulerFnColor}
          markerColor={rulerMkColor}
          positiveUnitsNumber={rulerYElements}
          negativeUnitsNumber={0}
        /> : null }
      </div>
      <ReactSVGPanZoom
        style={{ gridColumn: 2, gridRow: 2 }}
        width={width - rulerSize}
        height={height - rulerSize}
        value={viewer2D.isEmpty() ? null : viewer2D.toJS()}
        onChangeValue={onChangeValue}
        tool={mode2Tool(mode)}
        onChangeTool={onChangeTool}
        detectAutoPan={mode2DetectAutopan(mode)}
        onMouseDown={onMouseDown}
        onMouseMove={onMouseMove}
        onMouseUp={onMouseUp}
        miniaturePosition="none"
        toolbarPosition="none"
        background="#FFF"
        ref={viewerRef}
        zoomOnViewerCenter={1}
      >

        <svg width={scene.width} height={scene.height}>
          <defs>
            <pattern id="diagonalFill" patternUnits="userSpaceOnUse" width="4" height="4" fill="#FFF">
              <rect x="0" y="0" width="4" height="4" fill="aquamarine" />
              <path d="M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2" style={{ stroke: '#FF1493', strokeWidth: 0.5 }} />
            </pattern>
            <pattern id="landlordDiagonalFill" patternUnits="userSpaceOnUse" width="4" height="4" fill="#FFF">
              <rect x="0" y="0" width="4" height="4" fill="#e5a76dCC" opacity={0.7}/>
              <path d="M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2" style={{ stroke: '#808080', strokeWidth: 0.5 }} />
            </pattern>
            <pattern id="landlordSelectedDiagonalFill" patternUnits="userSpaceOnUse" width="4" height="4" fill="#FFF">
              <rect x="0" y="0" width="4" height="4" fill="#e5a76dCC" />
              <path d="M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2" style={{ stroke: '#808080', strokeWidth: 0.5 }} />
            </pattern>
          </defs>
          <g style={Object.assign(mode2Cursor(mode), mode2PointerEvents(mode))}>
            <State state={state} catalog={catalog} />
          </g>
        </svg>

      </ReactSVGPanZoom>
    </div>
  );
}


Viewer2D.propTypes = {
  state: PropTypes.object.isRequired,
  width: PropTypes.number.isRequired,
  height: PropTypes.number.isRequired,
};

Viewer2D.contextTypes = {
  viewer2DActions: PropTypes.object.isRequired,
  linesActions: PropTypes.object.isRequired,
  holesActions: PropTypes.object.isRequired,
  verticesActions: PropTypes.object.isRequired,
  itemsActions: PropTypes.object.isRequired,
  areaActions: PropTypes.object.isRequired,
  projectActions: PropTypes.object.isRequired,
  sceneActions: PropTypes.object.isRequired,
  catalog: PropTypes.object.isRequired,
};
