//#############################################################################
//#                                                                           #
//#  Copyright (C) <2015>  <IMS MAXIMS>                                       #
//#                                                                           #
//#  This program is free software: you can redistribute it and/or modify     #
//#  it under the terms of the GNU Affero General Public License as           #
//#  published by the Free Software Foundation, either version 3 of the       #
//#  License, or (at your option) any later version.                          # 
//#                                                                           #
//#  This program is distributed in the hope that it will be useful,          #
//#  but WITHOUT ANY WARRANTY; without even the implied warranty of           #
//#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
//#  GNU Affero General Public License for more details.                      #
//#                                                                           #
//#  You should have received a copy of the GNU Affero General Public License #
//#  along with this program.  If not, see <http://www.gnu.org/licenses/>.    #
//#                                                                           #
//#  IMS MAXIMS provides absolutely NO GUARANTEE OF THE CLINICAL SAFTEY of    #
//#  this program.  Users of this software do so entirely at their own risk.  #
//#  IMS MAXIMS only ensures the Clinical Safety of unaltered run-time        #
//#  software that it builds, deploys and maintains.                          #
//#                                                                           #
//#############################################################################
//#EOH
// This code was generated by Catalin Tomozei using IMS Development Environment (version 1.70 build 3420.28895)
// Copyright (C) 1995-2009 IMS MAXIMS plc. All rights reserved.

package ims.rules.forms.ruleslist;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.List;

import org.w3c.dom.DOMException;
import org.xml.sax.SAXException;

import ims.admin.vo.BusinessRuleVo;
import ims.admin.vo.BusinessRuleVoCollection;
import ims.admin.vo.lookups.BusinessRuleCategory;
import ims.admin.vo.lookups.BusinessRuleCategoryCollection;
import ims.configuration.gen.ConfigFlag;
import ims.configuration.EnvironmentConfig;
import ims.domain.exceptions.DomainInterfaceException;
import ims.framework.Control;
import ims.framework.FormName;
import ims.framework.MessageButtons;
import ims.framework.MessageIcon;
import ims.framework.controls.DynamicGridCell;
import ims.framework.controls.DynamicGridColumn;
import ims.framework.controls.DynamicGridRow;
import ims.framework.controls.TreeNode;
import ims.framework.enumerations.Alignment;
import ims.framework.enumerations.DialogResult;
import ims.framework.enumerations.DynamicCellType;
import ims.framework.enumerations.FormMode;
import ims.framework.exceptions.CodingRuntimeException;
import ims.framework.exceptions.FormOpenException;
import ims.framework.exceptions.PresentationLogicException;
import ims.rules.exceptions.RulesEngineDeserializationException;
import ims.rules.exceptions.RulesEngineSerializationException;
import ims.rules.helper.Entities;
import ims.rules.types.RulesEngineEntity;
import ims.rules.utils.RuleXmlSerialization;
import ims.rules.vo.BusinessRulesSearchVo;
import ims.rules.vo.enums.RulesEditorComponentFormAction;
import ims.vo.LookupInstVo;

public class Logic extends BaseLogic
{  
	private static final long serialVersionUID = 1L;

	// Dynamic Grid column
	private static final int COL_IMAGE = 1;
	private static final int COL_RULE_NAME = 2;
	private static final int COL_RULE_BUSINESS_OBJECT = 3;
	private static final int COL_RULE_STATUS = 4;
	

	//----------------------------------------------------------------------------------------------------------------
	// EVENTS REGION
	//----------------------------------------------------------------------------------------------------------------
	// Form
	protected void onFormOpen(Object[] args) throws PresentationLogicException
	{
		initialize();
		open();
	}

	protected void onFormModeChanged()
	{
		updateControlsState();
	}

	protected void onFormDialogClosed(FormName formName, DialogResult result) throws PresentationLogicException
	{
		if (form.getForms().Rules.RuleXMLImport.equals(formName))
		{
			if (DialogResult.OK.equals(result))
			{
				form.getGlobalContext().Rules.RuleEditorComponent.setFormMode(RulesEditorComponentFormAction.EDIT);
				engine.open(form.getForms().Rules.RulesEdit);
			}
		}
		
		open();
	}

