/**
 * Should objects be aligned by a bounding box?
 * [Bug] Scaled objects sometimes can not be aligned by edges
 *
 */
var ctx,canvasAligning
function initAligningGuidelines(canvas,bor){
	ctx = canvas.getSelectionContext()
	canvasAligning = canvas
	setCanvasAligning(bor)
}
var aligningLineOffset = 5,
	aligningLineMargin = 4,
	aligningLineWidth = 1,
	aligningLineColor = 'rgb(178, 204, 255)',
	viewportTransform,
	zoom = 1;

function drawVerticalLine(coords) {
	drawLine(
		coords.x + 0.5,
		coords.y1 > coords.y2 ? coords.y2 : coords.y1,
		coords.x + 0.5,
		coords.y2 > coords.y1 ? coords.y2 : coords.y1);
}

function drawHorizontalLine(coords) {
	drawLine(
		coords.x1 > coords.x2 ? coords.x2 : coords.x1,
		coords.y + 0.5,
		coords.x2 > coords.x1 ? coords.x2 : coords.x1,
		coords.y + 0.5);
}

function drawLine(x1, y1, x2, y2) {
	ctx.save();
	ctx.lineWidth = aligningLineWidth;
	ctx.strokeStyle = aligningLineColor;
	ctx.beginPath();
	ctx.moveTo(x1 * zoom + viewportTransform[4], y1 * zoom + viewportTransform[5]);
	ctx.lineTo(x2 * zoom + viewportTransform[4], y2 * zoom + viewportTransform[5]);
	ctx.stroke();
	ctx.restore();
}

function isInRange(value1, value2) {
	value1 = Math.round(value1);
	value2 = Math.round(value2);
	for (var i = value1 - aligningLineMargin, len = value1 + aligningLineMargin; i <= len; i++) {
		if (i === value2) {
			return true;
		}
	}
	return false;
}

var verticalLines = [],
	horizontalLines = [];
	stateAligning = false
