/*
 * Copyright (c) 2009 The Olympos Development Team.
 * 
 * http://sourceforge.net/projects/olympos/
 * 
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html. If redistributing this code, this
 * entire header must remain intact.
 */
Ext.namespace("cwe.modelgrid");

/**
 * @class A list of all objects of one class.
 * 
 * <p>
 * Shows a grid with all objects of the selected class. Supports paging.
 * Provides buttons for creating a new object, editing the selected object(s)
 * and deleting the selected object(s). Additionally, it supports linking an
 * object of this class as target to another object.
 * </p>
 * 
 * @extends Ext.grid.GridPanel
 * @constructor
 * @param {Object}
 *            config The configuration object.
 * @config modelDescription The ModelDescription of this grid.
 * @config editors The Editor Container as target of object of this grid.
 */
cwe.modelgrid.ModelGrid = function(config) {
};

cwe.modelgrid.ModelGrid = Ext.extend(Ext.grid.GridPanel, {
	initComponent : function() {
		var self = this;

		/**
		 * Number of objects per page.
		 * 
		 * @private
		 * @type int
		 */
		this.objectPerPage = 25;

		/**
		 * Delay for loading the preview, if a row is selected.
		 * 
		 * @private
		 * @type int (milliseconds)
		 */
		this.previewDelay = 500;

		this.pageFormat = "A4";
		this.pageOrientation = "portrait";

		/**
		 * The currently displayed associate button, if any.
		 * 
		 * @private
		 * @type cwe.modelgrid.AssociateButton
		 */
		this.associateButton = null;

		/**
		 * The store holding the objects.
		 * 
		 * @private
		 * @type chi.model.ModelStore
		 */
		this.store = this.getDefaultStore();

		/**
		 * The paging toolbar.
		 * 
		 * @private
		 * @type Ext.PagingToolbar
		 */
		this.pagingBar = new Ext.PagingToolbar( {
		    pageSize : this.objectPerPage,
		    store : this.store,
		    displayInfo : true,
		    displayMsg : chi.Dict.translate("Displaying objects {0} &ndash; {1} of {2}"),
		    emptyMsg : chi.Dict.translate("No objects to display")
		});

		/**
		 * The button for creating a new object of this class.
		 * 
		 * @private
		 * @type Ext.Toolbar.Button
		 */
		this.createButton = new Ext.Toolbar.Button( {
		    text : chi.Dict.translate("Create"),
		    iconCls : "createButton",
		    handler : function() {
			    self.createNew();
		    }
		});

		/**
		 * The button for editing the selected object(s).
		 * 
		 * @private
		 * @type Ext.Toolbar.Button
		 */
		this.editButton = new Ext.Toolbar.Button( {
		    text : chi.Dict.translate("Edit"),
		    iconCls : "editButton",
		    handler : function() {
			    var records = self.getSelectionModel().getSelections();

			    for ( var i = 0; i < records.length; i++) {
				    self.editors.loadOrShow(records[i].getOid(), records[i].getLabel());
			    }
		    }
		});

		/**
		 * The button for deleting the selected object(s).
		 * 
		 * @private
		 * @type Ext.Toolbar.Button
		 */
		this.deleteButton = new Ext.Toolbar.Button( {
		    text : chi.Dict.translate("Delete"),
		    iconCls : "deleteButton",
		    handler : function() {
			    self.deleteSelected();
		    }
		});

		this.searchField = new cwe.ui.SearchField( {
			updateSearch : function(newValue) {
				self.updateSearch(newValue);
			}
		});

		this.reportButton = new Ext.Toolbar.SplitButton( {
		    text : chi.Dict.translate("Export report"),
		    iconCls : "reportButton",
		    handler : function() {
			    self.exportReport();
		    },
		    menu : {
			    items : [ "<div class='menuHeader'>" + chi.Dict.translate("Page Format:") + "</div>", new Ext.menu.CheckItem( {
			        text : "A4",
			        checked : true,
			        group : "pageFormat-" + this.modelDescription.getId(),
			        checkHandler : function() {
				        self.pageFormat = "A4";
			        }
			    }), new Ext.menu.CheckItem( {
			        text : "Letter",
			        checked : false,
			        group : "pageFormat-" + this.modelDescription.getId(),
			        checkHandler : function() {
				        self.pageFormat = "Letter";
			        }
			    }), new Ext.menu.Separator(), "<div class='menuHeader'>" + chi.Dict.translate("Page Orientation:") + "</div>", new Ext.menu.CheckItem( {
			        text : chi.Dict.translate("Portrait"),
			        checked : true,
			        group : "pageOrientation-" + this.modelDescription.getId(),
			        checkHandler : function() {
				        self.pageOrientation = "portrait";
			        }
			    }), new Ext.menu.CheckItem( {
			        text : chi.Dict.translate("Landscape"),
			        checked : false,
			        group : "pageOrientation-" + this.modelDescription.getId(),
			        checkHandler : function() {
				        self.pageOrientation = "landscape";
			        }
			    }) ]
		    }
		});

		Ext.apply(this, {
		    region : "north",
		    height : 250,
		    split : true,
		    tbar : [ this.createButton, this.editButton, this.deleteButton, new Ext.Toolbar.Separator(), this.searchField, new Ext.Toolbar.Separator(), this.reportButton ],
		    loadMask : true,
		    selModel : new Ext.grid.RowSelectionModel( {
		        singleSelect : false,
		        listeners : {
			        "selectionchange" : function(selModel) {
				        self.updateAssociateButton();
				        var record = selModel.getSelected();
				        if (record) {
					        self.loadPreview.defer(self.previewDelay, self, [ record ]);
				        }
			        }
		        }
		    }),
		    columns : this.modelDescription.getGridColumns(),
		    store : this.store,
		    plugins : [ new Ext.ux.grid.RowEditor( {
		        saveText : chi.Dict.translate("Save"),
		        cancelText : chi.Dict.translate("Cancel"),
		        clicksToEdit : 2,
		        listeners : {
			        "afteredit" : function() {
				        self.store.commitChanges();
			        }
		        }
		    }) ],
		    viewConfig : {
		        forceFit : true,
		        markDirty : false
		    },
		    bbar : this.pagingBar
		});

		cwe.modelgrid.ModelGrid.superclass.initComponent.apply(this, arguments);

		this.store.load( {
			params : {
			    start : 0,
			    limit : this.objectPerPage
			}
		});
	}
});