	// Buttons
	protected void onBtnNewClick() throws PresentationLogicException
	{
		newInstance();
	}

	protected void onBtnEditClick() throws PresentationLogicException
	{
		updateInstance();
	}

	protected void onBtnSearchClick() throws PresentationLogicException
	{
		form.getGlobalContext().Rules.RulesList.setRulesSearchCriteria(null);
		open();
	}
	
	protected void onBtnReloadAllRulesClick() throws PresentationLogicException 
	{
		boolean result = engine.reloadAllBusinessRules();
		if(result)
		{
			engine.showMessage("Rules reloaded successfully.", "Rules Reload", MessageButtons.OK, MessageIcon.INFORMATION);
		}
		else 
		{
			engine.showMessage("An error occured while reloading the rules. Please check the system log for the error details.", "Rules Reload", MessageButtons.OK, MessageIcon.ERROR);
		}		
	}

	protected void onBtnAssignPrioritiesClick() throws PresentationLogicException
	{
		engine.open(form.getForms().Rules.RulesPriorityEditor, false);
	}

	// Tree
	protected void onTreRootEntityTreeViewSelectionChanged(TreeNode node) throws PresentationLogicException
	{
		form.getGlobalContext().Rules.RulesList.setRulesSearchCriteria(null);
		open();
	}

	protected void onTreRootEntityTreeViewSelectionCleared() throws PresentationLogicException
	{
		open();
	}

	// Dynamic Grid
	protected void onDyngrdRulesRowSelectionChanged(DynamicGridRow row)
	{
		// Put selection in global context
		if (form.dyngrdRules().getValue() instanceof BusinessRuleVo)
		{
			form.getGlobalContext().Rules.setRuleToEdit((BusinessRuleVo) form.dyngrdRules().getValue());
		}
		
		updateControlsState();
	}

	protected void onDyngrdRulesRowSelectionCleared() throws PresentationLogicException
	{
		// Clear the global context
		form.getGlobalContext().Rules.setRuleToEdit(null);
		
		updateControlsState();
	}
	
	// Context menu
	protected void onContextMenuItemClick(int menuItemID, Control sender) throws PresentationLogicException
	{
		switch (menuItemID)
		{
			case GenForm.ContextMenus.RulesNamespace.RulesListContextMenu.NEW:
				newInstance();
				break;
				
			case GenForm.ContextMenus.RulesNamespace.RulesListContextMenu.EDIT:
				updateInstance();
				break;
				
			case GenForm.ContextMenus.RulesNamespace.RulesListContextMenu.VIEW:
				viewInstance();
				break;
			
			case GenForm.ContextMenus.RulesNamespace.RulesListContextMenu.CLONE:
				cloneInstance();
				break;
				
			case GenForm.ContextMenus.RulesNamespace.RulesListContextMenu.VIEWXML:
				previewInstance();
				break;
				
			case GenForm.ContextMenus.RulesNamespace.RulesListContextMenu.IMPORT:
				importXMLRule();
				break;
				
			case GenForm.ContextMenus.RulesNamespace.RulesListContextMenu.EXPORT:
				exportXMLRule();
				break;
		}
	}

	//------------------------------------------------------------------------------------------------------------------------
	// FORM CONTROL FUNCTIONS
	//------------------------------------------------------------------------------------------------------------------------
	
	/**
	 * 
	 */
	public void initialize() throws FormOpenException
	{
		super.initialize();
		
		// Set the buttons
		form.btnReloadAllRules().setEnabled(!ConfigFlag.GEN.RELEASE_MODE.getValue());
		form.btnReloadAllRules().setVisible(!ConfigFlag.GEN.RELEASE_MODE.getValue());
		
		// Set active only display
		form.chkActiveOnly().setValue(Boolean.TRUE);
		
		// Set BusinessRule entities
		setRulesEntities();
		
		// Initialize the dynamic grid
		initializeDynamicGrid();
	}
	
	/**
	 * 
	 */
	private void setRulesEntities()
	{
		// Clear combobox
		form.cmbEntity().clear();
		
		List<RulesEngineEntity> entities = new Entities().getAllPublicEntities();
		
		for (int i = 0 ; i < entities.size(); i++)
		{
			form.cmbEntity().newRow(entities.get(i), entities.get(i).getName());
		}
		
	}