let mouseDown = (e) => {
	if(e.target){
		stateAligning = true
	}else{
		stateAligning = false
	}
	viewportTransform = canvasAligning.viewportTransform;
	zoom = canvasAligning.getZoom();
}
let objectMoving = (e) => {
	var activeObject = e.target,
		canvasAligningObjects = canvasAligning.getObjects(),
		activeObjectCenter = activeObject.getCenterPoint(),
		activeObjectLeft = activeObjectCenter.x,
		activeObjectTop = activeObjectCenter.y,
		activeObjectBoundingRect = activeObject.getBoundingRect(),
		activeObjectHeight = activeObjectBoundingRect.height / viewportTransform[3],
		activeObjectWidth = activeObjectBoundingRect.width / viewportTransform[0],
		horizontalInTheRange = false,
		verticalInTheRange = false,
		transform = canvasAligning._currentTransform;

	if (!transform) return;

	// It should be trivial to DRY this up by encapsulating (repeating) creation of x1, x2, y1, and y2 into functions,
	// but we're not doing it here for perf. reasons -- as this a function that's invoked on every mouse move

	for (var i = canvasAligningObjects.length; i--;) {

		if (canvasAligningObjects[i] === activeObject) continue;

		var objectCenter = canvasAligningObjects[i].getCenterPoint(),
			objectLeft = objectCenter.x,
			objectTop = objectCenter.y,
			objectBoundingRect = canvasAligningObjects[i].getBoundingRect(),
			objectHeight = objectBoundingRect.height / viewportTransform[3],
			objectWidth = objectBoundingRect.width / viewportTransform[0];

		// snap by the horizontal center line
		if (isInRange(objectLeft, activeObjectLeft)) {
			verticalInTheRange = true;
			verticalLines.push({
				x: objectLeft,
				y1: (objectTop < activeObjectTop)
					? (objectTop - objectHeight / 2 - aligningLineOffset)
					: (objectTop + objectHeight / 2 + aligningLineOffset),
				y2: (activeObjectTop > objectTop)
					? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset)
					: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset)
			});
			activeObject.setPositionByOrigin(new fabric.Point(objectLeft, activeObjectTop), 'center', 'center');
		}

		// snap by the left edge
		if (isInRange(objectLeft - objectWidth / 2, activeObjectLeft - activeObjectWidth / 2)) {
			verticalInTheRange = true;
			verticalLines.push({
				x: objectLeft - objectWidth / 2,
				y1: (objectTop < activeObjectTop)
					? (objectTop - objectHeight / 2 - aligningLineOffset)
					: (objectTop + objectHeight / 2 + aligningLineOffset),
				y2: (activeObjectTop > objectTop)
					? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset)
					: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset)
			});
			activeObject.setPositionByOrigin(new fabric.Point(objectLeft - objectWidth / 2 + activeObjectWidth / 2, activeObjectTop), 'center', 'center');
		}

		// snap by the right edge
		if (isInRange(objectLeft + objectWidth / 2, activeObjectLeft + activeObjectWidth / 2)) {
			verticalInTheRange = true;
			verticalLines.push({
				x: objectLeft + objectWidth / 2,
				y1: (objectTop < activeObjectTop)
					? (objectTop - objectHeight / 2 - aligningLineOffset)
					: (objectTop + objectHeight / 2 + aligningLineOffset),
				y2: (activeObjectTop > objectTop)
					? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset)
					: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset)
			});
			activeObject.setPositionByOrigin(new fabric.Point(objectLeft + objectWidth / 2 - activeObjectWidth / 2, activeObjectTop), 'center', 'center');
		}

		// snap by the vertical center line
		if (isInRange(objectTop, activeObjectTop)) {
			horizontalInTheRange = true;
			horizontalLines.push({
				y: objectTop,
				x1: (objectLeft < activeObjectLeft)
					? (objectLeft - objectWidth / 2 - aligningLineOffset)
					: (objectLeft + objectWidth / 2 + aligningLineOffset),
				x2: (activeObjectLeft > objectLeft)
					? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset)
					: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset)
			});
			activeObject.setPositionByOrigin(new fabric.Point(activeObjectLeft, objectTop), 'center', 'center');
		}

		// snap by the top edge
		if (isInRange(objectTop - objectHeight / 2, activeObjectTop - activeObjectHeight / 2)) {
			horizontalInTheRange = true;
			horizontalLines.push({
				y: objectTop - objectHeight / 2,
				x1: (objectLeft < activeObjectLeft)
					? (objectLeft - objectWidth / 2 - aligningLineOffset)
					: (objectLeft + objectWidth / 2 + aligningLineOffset),
				x2: (activeObjectLeft > objectLeft)
					? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset)
					: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset)
			});
			activeObject.setPositionByOrigin(new fabric.Point(activeObjectLeft, objectTop - objectHeight / 2 + activeObjectHeight / 2), 'center', 'center');
		}

		// snap by the bottom edge
		if (isInRange(objectTop + objectHeight / 2, activeObjectTop + activeObjectHeight / 2)) {
			horizontalInTheRange = true;
			horizontalLines.push({
				y: objectTop + objectHeight / 2,
				x1: (objectLeft < activeObjectLeft)
					? (objectLeft - objectWidth / 2 - aligningLineOffset)
					: (objectLeft + objectWidth / 2 + aligningLineOffset),
				x2: (activeObjectLeft > objectLeft)
					? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset)
					: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset)
			});
			activeObject.setPositionByOrigin(new fabric.Point(activeObjectLeft, objectTop + objectHeight / 2 - activeObjectHeight / 2), 'center', 'center');
		}
	}

	if (!horizontalInTheRange) {
		horizontalLines.length = 0;
	}

	if (!verticalInTheRange) {
		verticalLines.length = 0;
	}
}
let beforeRender = ()=>{
	if(stateAligning){
		canvasAligning.clearContext(canvasAligning.contextTop);
	}
}
let afterRender = ()=>{
	for (var i = verticalLines.length; i--;) {
		drawVerticalLine(verticalLines[i]);
	}
	for (var i = horizontalLines.length; i--;) {
		drawHorizontalLine(horizontalLines[i]);
	}

	verticalLines.length = horizontalLines.length = 0;
}
let mouseUp = ()=>{
	verticalLines.length = horizontalLines.length = 0;
	canvasAligning.renderAll();
}
let setCanvasAligning = (bor)=>{
	if(bor){
		canvasAligning.on('mouse:down', mouseDown);
		canvasAligning.on('object:moving', objectMoving);
		canvasAligning.on('before:render', beforeRender);
		canvasAligning.on('after:render', afterRender);
		canvasAligning.on('mouse:up', mouseUp);
	}else{
		canvasAligning.off('mouse:down', mouseDown);
		canvasAligning.off('object:moving', objectMoving);
		canvasAligning.off('before:render', beforeRender);
		canvasAligning.off('after:render', afterRender);
		canvasAligning.off('mouse:up', mouseUp);
	}
}
