//##############################################################################
//------------------------------------------------------------------------------
function Viewport()
{   
    this.noSprings = false;
    this.minZoom = 1.0;
    this.maxZoom = 5000;
    this.panX = new Spring(6);
    this.panY = new Spring(6);
    this.zoomSpring = new Spring(6);
    this.zoomCenter = new Point();
    this.changed = true;

    var windowSize = getWindowSize();
    this.zoomCenter.x = (windowSize.x / 2);
    this.zoomCenter.y = (windowSize.y / 2);
    this.panX.setCurrentAndTarget(0);
    this.panY.setCurrentAndTarget(0);
    this.zoomSpring.setCurrentAndTarget(1);
}

//------------------------------------------------------------------------------
Viewport.prototype.zoom = function(delta, center, immediately)
{
	this.setZoom(this.zoomSpring.target * delta, center, immediately);
}

//------------------------------------------------------------------------------
Viewport.prototype.setZoom = function(value, center, immediately)
{
	var windowSize = getWindowSize();
	var windowCenter = new Clone(windowSize);
	windowCenter.divide(2);
	
	if(!center)
		center = windowCenter;
		
	if(this.noSprings)
		immediately = true;
	
	if(value < this.minZoom)
		value = this.minZoom;

	this.zoomSpring.target = value;

	if(immediately)
	{
		if(this.zoomSpring.current != value)
		{
			if(center.isEqualTo(windowCenter) == false)
			{
				var oldZoomDistance = new Clone(center);
				oldZoomDistance.subtract(windowCenter);
				
				var newZoomDistance = new Clone(oldZoomDistance);
				oldZoomDistance.divide(new Point(this.zoomSpring.current));
				newZoomDistance.divide(new Point(value));
				
				var addToPan = new Clone(oldZoomDistance);
				addToPan.subtract(newZoomDistance);
				this.pan(addToPan, true);
			}

			this.zoomSpring.current = this.zoomSpring.target;
			this.changed = true;
		}

		this.zoomCenter = windowCenter;
	}
	else
		this.zoomCenter = new Clone(center);
}

//------------------------------------------------------------------------------
Viewport.prototype.panByPixels = function(delta)
{
	var newDelta = new Clone(delta);
	newDelta.divide(new Point(this.zoomSpring.current));
	this.pan(newDelta, false);
}

//------------------------------------------------------------------------------
Viewport.prototype.pan = function(delta, immediately)
{
	var newPan = new Point(this.panX.target, this.panY.target);
	newPan.add(delta);
	this.setPan(newPan, immediately);
}

//------------------------------------------------------------------------------
Viewport.prototype.setPan = function(value, immediately)
{
	if(this.noSprings)
		immediately = true;
	
	this.panX.target = value.x;
	this.panY.target = value.y;
	
	if(immediately)
	{
		this.panX.current = value.x;
		this.panY.current = value.y;
		this.changed = true;
	}
}

//------------------------------------------------------------------------------
Viewport.prototype.shiftPan = function(value)
{        
	this.panX.target += value.x;
	this.panY.target += value.y;
	this.panX.current += value.x;
	this.panY.current += value.y;
	this.changed = true;
}

//------------------------------------------------------------------------------
Viewport.prototype.centerOn = function(bounds, fit, immediately)
{
	var windowSize = getWindowSize();
	var windowCenter = new Clone(windowSize);
	windowCenter.divide(2);

	var contentSize = bounds.size();
	var oldZoom = this.zoomSpring.current;
	var newZoom = this.getZoomForCenteringOn(bounds, fit);
	var newPan = new Point(bounds.left + (contentSize.x / 2), bounds.top + (contentSize.y / 2));

	if(Math.abs(1 - (newZoom / oldZoom)) < 0.001)
	{
		this.setZoom(newZoom, new Point(windowSize.x, windowSize.y), immediately);
		this.setPan(newPan, immediately);
	}
	else
	{
		var addToPan = new Point(newPan.x - this.panX.current, newPan.y - this.panY.current);
		var multiplier = (oldZoom * newZoom) / (newZoom - oldZoom);
		var distance = new Point(multiplier * addToPan.x, multiplier * addToPan.y);
		var newZoomCenter = new Point(windowCenter.x + distance.x, windowCenter.y + distance.y);

		this.setZoom(newZoom, newZoomCenter, immediately);
	}
}

//------------------------------------------------------------------------------
Viewport.prototype.getZoomForCenteringOn = function(bounds, fit)
{
	if(typeof(fit) == 'undefined')
		fit = 0.9;
		
	var windowSize = getWindowSize();

	var targetSize = new Point(windowSize.x * fit, windowSize.y * fit);
	var contentSize = bounds.size();

	var zoom = targetSize.y / contentSize.y;
	if(contentSize.x * zoom > targetSize.x)
		zoom = targetSize.x / contentSize.x;

	return zoom;
}

//------------------------------------------------------------------------------
Viewport.prototype.screenFromWorld = function(world)
{
	// Currently assumes world is a Point

	var zoom = this.zoomSpring.current;    
	var windowSize = getWindowSize();
	var windowCenterX = windowSize.x / 2;
	var windowCenterY = windowSize.y / 2; 

	var x = ((world.x - this.panX.current) * zoom) + windowCenterX;
	var y = ((world.y - this.panY.current) * zoom) + windowCenterY;

	return new Point(x, y);
}

//------------------------------------------------------------------------------
Viewport.prototype.worldFromScreen = function(screen)
{
	// Currently assumes screen is a Point

	var zoom = this.zoomSpring.current;    
	var windowSize = getWindowSize();
	var windowCenterX = windowSize.x / 2;
	var windowCenterY = windowSize.y / 2; 

	var x = ((screen.x - windowCenterX) / zoom) + this.panX.current;
	var y = ((screen.y - windowCenterY) / zoom) + this.panY.current;

	return new Point(x, y);
}

//------------------------------------------------------------------------------
Viewport.prototype.update = function()
{
	var windowSize = getWindowSize();
	var windowCenter = new Clone(windowSize);
	windowCenter.divide(2);

	var changed = this.changed;
	this.changed = false;
	
	// ___ zoom
	var oldZoomDistance = new Clone(this.zoomCenter);
	oldZoomDistance.x -= windowCenter.x;
	oldZoomDistance.y -= windowCenter.y;

	var newZoomDistance = new Clone(oldZoomDistance);
	oldZoomDistance.x /= this.zoomSpring.current;
	oldZoomDistance.y /= this.zoomSpring.current;

	if(this.zoomSpring.step())
		changed = true;

	newZoomDistance.x /= this.zoomSpring.current;
	newZoomDistance.y /= this.zoomSpring.current;

	var addToPan = new Clone(oldZoomDistance);
	addToPan.x -= newZoomDistance.x;
	addToPan.y -= newZoomDistance.y;

	this.panX.current = this.panX.current + addToPan.x;
	this.panY.current = this.panY.current + addToPan.y;
	this.panX.target = this.panX.target + addToPan.x;
	this.panY.target = this.panY.target + addToPan.y;

	// ___ pan
	if(this.panX.step())
		changed = true;
	
	if(this.panY.step())
		changed = true;

	return changed;
}