	/**
	 * 
	 */
	private void initializeDynamicGrid()
	{
		// Allow un-selection
		form.dyngrdRules().setUnselectable(true);
		
		// Set up the columns
		DynamicGridColumn column;
		column = form.dyngrdRules().getColumns().newColumn("", COL_IMAGE);
		column.setWidth(30);
		column.setAlignment(Alignment.RIGHT);
		column.setCanGrow(false);
		
		column = form.dyngrdRules().getColumns().newColumn("Rule Name", COL_RULE_NAME);
		column.setWidth(200);
		column.setCanGrow(true);
		
		column = form.dyngrdRules().getColumns().newColumn("Entity", COL_RULE_BUSINESS_OBJECT);
		column.setWidth(200);
		column.setCanGrow(true);
		
		column = form.dyngrdRules().getColumns().newColumn("Status", COL_RULE_STATUS);
		column.setWidth(60);
		column.setCanGrow(false);
	}

	/**
	 * 
	 */
	public void open() throws PresentationLogicException
	{
		clearInstanceControls();

		try
		{
			// Set business rule category tree
			setBusinessRuleCategoryTree();			
			
			//Get search criteria
			boolean isActive 			= form.chkActiveOnly().getValue();
			String  name     			= form.txtRuleName().getValue();
			String  rulesEngineEntityID = form.cmbEntity().getValue() != null ? form.cmbEntity().getValue().getId() : null;
			Integer categoryID 			= form.treRootEntity().getValue() != null ? ((BusinessRuleCategory) form.treRootEntity().getValue()).getID() : null;
			
			//Set search criteria in Global Context
			if (form.getGlobalContext().Rules.RulesList.getRulesSearchCriteria() == null)
			{
				BusinessRulesSearchVo searchCriteria = new BusinessRulesSearchVo();
									  searchCriteria.setActiveOnly(isActive);
									  searchCriteria.setName(name);
									  searchCriteria.setEntityID(rulesEngineEntityID);
									  searchCriteria.setCategoryID(categoryID);
				
				form.getGlobalContext().Rules.RulesList.setRulesSearchCriteria(searchCriteria);
			}
			else
			{
				isActive 			= form.getGlobalContext().Rules.RulesList.getRulesSearchCriteria().getActiveOnly();
				name     			= form.getGlobalContext().Rules.RulesList.getRulesSearchCriteria().getName();
				rulesEngineEntityID = form.getGlobalContext().Rules.RulesList.getRulesSearchCriteria().getEntityID();
				categoryID 			= form.getGlobalContext().Rules.RulesList.getRulesSearchCriteria().getCategoryID();
				
				populateSearchCriteria(isActive, name, rulesEngineEntityID, categoryID);
			}
			
			// Search the rules
			if (form.treRootEntity().getValue() instanceof BusinessRuleCategory)
			{				
				setRulesList(domain.listRules(isActive, name, rulesEngineEntityID), categoryID);
			}
			else
			{
				setRulesList(domain.listRules(isActive,name, rulesEngineEntityID), null);
			}
			
			// Set focus on rule name search text box
			form.txtRuleName().setFocus();
		}
		catch (DomainInterfaceException e)
		{
			engine.showMessage(e.getMessage(), "UKSH - Error Message");
		}
		
		form.setMode(FormMode.VIEW);
	}


	private void populateSearchCriteria(boolean isActive, String name, String rulesEngineEntityID, Integer categoryID) 
	{
		form.chkActiveOnly().setValue(isActive);
		form.txtRuleName().setValue(name);
		
		if (rulesEngineEntityID != null)
		{
			List<RulesEngineEntity> entities = new Entities().getAllPublicEntities();		
			for (int i = 0 ; i < entities.size(); i++)
			{
				if (rulesEngineEntityID.equals(entities.get(i).getId()))
				{
					form.cmbEntity().setValue(entities.get(i));
					break;
				}
			}
		}
		
		if (categoryID != null)
		{
			for (int i = 0; i < form.treRootEntity().getNodes().size(); i++) 
			{
				if (form.treRootEntity().getNodes().get(i).getValue() instanceof BusinessRuleCategory &&
						((BusinessRuleCategory)form.treRootEntity().getNodes().get(i).getValue()).getID() == categoryID)
				{
					form.treRootEntity().setValue(form.treRootEntity().getNodes().get(i).getValue());
					break;
				}
			}
		}
	}

