import { is, isAny } from '../utils/modelUtil';
import { findFreePosition, generateGetNextPosition, getConnectedDistance } from '../autoPlace/YmAutoPlaceUtil';
import { isObject } from 'min-dash';
import { bpmnStart } from '../config/variableName';
import { DEFAULT_CONNECT } from '../config/constants';
var HIGH_PRIORITY = 2000;
function distance(a: any, b: any) {
  return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
}
/**
 * Convert the given bounds to a { top, left, bottom, right } descriptor.
 *
 * @param {Point|Rect} bounds
 *
 * @return {RectTRBL}
 */
export function asTRBL(bounds: any) {
  return {
    top: bounds.y,
    right: bounds.x + (bounds.width || 0),
    bottom: bounds.y + (bounds.height || 0),
    left: bounds.x,
  };
}
export function getConnectionMid(connection: any) {
  var waypoints = connection.waypoints;
  var parts = waypoints.reduce(function (parts: any, point: any, index: any) {
    var lastPoint = waypoints[index - 1];
    if (lastPoint) {
      var lastPart = parts[parts.length - 1];
      var startLength = (lastPart && lastPart.endLength) || 0;
      var length = distance(lastPoint, point);
      parts.push({
        start: lastPoint,
        end: point,
        startLength: startLength,
        endLength: startLength + length,
        length: length,
      });
    }
    return parts;
  }, []);
  var totalLength = parts.reduce(function (length: any, part: any) {
    return length + part.length;
  }, 0);
  // find which segement contains middle point
  var midLength = totalLength / 2;
  var i = 0;
  var midSegment = parts[i];
  while (midSegment.endLength < midLength) {
    midSegment = parts[++i];
  }
  // calculate relative position on mid segment
  var segmentProgress = (midLength - midSegment.startLength) / midSegment.length;
  var midPoint = {
    x: midSegment.start.x + (midSegment.end.x - midSegment.start.x) * segmentProgress,
    y: midSegment.start.y + (midSegment.end.y - midSegment.start.y) * segmentProgress,
  };

  return midPoint;
}
export function roundPoint(point: any) {
  return {
    x: Math.round(point.x),
    y: Math.round(point.y),
  };
}
export function getBoundsMid(bounds: any) {
  return roundPoint({
    x: bounds.x + (bounds.width || 0) / 2,
    y: bounds.y + (bounds.height || 0) / 2,
  });
}
export function getMid(element: any) {
  if (!!element.waypoints) return getConnectionMid(element);
  return getBoundsMid(element);
}
export function getOrientation(rect: any, reference: any, padding: any) {
  padding = padding || 0;
  if (!isObject(padding)) padding = { x: padding, y: padding };

  var rectOrientation = asTRBL(rect),
    referenceOrientation = asTRBL(reference);
  var top = rectOrientation.bottom + padding.y <= referenceOrientation.top,
    right = rectOrientation.left - padding.x >= referenceOrientation.right,
    bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom,
    left = rectOrientation.right + padding.x <= referenceOrientation.left;
  var vertical = top ? 'top' : bottom ? 'bottom' : null,
    horizontal = left ? 'left' : right ? 'right' : null;
  if (horizontal && vertical) return vertical + '-' + horizontal;
  else return horizontal || vertical || 'intersect';
}

function getVerticalDistance(orientation: any, minDistance: any) {
  if (orientation.indexOf('top') != -1) return -1 * minDistance;
  else if (orientation.indexOf('bottom') != -1) return minDistance;
  else return 0;
}
/**
 * horizontalDistance 控制距离 计算默认取50 如果有连接过 获取之前的连接距离
 * */
