/* Copyright (c) 2011, Geert Bergman (geert@scrivo.nl)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of "Scrivo" nor the names of its contributors may be
 *    used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: BorderLayout.js 616 2013-04-22 23:48:38Z geert $
 */

"use strict";

SUI.BorderLayout = SUI.defineClass(
	/** @lends SUI.BorderLayout.prototype */{

	/** @ignore */ baseClass: SUI.SplitLayout,

	/**
	 * @class
	 * The border layOut is a container type that lets you split an area into
	 * a center box and (optional) north, south, west and east boxes, just at
	 * as the split layOut from which it is inherited. The difference between
	 * the border layOut and the split layOut is that the border layOut has
	 * sizable borders. When sizing the total border layOut component the same
	 * rules are followed as for the split layOut.
	 *
	 * @augments SUI.SplitLayout
	 *
	 * @description
	 * Construct a split border object. You can tell the split layOut which
	 * areas (north/south/west/east) to set and the dimensions to use.
	 * It's also possible to set the child boxes directly.
	 *
	 * @constructs
	 * @param see base class
	 */
	initializer: function(arg) {

		SUI.BorderLayout.initializeBase(this, arg);

		this.addClass("sui-borderlayout");
		this.anchor = {left:true,right:true,top:true,bottom:true};

		this.center = new SUI.Box({parent: this});
		if (arg.center && arg.center.box) {
			this.add(arg.center.box, "center");
		}

		// add north, south, west and east if given in arguments
		if (arg.north) {
			this.north = new SUI.Box({parent: this});
			this.north.height(arg.north.height);
			this._createBorder(this.north);
			if (arg.north.box) {
				this.add(arg.north.box, "north");
			}
		}

		if (arg.south) {
			this.south = new SUI.Box({parent: this});
			this.south.height(arg.south.height);
			this._createBorder(this.south);
			if (arg.south.box) {
				this.add(arg.south.box, "south");
			}
		}

		if (arg.west) {
			this.west = new SUI.Box({parent: this});
			this.west.width(arg.west.width);
			this._createBorder(this.west);
			if (arg.west.box) {
				this.add(arg.west.box, "west");
			}
		}

		if (arg.east) {
			this.east = new SUI.Box({parent: this});
			this.east.width(arg.east.width);
			this._createBorder(this.east);
			if (arg.east.box) {
				this.add(arg.east.box, "east");
			}
		}

	},

	/**
	 * Width of the border between the panels.
	 */
	BORDER_WIDTH: 6,

	/**
	 * Size of the gripper image on the panel.
	 */
	HANDLE_LENGTH: 48,

	/**
	 * Set the CSS width and height of she SplitLayout, its locations and
	 * all the child boxes.
	 */
	display: function() {

		// set the CSS dimensions of the container box ...
		this.setDim();

		// ... and the of the location boxes ...
		if (this.north) {
			this.north.setDim();
			this.north.paneBorder.setDim();
			this.north.handle.setDim();
		}
		if (this.south) {
			this.south.setDim();
			this.south.paneBorder.setDim();
			this.south.handle.setDim();
		}
		if (this.west) {
			this.west.setDim();
			this.west.paneBorder.setDim();
			this.west.handle.setDim();
		}
		if (this.east) {
			this.east.setDim();
			this.east.paneBorder.setDim();
			this.east.handle.setDim();
		}
		this.center.setDim();

		// ... and of all the children
		SUI.SplitLayout.parentMethod(this, "display");
	},

	/**
	 * Recalculate size of the frames in the border layOut
	 */
	layOut: function() {

		// get the minimum widths and heights
		var tmp = this._prepareLayout();
		var w = tmp.w; // corrected widths
		var h = tmp.h; // corrected heights
		var ct = 0; // center top
		var cl = 0; // center left
		var cw = w.w; // center width
		var ch = h.h; // center height
		var hl = 0; // handle length

		// set the dimensions of the container box
		this.setRect(this.top(), this.left(), w.w, h.h);

		// handle length for horizontal border
		hl = this.HANDLE_LENGTH > w.w ? w.w : this.HANDLE_LENGTH;

		// if there is a north panel ...
		if (this.north) {
			// ... set the dimensions ...
			this.north.setRect(0, 0, w.w, h.nh);
			// ... and adjust the top and height of the center
			ct += h.nh + this.BORDER_WIDTH;
			ch -= ct;

			// set the dimensions of the north border and handle
			this.north.paneBorder.setRect(h.nh, 0, w.w, this.BORDER_WIDTH);
			this.north.handle.setRect(
				0, Math.floor((w.w-hl)/2), hl, this.BORDER_WIDTH);
		}

		// if there is a south panel ...
		if (this.south) {
			// ... set the dimensions ...
			this.south.setRect(h.h-h.sh, 0, w.w, h.sh);
			// ... and adjust the height of the center
			ch -= h.sh + this.BORDER_WIDTH;

			// set the dimensions of the south border and handle
			this.south.paneBorder.setRect(h.h-h.sh-this.BORDER_WIDTH, 0, w.w,
				this.BORDER_WIDTH);
			this.south.handle.setRect(0, Math.floor((w.w-hl)/2), hl,
				this.BORDER_WIDTH);
		}

		// handle length for vertical border
		hl = this.HANDLE_LENGTH > ch ? ch : this.HANDLE_LENGTH;

		// if there is a west panel ...
		if (this.west) {
			// ... set the dimensions ...
			this.west.setRect(ct, 0, w.ew, ch);
			// ... and adjust the left and width of the center
			cl += w.ew + this.BORDER_WIDTH;
			cw -= cl;

			// set the dimensions of the west border and handle
			this.west.paneBorder.setRect(ct, w.ew, this.BORDER_WIDTH, ch);
			this.west.handle.setRect(
				Math.floor((ch-hl)/2), 0,this.BORDER_WIDTH, hl);
		}

		// if there is an east panel ...
		if (this.east) {
			// ... set the dimensions ...
			this.east.setRect(ct, w.w-w.ww, w.ww, ch);
			// ... and adjust the width of the center
			cw -= w.ww + this.BORDER_WIDTH;

			// set the dimensions of the east border and handle
			this.east.paneBorder.setRect(
				ct, w.w-w.ww-this.BORDER_WIDTH, this.BORDER_WIDTH, ch);
			this.east.handle.setRect(
				Math.floor((ch-hl)/2), 0,this.BORDER_WIDTH, hl);
		}

		// now we know the position of the center
		this.center.setRect(ct, cl, cw, ch);

		// layOut the child boxes
		SUI.SplitLayout.parentMethod(this, "layOut"); return;
	},

	/* Create border and handle box for a pane
	 */
	_createBorder: function(pane) {

		// create the border
		pane.paneBorder = new SUI.Box({parent: this});
		pane.paneBorder.addClass("sui-borderlayout-border");
		// and set the cursor type for the border
		pane.paneBorder.el().style.cursor =
			(pane === this.east || pane === this.west)
			? "col-resize" : "row-resize";

		// create the handle
		pane.handle = new SUI.Box({parent: pane.paneBorder});
		pane.handle.addClass("sui-borderlayout-handle");
		pane.handle.el().style.cursor = pane.paneBorder.el().style.cursor;
		pane.handle.el().style.backgroundImage = "url(" + SUI.imgDir + "/"
			+ SUI.resource.blHandle + ")";

		// add the onmousedown handler to the border
		var that = this;
		SUI.browser.addEventListener(pane.paneBorder.el(), "mousedown",
			function(e) {
				if (!that._doMouseDown(new SUI.Event(this, e), pane)) {
					SUI.browser.noPropagation(e);
				}
			}
		);

	},

	/* Start the dragging motion: create and initialize the dragger.
	 */
	_doMouseDown: function(event, pane) {

		// Create  dragger ...
		var dragger = new SUI.Dragger({parent: this});
		// ... width the same dimensions as the border
		dragger.setRect(pane.paneBorder);
		dragger.addClass("sui-borderlayout-dragger");

		// if we're resizing the west pane ...
		if (this.west === pane) {
			// ... set minimum x ...
			dragger.xMin(this.west.minWidth());
			// ... and maximum x ...
			dragger.xMax(this.center.left() + this.center.width()
				- this.center.minWidth() - this.BORDER_WIDTH);
			// ... and horizontal dragging direction
			dragger.direction(dragger.HORIZONTAL);
		}

		// if we're resizing the east pane ...
		if (this.east === pane) {
			// ... set minimum x ...
			dragger.xMin(this.center.left() + this.center.minWidth());
			// ... and maximum x ...
			dragger.xMax(this.east.left() + this.east.width()
				- this.east.minWidth() - this.BORDER_WIDTH);
			// ... and horizontal dragging direction
			dragger.direction(dragger.HORIZONTAL);
		}

		// if we're resizing the north pane ...
		if (this.north === pane) {
			// ... set minimum y ...
			dragger.yMin(this.north.minHeight());
			// ... and maximum y ...
			dragger.yMax(this.center.top() + this.center.height()
				- this.center.minHeight() - this.BORDER_WIDTH);
			// ... and vertical dragging direction
			dragger.direction(dragger.VERTICAL);
		}

		// if we're resizing the south pane ...
		if (this.south === pane) {
			// ... set minimum y ...
			dragger.yMin(this.center.top() + this.center.minHeight());
			// ... and maximum y ...
			dragger.yMax(this.south.top() + this.south.height()
				- this.south.minHeight() - this.BORDER_WIDTH);
			// ... and vertical dragging direction
			dragger.direction(dragger.VERTICAL);
		}

		// make a box that covers the entire border layOut. This prevents
		// HTML objects such as an iframe to steal the onmousemove event. You
		// can use this to keep the cursor if you move off the border also.
		var draggerbg = new SUI.Box({parent: this});
		draggerbg.setRect(0, 0, this.width(), this.height());
		draggerbg.el().style.cursor = pane.paneBorder.el().style.cursor;
		draggerbg.setDim();

		// copy the cursor of the border to the dragger
		dragger.el().style.cursor = pane.paneBorder.el().style.cursor;
		// and set the CSS dimension of the dragger
		dragger.setDim();

		var that = this;
		// 'that', 'pane', 'dragger', 'draggerbg' are closure variables
		dragger.addListener("onEndDrag",
			function() {
				that._endDrag(pane, dragger, draggerbg);
			}
		);

		// and start dragging
		dragger.start(event, this);
	},

	/* End dragging of the border: set the new pane and border dimensions
	 */
	_endDrag: function(pane, dragger, draggerbg) {

		// remove the dragger and dragger background from the DOM tree
		dragger.removeBox();
		draggerbg.removeBox();

		// set the new pane and border dimensions
		if (this.north === pane) {
			var d = this.north.paneBorder.top() - dragger.top();
			this.north.height(this.north.height() - d);
		}
		if (this.south === pane) {
			var d = this.south.paneBorder.top() - dragger.top();
			this.south.height(this.south.height() + d);
		}
		if (this.west === pane) {
			var d = this.west.paneBorder.left() - dragger.left();
			this.west.width(this.west.width() - d);
		}
		if (this.east === pane) {
			var d = this.east.paneBorder.left() - dragger.left();
			this.east.width(this.east.width() + d);
		}

		// redraw the border layOut
		this.draw();
	}

});