/**
 * Creates the form for entering a new object of this class.
 * 
 * <p>
 * The object is actually created only when the user clicks the save button.
 * </p>
 */
cwe.modelgrid.ModelGrid.prototype.createNew = function() {
	var self = this;

	self.editors.loadOrShow(null, "<i>" + self.modelDescription.getNewLabel() + "</i>", true);
};

/**
 * Deletes the selected objects.
 * 
 * <p>
 * The user has to confirm the deletion in a pop-up box prior to actual
 * deletion.
 * </p>
 */
cwe.modelgrid.ModelGrid.prototype.deleteSelected = function() {
	var records = this.getSelectionModel().getSelections();

	var self = this;

	if (records.length > 0) {

		var msgText = "<p>" + chi.Dict.translate("Are you sure you want to delete the following instances of ${1}:", this.modelDescription.getName()) + "</p><ul class='deleteMsgBox'>";
		for ( var i = 0; i < records.length; i++) {
			msgText += "<li>" + records[i].getLabel() + "</li>";
		}
		msgText += "</ul>";

		Ext.MessageBox.show( {
		    title : chi.Dict.translate("Delete Objects"),
		    msg : msgText,
		    buttons : Ext.MessageBox.YESNO,
		    fn : function(buttonId) {
			    if (buttonId == "yes") {
				    var actionSet = new chi.persistency.ActionSet();

				    for ( var i = 0; i < records.length; i++) {
					    actionSet.addDestroy(records[i].getOid());
				    }

				    actionSet.commit(function(data) {
					    var store = self.getStore();

					    for ( var i = 0; i < records.length; i++) {
						    store.remove(records[i]);

						    self.editors.removeEditor(records[i].getOid());
					    }
				    });
			    }
		    }
		});
	}
};

/**
 * Load a preview for the given record.
 * 
 * <p>
 * Handler to selecting a row. Called after previewDelay time.
 * </p>
 * 
 * @param {chi.model.Record}
 *            record The record to display.
 */
cwe.modelgrid.ModelGrid.prototype.loadPreview = function(record) {
	if (!record) {
		return;
	}
	var selRecord = this.getSelectionModel().getSelected();
	if (selRecord && selRecord.getOid() == record.getOid()) {
		this.editors.updatePreview(record.getOid(), record.getLabel());
	}
};

/**
 * Opens an editor of the selected object.
 * 
 * <p>
 * Handler to doubleclick on a row.
 * </p>
 * 
 * @param {cwe.modelgrid.ModelGrid}
 *            grid The grid which was doubleclicked, i. e. <code>this</code>.
 * @param rowIndex
 *            {int} Index of the doubleclicked row.
 * @param e
 *            {Ext.Event} Event object.
 */