export function getFlowNodePosition(source: any, element: any, jnpfData: any) {
  var sourceTrbl = asTRBL(source);
  var sourceMid = getMid(source);
  let layout = jnpfData.data['layout']; // horizontal: 横向， vertical：纵向
  var horizontalDistance =
    layout?.value === 'horizontal'
      ? getConnectedDistance(source, {
          filter: function (connection: any) {
            return is(connection, 'bpmn:SequenceFlow');
          },
        })
      : 120;
  var margin = 30,
    minDistance = 50,
    orientation = 'left';
  if (is(source, 'bpmn:BoundaryEvent')) {
    orientation = getOrientation(source, source.host, -25);
    if (orientation.indexOf('top') !== -1) margin *= -1;
  }
  var position: any = {};
  let nextPositionDirection: any = {};
  // 获取排序 横向还是竖向 横向
  if (layout?.value === 'horizontal') {
    position = {
      x: sourceTrbl.right + DEFAULT_CONNECT + element.width / 2,
      y: sourceMid.y + getVerticalDistance(orientation, minDistance),
    };
    nextPositionDirection = {
      y: {
        margin: margin,
        minDistance: minDistance,
      },
    };
  } else {
    position = {
      x: sourceTrbl.right + getVerticalDistance(orientation, minDistance),
      y: sourceMid.y + (element.height + source.height) / 2 + horizontalDistance,
    };
    nextPositionDirection = {
      x: {
        margin: (source.width - element.width) / 2 + 220,
        minDistance: minDistance,
      },
    };
  }
  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
}
export function getNewShapePosition(source: any, element: any, jnpfData: any) {
  if (is(element, 'bpmn:TextAnnotation')) return getTextAnnotationPosition(source, element);
  if (isAny(element, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) return getDataElementPosition(source, element);
  if (is(element, 'bpmn:FlowNode')) return getFlowNodePosition(source, element, jnpfData);
}
/**
 * Always put element bottom right of source.
 */
export function getDataElementPosition(source: any, element: any) {
  var sourceTrbl = asTRBL(source);
  var position = {
    x: sourceTrbl.right - 10 + element.width / 2,
    y: sourceTrbl.bottom + 40 + element.width / 2,
  };
  var nextPositionDirection = {
    x: {
      margin: 30,
      minDistance: 30,
    },
  };
  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
}
/**
 * Always try to place text annotations top right of source.
 */
export function getTextAnnotationPosition(source: any, element: any) {
  var sourceTrbl = asTRBL(source);
  var position = {
    x: sourceTrbl.right + element.width / 2,
    y: sourceTrbl.top - 50 - element.height / 2,
  };
  if (!!source.waypoints) {
    position = getMid(source);
    position.x += 100;
    position.y -= 50;
  }
  var nextPositionDirection = {
    y: {
      margin: -30,
      minDistance: 20,
    },
  };
  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
}
export default function YmGridSnappingAutoPlaceBehavior(eventBus: any, gridSnapping: any, jnpfData: any) {
  eventBus.on('autoPlace', HIGH_PRIORITY, function (context: any) {
    var source = context.source,
      sourceMid = getMid(source),
      shape = context.shape,
      layout = jnpfData.data['layout']; // horizontal: 横向， vertical：纵向
    var position: any = getNewShapePosition(source, shape, jnpfData);
    ['x', 'y'].forEach(function (axis: any) {
      var options: any = {};
      if (position[axis] === sourceMid[axis]) return;
      if (position[axis] > sourceMid[axis]) options.min = position[axis];
      else options.max = position[axis];

      if (is(shape, 'bpmn:TextAnnotation')) {
        if (isHorizontal(axis)) options.offset = -shape.width / 2;
        else options.offset = -shape.height / 2;
      }
    });
    if (layout?.value === 'vertical') {
      if (source.type === bpmnStart) {
        position = {
          x: position.x + (source.width - shape.width) / 2 + 10,
          y: position.y,
        };
      } else {
        position = {
          x: position.x - 100,
          y: position.y,
        };
      }
    }
    return position;
  });
}

YmGridSnappingAutoPlaceBehavior.$inject = ['eventBus', 'gridSnapping', 'jnpfData'];
function isHorizontal(axis: any) {
  return axis === 'x';
}