	/**
	 * 
	 */
	public void clearInstanceControls()
	{
		// Clear rules list
		form.dyngrdRules().getRows().clear();
		form.dyngrdRules().setFooterValue("Total: " + String.valueOf(form.dyngrdRules().getRows().size()));//WDEV-18221
		
	}

	/**
	 * 
	 */
	public void updateControlsState()
	{
		if (FormMode.VIEW.equals(form.getMode()))
		{
			form.btnEdit().setVisible(form.dyngrdRules().getValue() instanceof BusinessRuleVo);
		}
		
		form.treRootEntity().expandAll();
		
		updateContextMenu();
	}

	/**
	 * 
	 */
	private void updateContextMenu()
	{
		if (FormMode.VIEW.equals(form.getMode()))
		{
			form.getContextMenus().Rules.getRulesListContextMenuNEWItem().setVisible(true);
			form.getContextMenus().Rules.getRulesListContextMenuEDITItem().setVisible(form.dyngrdRules().getValue() instanceof BusinessRuleVo);
			form.getContextMenus().Rules.getRulesListContextMenuVIEWItem().setVisible(form.dyngrdRules().getValue() instanceof BusinessRuleVo);
			form.getContextMenus().Rules.getRulesListContextMenuCLONEItem().setVisible(form.dyngrdRules().getValue() instanceof BusinessRuleVo);
			
			form.getContextMenus().Rules.getRulesListContextMenuVIEWXMLItem().setVisible(form.dyngrdRules().getValue() instanceof BusinessRuleVo);
			
			form.getContextMenus().Rules.getRulesListContextMenuIMPORTItem().setVisible(true);
			form.getContextMenus().Rules.getRulesListContextMenuEXPORTItem().setVisible(form.dyngrdRules().getValue() instanceof BusinessRuleVo);
		}
		else  if (FormMode.EDIT.equals(form.getMode()))
		{
			form.getContextMenus().Rules.hideAllRulesListContextMenuMenuItems();
		}
	}

	/**
	 * 
	 */
	public void newInstance() throws PresentationLogicException
	{
		form.getGlobalContext().Rules.setRuleToEdit(null);
		form.getGlobalContext().Rules.RuleEditorComponent.setFormMode(RulesEditorComponentFormAction.NEW);
		editRule();
	}

	public void updateInstance()
	{
		editRule();
	}
	
	public void viewInstance()
	{
		viewRule();
	}

	public void cloneInstance()
	{
		cloneRule();
	}

	
	public void previewInstance()
	{
		previewRule();
	}

	private void previewRule()
	{
		form.getGlobalContext().Rules.RulesCodePreview.setXmlRule(form.getGlobalContext().Rules.getRuleToEdit().getXmlContent());
		engine.open(form.getForms().Rules.RuleCodePreview);
	}

	private void viewRule()
	{
		form.getGlobalContext().Rules.RuleEditorComponent.setFormMode(RulesEditorComponentFormAction.VIEW);
		engine.open(form.getForms().Rules.RulesEdit);		
	}

	private void editRule() 
	{
		form.getGlobalContext().Rules.RuleEditorComponent.setFormMode(RulesEditorComponentFormAction.EDIT);
		engine.open(form.getForms().Rules.RulesEdit);		
	}	
	
	private void cloneRule() 
	{
		form.getGlobalContext().Rules.RuleEditorComponent.setFormMode(RulesEditorComponentFormAction.CLONE);
		engine.open(form.getForms().Rules.RulesEdit);		
	}
	
	public boolean save() throws PresentationLogicException
	{
		// TODO Auto-generated method stub
		return false;
	}


	//--------------------------------------------------------------------------------------------------------------------------
	//	Tree functions
	//--------------------------------------------------------------------------------------------------------------------------
	