cwe.modelgrid.ModelGrid.prototype.openEditor = function(grid, rowIndex, e) {
	var store = this.getStore();
	var record = store.getAt(rowIndex);

	this.editors.loadOrShow(record.getOid(), record.getLabel());
};

/**
 * Adds a button for associating objects of this grid as target.
 * 
 * <p>
 * Only one associate button is allowed per grid, thus all previous buttons are
 * removed.
 * </p>
 * <p>
 * The grid selection is cleared.
 * </p>
 * 
 * @param {cwe.modelgrid.AssociateButton}
 *            button The button to add.
 */
cwe.modelgrid.ModelGrid.prototype.addAssociateButton = function(button) {
	if (this.associateButton) {
		this.getTopToolbar().remove(this.associateButton);
	}

	button.grid = this;

	this.associateButton = button;

	this.getTopToolbar().add(button);

	this.getSelectionModel().clearSelections();

	this.doLayout();
};

/**
 * Removes the associate button.
 */
cwe.modelgrid.ModelGrid.prototype.removeAssociateButton = function() {
	if (this.associateButton) {
		this.getTopToolbar().remove(this.associateButton);
	}
};

/**
 * Enables or disables the associate button according to grid selection status.
 * 
 * <p>
 * If nothing is selected, the button is disabled. If the button is a single
 * select button, the button is only enabled if only one row is selected. If the
 * button is a multi select button it is enabled as long as at least one row is
 * selected.
 * </p>
 * 
 * @private
 */
cwe.modelgrid.ModelGrid.prototype.updateAssociateButton = function() {
	if (this.associateButton) {
		var count = this.getSelectionModel().getCount();

		if (count == 0 || this.associateButton.isSingleSelect() && count > 1) {
			this.associateButton.disable();
		} else {
			this.associateButton.enable();
		}
	}
};

cwe.modelgrid.ModelGrid.prototype.getDefaultStore = function() {
	return new chi.model.Store( {
		modelDescription : this.modelDescription
	});
};

cwe.modelgrid.ModelGrid.prototype.updateSearch = function(newQuery) {
	var newStore = null;
	var columnModel = this.modelDescription.getGridColumns();

	if (newQuery && newQuery.trim() != "") {
		newStore = new cwe.modelgrid.SearchStore( {
		    query : newQuery,
		    modelDescription : this.modelDescription
		});
		columnModel.push( {
		    header : chi.Dict.translate("Relevance"),
		    dataIndex : "cwe-relative-relevance",
		    width : 100,
		    sortable : false,
		    renderer : cwe.Util.relevanceRenderer
		});
	} else {
		newStore = this.getDefaultStore();
	}

	this.updateStore(newStore, new Ext.grid.ColumnModel(columnModel));
};

cwe.modelgrid.ModelGrid.prototype.updateStore = function(newStore, newColumnModel) {
	this.store = newStore;
	this.pagingBar.bindStore(newStore);
	this.reconfigure(newStore, newColumnModel);

	newStore.load( {
		params : {
		    start : 0,
		    limit : this.objectPerPage
		}
	});
};

cwe.modelgrid.ModelGrid.prototype.exportReport = function() {
	var config = {};

	config.action = this.store instanceof chi.model.Store ? "list" : "search";
	config.query = this.searchField.getValue();

	config.type = this.modelDescription.getId();

	config.pageFormat = this.pageFormat;
	config.pageOrientation = this.pageOrientation;

	config.columns = [];
	var colModel = this.getColumnModel();
	var numCols = colModel.getColumnCount();
	for ( var i = 0; i < numCols; i++) {
		if (!colModel.isHidden(i)) {
			config.columns.push( {
			    name : colModel.getDataIndex(i),
			    width : colModel.getColumnWidth(i)
			});
		}
	}

	config.sort = this.store.getSortState();

	var configString = Ext.encode(config).replace("'", '"');

	var reportWindow = window.open();
	var doc = reportWindow.document;

	var title = chi.Dict.translate("Report");
	var message = chi.Dict.translate("Please wait while your report is generated ...");

	doc.write("<html><head><title>Badger Finance " + title + "</title><style type='text/css'>body { font-family: sans-serif; font-size: 60%; }</style></head><body><h1>Badger Finance " + title
	        + "</h1><p>" + message + "</p><form id='report' method='post' action='../application/main.php'><input type='hidden' name='usr_action' value='report'/>");

	doc.write("<input type='hidden' id='config' name='config' value='" + configString + "'/>");

	doc.write("</form><script type='text/javascript'>document.getElementById('report').submit();</script></body></html>");
	reportWindow.focus();
};

