/* 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: Accordion.js 743 2013-07-18 10:12:39Z geert $
 */

"use strict";

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

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

	/**
	 * @class
	 * SUI.Accordion is a component that helps you to save space by stacking a
	 * number of panels/boxes and let the user swap them. It's sort of a
	 * vertical tab list. The SUI.Accordion object can host a number of SUI
	 * boxes, each of them has a title bar for the user to identify and select
	 * them.
	 *
	 * @augments SUI.Box
	 *
	 * @description
	 * Construct a SUI.Accordion object. The titles of the headers and
	 * (optional) the client boxes are given as argument to this constructor.
	 * It is also possible to select the initial box.
	 *
	 * @constructs
	 * @param {inherit} arg An argument object.
	 * @param {Object[]} arg.items An array of object containing data for the
	 *    accordion items.
	 * @param {String} [arg.items[].title="Item x"] Header title.
	 * @param {SUI.Box} [arg.items[].box] A client box.
	 * @param {int} [arg.selected] Index of the client box that is initially
	 *     shown.
	 */
	initializer: function(arg) {

		// anchors default to all sides
		if (!arg.anchor) {
			arg.anchor = {left:true,right:true,top:true,bottom:true};
		}

		SUI.Accordion.initializeBase(this, arg);

		if (!arg.selected) {
			arg.selected = 0;
		}

		this._buildControl(arg);
	},

	/**
	 * Line thickness of the lines separating the different parts of the
	 * control.
	 * @constant
	 * @type int
	 * @private
	 */
	ITEM_BORDER_BOTTOM_WIDTH: 1,

	/**
	 * Height of an header.
	 * @constant
	 * @type int
	 * @private
	 */
	ITEM_HEIGHT: 24,

	/**
	 * Padding top of text and icon in the header.
	 * @constant
	 * @type int
	 * @private
	 */
	ITEM_PADDING_TOP: 4,

	/**
	 * Padding left and right of text and icon in the header.
	 * @constant
	 * @type int
	 * @private
	 */
	ITEM_PADDING_LR: 5,

	/**
	 * Size (width/height) of the icon.
	 * @constant
	 * @type int
	 * @private
	 */
	ICON_SIZE: 16,

	/**
	 * Add a box to one of the client areas of the control.
	 * @param {SUI.Box} child The box to add to the control.
	 * @param {int} i Index position of the client container to at the box to.
	 */
	add: function(child, i) {
		this._items[i].content.add(child);
	},

	/**
	 * Display the accordion control. Set the CSS size and position of all the
	 * headers and for the currently displayed content box.
	 */
	display: function() {

		// size and position of the current control
		this.setDim();

		// size and position of the header
		for (var i=0; i<this._items.length; i++) {
			this._items[i].header.setDim();
			this._items[i].headertext.setDim();
		}

		// and the client area
		this._selectedItem.content.display();
	},

	/**
	 * Lay out the accordion control. Calculate the size and position of all
	 * the headers and for the currently displayed content box.
	 */
	layOut: function() {

		for (var i=0, t=0; i<this._items.length; i++) {

			// get the size and position of all the header parts
			this._items[i].header.setRect(
				t, 0, this.width(), this.ITEM_HEIGHT
			);
			this._items[i].headertext.setRect(
				this.ITEM_PADDING_TOP, this.ITEM_PADDING_LR,
				this.width() - 2 * this.ITEM_PADDING_LR - this.ICON_SIZE,
				this.ITEM_HEIGHT - this.ITEM_PADDING_TOP
			);
			// set CSS positions of the icon
			SUI.style.setRect(this._items[i].headersign,
				this.ITEM_PADDING_TOP,
				this.width() - this.ITEM_PADDING_LR - this.ICON_SIZE,
				this.ICON_SIZE,     this.ICON_SIZE
			);

			// increase top with header height
			t += this.ITEM_HEIGHT;

			// by default we use a close sign ...
			this._items[i].headersign.src = SUI.imgDir + "/"
				+ SUI.resource.acClosed;
			// ... and do not display the content
			this._items[i].content.el().style.display = "none";

			// but if the item is selected ...
			if (this._selectedItem === this._items[i]) {
				// ... then show the open sign ...
				this._items[i].headersign.src = SUI.imgDir + "/"
					+ SUI.resource.acDown;
				// ... and display the content
				this._items[i].content.el().style.display = "block";

				// get the available height for the content ...
				var h = this._contentHeight();
				// ... and use it to set the client area of the control
				this._items[i].content.setRect(t, 0, this.width(), h);
				// ... and to set the new top
				t += h;
			}
		}

		// and layOut the control's client area
		this._selectedItem.content.layOut();
	},

	/**
	 * The list of accordion items.
	 * @type Object[]
	 * @private
	 */
	_items: null,

	/**
	 * Reference to the currently selected item.
	 * @type Object
	 * @private
	 */
	_selectedItem: null,

	/**
	 * Add the onclick event handler on the header.
	 * @param {Object} item Accordion item object.
	 * @private
	 */
	_addOnClickHeader: function(item) {
		var that = this;
		// 'that' and 'item' are two closures
		SUI.browser.addEventListener(item.header.el(), "click",
			function(e) {
				if (!that._doClick(item)) {
					SUI.browser.noPropagation(e);
				}
			}
		);
	},

	/**
	 * Make all required boxes for the control.
	 * @param {Object[]} arg Argument object as passed to the constructor.
	 * @private
	 */
	_buildControl: function(arg) {

		// start with an empty list
		this._items = [];
		// and no selected item
		this._selectedItem = null;

		// read in the items form the arguments object and store them in
		// a standardized way in the item list
		for (var i=0; i<arg.items.length; i++) {
			// start with a default profile
			var def = {
				title: "Item "+i,
				box: null
			};
			for (var prop in arg.items[i]) {
				// and overwrite the default profile with the entries set
				// in the arguments
				if (arg.items[i].hasOwnProperty(prop)) {
					def[prop] = arg.items[i][prop];
				}
			}
			this._items.push(def);
		}

		// Work trough the items and make the necessary boxes
		for (var i=0; i<this._items.length; i++) {

			// create a header box which gets the onclick
			this._items[i].header = new SUI.Box({parent: this});
			this._addOnClickHeader(this._items[i]);
			this._items[i].header.addClass("sui-ac-header");
			this._items[i].header.border(
				new SUI.Border(0, 0, this.ITEM_BORDER_BOTTOM_WIDTH));

			// a simple box for the header text
			this._items[i].headertext = new SUI.Box(
				{parent: this._items[i].header});
			this._items[i].headertext.el().innerHTML = this._items[i].title;

			// and add the open/close icon to the header
			this._items[i].headersign = SUI.browser.createElement("IMG");
			this._items[i].header.el().appendChild(this._items[i].headersign);

			// create a container to use as client panel
			this._items[i].content = new SUI.AnchorLayout({parent: this});
			this._items[i].content.addClass("sui-ac-content");
			this._items[i].content.border(
				new SUI.Border(0, 0, this.ITEM_BORDER_BOTTOM_WIDTH));
			this._items[i].content.el().style.overflow = "auto";
			this._items[i].content.el().style.display = "none";

			// if there is already content, then add it to the container
			if (this._items[i].box) {
				this.add(this._items[i].box, i);
			}

			// if this is the one, set the selected item
			if (i === arg.selected) {
				this._selectedItem = this._items[i];
			}
		}

		if (!this._selectedItem) {
			this._selectedItem = this._items[0];
		}
	},

	/**
	 * On onclick set the selected item and redraw the control.
	 * @param {Object} item Accordion item object.
	 * @private
	 */
	_doClick: function(item) {
		this._selectedItem = item;
		this.draw();
	},

	/**
	 * Calculate the available height for the content box.
	 * @return {int} Available height for the content box.
	 * @private
	 */
	_contentHeight: function() {
		return this.height() - this._items.length * this.ITEM_HEIGHT
			+ this.ITEM_BORDER_BOTTOM_WIDTH;
	}

});