	/**
	 * 
	 */
	private void setBusinessRuleCategoryTree()
	{
		// Clear the tree
		form.treRootEntity().getNodes().clear();
		
		// Get business rule categories
		BusinessRuleCategoryCollection ruleCategoryCollection = ims.admin.vo.lookups.LookupHelper.getBusinessRuleCategory(this.domain.getLookupService());
		
		// If the rules categories are missing - interupt
		if (ruleCategoryCollection == null)
			return;
		
		// Set the category nodes
		for (int i = 0; i < ruleCategoryCollection.size(); i++)
		{
			BusinessRuleCategory businessRuleCategory = ruleCategoryCollection.get(i);
			
			if (businessRuleCategory != null && businessRuleCategory.getParent() == null)
			{
				// Set node value
				TreeNode node = form.treRootEntity().getNodes().add(businessRuleCategory, businessRuleCategory.getText());
				node.setIdentifier(new Integer(0));
				node.setText(((BusinessRuleCategory) node.getValue()).getText() + " (" + node.getIdentifier().toString() + ")");
				
				// Add sub categories
				setTreeNodeSubCategories(node, businessRuleCategory);
			}
		}
	}

	/**
	 * 
	 * @param nodeByValue
	 * @param businessRuleCategory
	 */
	private void setTreeNodeSubCategories(TreeNode node, BusinessRuleCategory businessRuleCategory)
	{
		if (businessRuleCategory == null)
			return;
		
		if (node == null)
			throw new CodingRuntimeException("Major Logical Error - Can not populate a null node in tree business rule category");
		
		// Add business rule childs to tree
		if (businessRuleCategory.getChildInstances() != null)
		{
			for (int i = 0; i < businessRuleCategory.getChildInstances().size(); i++)
			{
				LookupInstVo lookupInstVo = businessRuleCategory.getChildInstances().get(i);
				
				if (lookupInstVo instanceof BusinessRuleCategory)
				{
					// Set node value
					TreeNode childNode = node.getNodes().add((BusinessRuleCategory) lookupInstVo, ((BusinessRuleCategory) lookupInstVo).getText());
					childNode.setIdentifier(new Integer(0));
					childNode.setText(((BusinessRuleCategory) childNode.getValue()).getText() + " (" + childNode.getIdentifier().toString() + ")");
					
					// Add sub categories
					setTreeNodeSubCategories(childNode, (BusinessRuleCategory) lookupInstVo);
				}
			}
		}
		
	}

	//--------------------------------------------------------------------------------------------------------------------------
	// Dynamic Grid functions
	//--------------------------------------------------------------------------------------------------------------------------

	/**
	 * 
	 * @param listRules
	 */
	private void setRulesList(BusinessRuleVoCollection listRules, Integer categoryID)
	{
		// Clear the rule list
		form.dyngrdRules().getRows().clear();
				
		// If the collection is null - interrupt
		if (listRules == null || listRules.size() == 0)
		{	
			engine.showMessage("No rules matching the entered search criteria were found. ");//WDEV-18211 
			return;
		}
		// Iterate through the collection
		for (int i = 0; i < listRules.size(); i++)
		{
			BusinessRuleVo rule = listRules.get(i);
			
			// Add non nulls elements to the grid
			if (rule != null)
			{
				// Increment number on tree business rule category node
				TreeNode node = form.treRootEntity().getNodeByValue(rule.getCategory());
				node.setIdentifier(((Integer) node.getIdentifier()) + 1);
				node.setText(((BusinessRuleCategory) node.getValue()).getText() + " (" + node.getIdentifier().toString() + ")");

				if (categoryID != null)
				{
					if (categoryID.equals(rule.getCategory().getID()))
					{
						setRuleListRow(form.dyngrdRules().getRows().newRow(), rule);
					}
				}
				else
				{
					setRuleListRow(form.dyngrdRules().getRows().newRow(), rule);
				}
			}
		}
				
		form.dyngrdRules().setFooterValue("Total: " + String.valueOf(form.dyngrdRules().getRows().size()));//18211
	}

	/**
	 * 
	 * @param row
	 * @param rule
	 */
	private void setRuleListRow(DynamicGridRow row, BusinessRuleVo rule)
	{
		if (rule == null)
			return;
		
		if (row == null)
			throw new CodingRuntimeException("Major Logical Error - Can not populate a null row!");

		// Set up the row
		DynamicGridColumn column;
		DynamicGridCell cell;
		
		// The first cell
		column = form.dyngrdRules().getColumns().getByIdentifier(COL_IMAGE);
		cell = row.getCells().newCell(column, DynamicCellType.IMAGE);
		cell.setValue(form.getImages().Core.Edit);
		cell.setReadOnly(true);
		
		// The second cell
		column = form.dyngrdRules().getColumns().getByIdentifier(COL_RULE_NAME);
		cell = row.getCells().newCell(column, DynamicCellType.STRING);
		cell.setValue(rule.getName());
		cell.setReadOnly(true);
		
		// The third cell
		column = form.dyngrdRules().getColumns().getByIdentifier(COL_RULE_BUSINESS_OBJECT);
		cell = row.getCells().newCell(column, DynamicCellType.STRING);
		cell.setValue(Entities.getInstance().getEntityById(rule.getRootEntity()) == null ? "<Unknown Entity>" : Entities.getInstance().getEntityById(rule.getRootEntity()).getName());
		cell.setReadOnly(true);
		
		// The fourth cell
		column = form.dyngrdRules().getColumns().getByIdentifier(COL_RULE_STATUS);
		cell = row.getCells().newCell(column, DynamicCellType.BOOL);
		cell.setValue(rule.getActive());
		cell.setReadOnly(true);
		
		// Set the row value
		row.setValue(rule);
	}


	//---------------------------------------------------------------------------------------------------------------------------------------------
	//	Export XML Rule
	//---------------------------------------------------------------------------------------------------------------------------------------------

	/**
	 * 
	 */
	private void importXMLRule()
	{
		engine.open(form.getForms().Rules.RuleXMLImport);
	}

	//---------------------------------------------------------------------------------------------------------------------------------------------
	//	Export XML Rule
	//---------------------------------------------------------------------------------------------------------------------------------------------

	/**
	 * 
	 */
	private void exportXMLRule()
	{
		if (!(form.dyngrdRules().getValue() instanceof BusinessRuleVo))
			return;
		
		// Get the selected rule
		BusinessRuleVo rule = (BusinessRuleVo) form.dyngrdRules().getValue();
		String xmlFileName = rule.getName() + ".xml";

		try
		{
			// Build output directory
			String tempDir = EnvironmentConfig.getBaseUri() + ConfigFlag.GEN.FILE_UPLOAD_DIR.getValue() + "/Rules";

			File fileLocation = new File(tempDir);
			if (!fileLocation.exists())
			{
				fileLocation.mkdirs();
			}
			
			File file = new File(tempDir + "/" + xmlFileName);
			
			if (!file.exists())
			{
				file.createNewFile();
			}
			
			FileOutputStream outputStream = new FileOutputStream(file);
			outputStream.write((RuleXmlSerialization.serialize(RuleXmlSerialization.deserializeFromXml(rule.getXmlContent(), Entities.getInstance()))).getBytes());
			outputStream.flush();
			outputStream.close();
		}
		catch (FileNotFoundException e)
		{
			engine.showMessage("File cannot be created - ");
			return;
		}
		catch (IOException e)
		{
			engine.showMessage("An IOException occurred saving xml " + e.getMessage());
			return;
		}
		catch (DOMException e)
		{
			engine.showMessage(e.getMessage());
			return;
		}
		catch (RulesEngineSerializationException e)
		{
			engine.showMessage(e.getMessage());
			return;
		}
		catch (SAXException e)
		{
			engine.showMessage(e.getMessage());
			return;
		}
		catch (ParseException e)
		{
			engine.showMessage(e.getMessage());
			return;
		}
		catch (RulesEngineDeserializationException e)
		{
			engine.showMessage(e.getMessage());
			return;
		}

		form.getGlobalContext().Core.setYesNoDialogMessage(ConfigFlag.GEN.FILE_UPLOAD_DIR.getValue() + "/rules/" + xmlFileName);
		engine.open(form.getForms().Admin.ExportFileDialog);
	}
}
