/******************************************************************************
 * Copyright (C) 2008 Elaine Tan                                              *
 * This program is free software; you can redistribute it and/or modify it    *
 * under the terms version 2 of the GNU General Public License as published   *
 * by the Free Software Foundation. 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 General Public License for more details.                       *
 * You should have received a copy of the GNU General Public License along    *
 * with this program; if not, write to the Free Software Foundation, Inc.,    *
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.                     *
 *****************************************************************************/
package org.adempiere.webui.panel;

import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.logging.Level;

import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.component.ConfirmPanel;
import org.adempiere.webui.component.Datebox;
import org.adempiere.webui.component.Grid;
import org.adempiere.webui.component.Label;
import org.adempiere.webui.component.ListItem;
import org.adempiere.webui.component.Listbox;
import org.adempiere.webui.component.NumberBox;
import org.adempiere.webui.component.Row;
import org.adempiere.webui.component.Rows;
import org.adempiere.webui.component.Window;
import org.adempiere.webui.editor.WDateEditor;
import org.adempiere.webui.editor.WNumberEditor;
import org.adempiere.webui.editor.WStringEditor;
import org.adempiere.webui.util.ZKUpdateUtil;
import org.compiere.model.MAttribute;
import org.compiere.model.MAttributeSet;
import org.compiere.model.MRole;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
import org.compiere.util.Msg;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Div;
import org.zkoss.zul.Separator;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Vbox;

/**
 * Search by Product Attribute.
 * @author Elaine
 *
 */
public class InfoPAttributePanel extends Window implements EventListener<Event>
{
	/**
	 * generated serial id
	 */
	private static final long serialVersionUID = -4922961793415942591L;

	/** the attribute set selected on the InfoProduct window */
	private int p_M_AttributeSet_ID = 0;
	
	/**
	 * 	Constructor.
	 * 	Called from InfoProduct,cmd_InfoPAttribute
	 *	@param input
	 */
	@SuppressWarnings("deprecation")
	public InfoPAttributePanel(Object input)
	{
		super();
		if (input != null) {
			if (input instanceof InfoProductPanel) {
				p_M_AttributeSet_ID = ((InfoProductPanel)input).getM_AttributeSet_ID();
			} else if (input instanceof Integer) {
				p_M_AttributeSet_ID = (Integer)input;
			}
		}
		setTitle(Msg.getMsg(Env.getCtx(), "InfoPAttribute"));
		this.setBorder("normal");
		this.setMaximizable(true);
		this.setSizable(true);
		
		try
		{
			jbInit();
			dynInit();
		}
		catch (Exception e)
		{
			log.log(Level.SEVERE, "InfoPAttribute", e);
		}
		if (input instanceof Window)
			AEnv.showCenterWindow((Window)input, this);
		else
			AEnv.showWindow(this);
	}	//	InfoPAttribute
	
	/**	Resulting Query			*/
	private String		m_query = "";
	/**	Product Attribute Editors	*/
	private ArrayList<Component>	m_productEditors = new ArrayList<Component>();
	private ArrayList<Component>	m_productEditorsTo = new ArrayList<Component>();
	/**	Instance Attribute Editors	*/
	private ArrayList<Component>	m_instanceEditors = new ArrayList<Component>();
	private ArrayList<Component>	m_instanceEditorsTo = new ArrayList<Component>();
	/**	Logger			*/
	private static final CLogger log = CLogger.getCLogger(InfoPAttributePanel.class);

	private Rows rows = null;
	private ConfirmPanel confirmPanel = new ConfirmPanel(true);
	//
	private Label serNoLabel = new Label(Msg.translate(Env.getCtx(), "SerNo"));
	private WStringEditor serNoField = new WStringEditor("SerNo", false, false, true, 10, 20, null, null); 
	private Label lotLabel = new Label(Msg.translate(Env.getCtx(), "Lot"));
	private WStringEditor lotField = new WStringEditor("Lot", false, false, true, 10, 20, null, null); 
	private Listbox guaranteeDateSelection = null;
	private Datebox guaranteeDateField = new Datebox();
	private Label lotLabel2 = new Label(Msg.translate(Env.getCtx(), "M_Lot_ID"));
	private Listbox lotSelection = null; 
	//

	/**
	 * 	Layout dialog
	 *	@throws Exception
	 */
	private void jbInit() throws Exception
	{
		Vbox vbox = new Vbox();
		this.appendChild(vbox);
		
		Grid grid = new Grid();
		ZKUpdateUtil.setWidth(grid, "400px");
		grid.setStyle("margin:0; padding:0;");
		grid.makeNoStrip();
		grid.setOddRowSclass("even");
		vbox.appendChild(grid);
        
		rows = new Rows();
		grid.appendChild(rows);
		
		//	ConfirmPanel
		confirmPanel.addActionListener(this);		
		vbox.appendChild(confirmPanel);
	}	//	jbInit

	/**
	 * 	Dynamic Init of the Center Panel
	 */
	private void dynInit()
	{
		addAttributes();

		boolean isGuarantee = true;
		boolean isSerial = true;
		boolean isLot = true;
		if (p_M_AttributeSet_ID > 0) {
			MAttributeSet as = new MAttributeSet(Env.getCtx(), p_M_AttributeSet_ID, null);
			isGuarantee = as.isGuaranteeDate();
			isSerial = as.isSerNo();
			isLot = as.isLot();
		}
		//
		String s = Msg.translate(Env.getCtx(), "GuaranteeDate");
		guaranteeDateSelection = new Listbox();
		guaranteeDateSelection.setRows(0);
		guaranteeDateSelection.setMultiple(false);
		guaranteeDateSelection.setMold("select");
		ZKUpdateUtil.setWidth(guaranteeDateSelection, "150px");
		guaranteeDateSelection.appendItem(s + " <", s + " <");
		guaranteeDateSelection.appendItem(s + " =", s + " =");
		guaranteeDateSelection.appendItem(s + " >", s + " >");
		initLotSelection();
		
		//	Fixed Instance Selection Fields
		Row row;
		Div div;
		if (isSerial) {
			row	= new Row();
			rows.appendChild(row);
			div = new Div();
			div.setStyle("text-align: right;");
			div.appendChild(serNoLabel);
			row.appendChild(div);
			row.appendChild(serNoField.getComponent());
			ZKUpdateUtil.setWidth(serNoField.getComponent(), "150px");
		}
		
		if (isLot) {
			row = new Row();
			rows.appendChild(row);
			div = new Div();
			div.setStyle("text-align: right;");
			div.appendChild(lotLabel);
			row.appendChild(div);
			row.appendChild(lotField.getComponent());
			ZKUpdateUtil.setWidth(lotField.getComponent(), "150px");

			row = new Row();
			rows.appendChild(row);
			div = new Div();
			div.setStyle("text-align: right;");
			div.appendChild(lotLabel2);
			row.appendChild(div);
			row.appendChild(lotSelection);
		}

		if (isGuarantee) {
			row = new Row();
			rows.appendChild(row);
			div = new Div();
			div.setStyle("text-align: right;");
			div.appendChild(guaranteeDateSelection);
			row.appendChild(div);
			row.appendChild(guaranteeDateField);
		}
	}	//	dynInit

	/**
	 * 	Add Attributes
	 *	@return rows
	 */
	private int addAttributes()
	{
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String joinAttributeSet;
		if (p_M_AttributeSet_ID > 0)
			joinAttributeSet = "JOIN M_AttributeUse mau ON (a.M_Attribute_ID = mau.M_Attribute_ID AND mau.M_AttributeSet_ID="+p_M_AttributeSet_ID+")";
		else
			joinAttributeSet = "";
		String sql = MRole.getDefault().addAccessSQL(
			"SELECT a.M_Attribute_ID, a.Name, a.Description, a.AttributeValueType, a.IsInstanceAttribute "
			+ "FROM M_Attribute a "
			+ joinAttributeSet
			+ " WHERE a.IsActive='Y' "
			+ " ORDER BY "
			+ (p_M_AttributeSet_ID > 0 ? "mau.SeqNo, " : "")
			+ "a.IsInstanceAttribute, a.Name", 
			"a", MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);
		boolean instanceLine = false;
		try
		{
			pstmt = DB.prepareStatement(sql, null);
			rs = pstmt.executeQuery();
			while (rs.next())
			{
				int attribute_ID = rs.getInt(1);
				String name = rs.getString(2);
				String description = rs.getString(3);
				String attributeValueType = rs.getString(4);
				boolean isInstanceAttribute = "Y".equals(rs.getString(5)); 
				//	Instance switch
				if (!instanceLine && isInstanceAttribute)
				{
					Row row = new Row();
					rows.appendChild(row);
					Div div = new Div();
					div.setStyle("text-align: left;width: 100%");
    				Label group = new Label(Msg.translate(Env.getCtx(), "IsInstanceAttribute"));
    				div.appendChild(group);
					row.appendCellChild(div, 2);
    				
    				row = new Row();
					rows.appendChild(row);
					Separator separator = new Separator();
                    separator.setBar(true);
					row.appendCellChild(separator, 2);
        			
					instanceLine = true;
				}
				//
				
				Row row = new Row();
				rows.appendChild(row);
				
				Label label = new Label(name);
				if (description != null && description.length() > 0)
					label.setTooltiptext(description);
				
				Div div = new Div();
				div.setStyle("text-align: right;");
				div.appendChild(label);
				row.appendChild(div);
				
				HtmlBasedComponent field = null;
				if (MAttribute.ATTRIBUTEVALUETYPE_List.equals(attributeValueType))
				{
					field = new Listbox();
					((Listbox) field).setRows(0);
					((Listbox) field).setMultiple(false);
					((Listbox) field).setMold("select");
					KeyNamePair[] knp = getAttributeList(attribute_ID);
					for(int i = 0; i < knp.length; i++)
						((Listbox) field).appendItem(knp[i].getName(), knp[i]);
				}
				else if (MAttribute.ATTRIBUTEVALUETYPE_Number.equals(attributeValueType))
				{
					field = new WNumberEditor(name, false, false, true, DisplayType.Number, name).getComponent();
				}
				else if(MAttribute.ATTRIBUTEVALUETYPE_Date.equals(attributeValueType))
				{
					field = new WDateEditor(name, false, false, true, name).getComponent();
				}
				else
				{
					field = new WStringEditor(name, false, false, true, 10, 40, null, null).getComponent();
				}
				
				ZKUpdateUtil.setWidth(field, "96%");
				row.appendChild(field);
				//
				field.setId(String.valueOf(attribute_ID));
				if (isInstanceAttribute)
					m_instanceEditors.add(field);
				else
					m_productEditors.add(field);
				
				//	To (numbers)
				Component fieldTo = null;
				if (MAttribute.ATTRIBUTEVALUETYPE_Number.equals(attributeValueType))
				{
					fieldTo = new WNumberEditor(name, false, false, true, DisplayType.Number, name).getComponent();
					ZKUpdateUtil.setWidth(((NumberBox) fieldTo), "150px");
					row = new Row();
					rows.appendChild(row);
					div = new Div();
					div.setStyle("text-align: right;");
					div.appendChild(new Label("-"));
					row.appendChild(div);
					row.appendChild(fieldTo);
				}
				if (isInstanceAttribute)
					m_instanceEditorsTo.add(fieldTo);
				else
					m_productEditorsTo.add(fieldTo);
			}
		}
		catch (Exception e)
		{
			log.log(Level.SEVERE, sql, e);
		}
		finally {
			DB.close(rs, pstmt);
			rs = null; pstmt = null;
		}
		
		// print instance line if not printed
		if (!instanceLine) {
			boolean isGuarantee = true;
			boolean isSerial = true;
			boolean isLot = true;
			if (p_M_AttributeSet_ID > 0) {
				MAttributeSet as = new MAttributeSet(Env.getCtx(), p_M_AttributeSet_ID, null);
				isGuarantee = as.isGuaranteeDate();
				isSerial = as.isSerNo();
				isLot = as.isLot();
			}
			if (isGuarantee || isSerial || isLot) {
				Row row = new Row();
				rows.appendChild(row);
				Label group = new Label(Msg.translate(Env.getCtx(), "IsInstanceAttribute"));
				row.appendCellChild(group, 2);
				
				row = new Row();
				rows.appendChild(row);
				Separator separator = new Separator();
                separator.setBar(true);
				row.appendCellChild(separator, 2);
    			
				instanceLine = true;
			}
		}
		
		return 0;
	}	//	addProductAttributes

	/**
	 *	Get Attribute List
	 *	@param M_Attribute_ID attribute
	 *	@return [M_AttributeValue_ID:Name]
	 */
	private KeyNamePair[] getAttributeList(int M_Attribute_ID)
	{
		ArrayList<KeyNamePair> list = new ArrayList<KeyNamePair>();
		list.add(new KeyNamePair(-1, ""));
		
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = MRole.getDefault().addAccessSQL( 
			"SELECT M_AttributeValue_ID, Value, Name "
			+ "FROM M_AttributeValue "
			+ "WHERE M_Attribute_ID=? "
			+ "ORDER BY 2",
			"M_AttributeValue", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO);
		try
		{
			pstmt = DB.prepareStatement(sql, null);
			pstmt.setInt(1, M_Attribute_ID);
			rs = pstmt.executeQuery();
			while (rs.next())
				list.add(new KeyNamePair(rs.getInt(1), rs.getString(3)));
		}
		catch (Exception e)
		{
			log.log(Level.SEVERE, sql, e);
		}
		finally {
			DB.close(rs, pstmt);
			rs = null; pstmt = null;
		}
		KeyNamePair[] retValue = new KeyNamePair[list.size()];
		list.toArray(retValue);
		return retValue;
	}	//	getAttributeList


	/**
	 * 	Initialize Lot Selection
	 */
	private void initLotSelection()
	{
		ArrayList<KeyNamePair> list = new ArrayList<KeyNamePair>();
		list.add(new KeyNamePair(-1, ""));
		
		String whereAttributeSet;
		if (p_M_AttributeSet_ID > 0)
			whereAttributeSet = " AND M_Product_ID IN (SELECT M_Product_ID FROM M_Product WHERE M_AttributeSet_ID="+p_M_AttributeSet_ID+")";
		else
			whereAttributeSet = "";
		String sql = MRole.getDefault().addAccessSQL(
			"SELECT M_Lot_ID, Name FROM M_Lot WHERE IsActive='Y' " + whereAttributeSet + " ORDER BY 2",
			"M_Lot", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO);
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try
		{
			pstmt = DB.prepareStatement(sql, null);
			rs = pstmt.executeQuery();
			while (rs.next())
				list.add(new KeyNamePair(rs.getInt(1), rs.getString(2)));
		}
		catch (Exception e)
		{
			log.log(Level.SEVERE, sql, e);
		}
		finally {
			DB.close(rs, pstmt);
			rs = null; pstmt = null;
		}
		//	Create List
		KeyNamePair[] items = new KeyNamePair[list.size()];
		list.toArray(items);
		lotSelection = new Listbox();
		lotSelection.setRows(0);
		lotSelection.setMultiple(false);
		lotSelection.setMold("select");
		ZKUpdateUtil.setWidth(lotSelection, "150px");
		for(int i = 0; i < items.length; i++)
			lotSelection.appendItem(items[i].getName(), items[i]);
	}	//	initLotSelection


	
	/**
	 *	Handle event
	 *	@param e event
	 */
	@Override
	public void onEvent(Event e) throws Exception 
	{
		if (e.getTarget().getId().equals(ConfirmPanel.A_OK))
		{
			createQuery();
			dispose();
		}
		else if (e.getTarget().getId().equals(ConfirmPanel.A_CANCEL))
		{
			m_query = null;
			dispose();
		}
	}

	/**
	 * 	Create Query
	 *  <p>
	 * 	Available synonyms:
	 *	<ul>M_Product p</ul>
	 *	<ul>M_ProductPrice pr</ul>
	 *	<ul>M_AttributeSet pa</ul>
	 *	</p>
	 *	@return query
	 */
	private String createQuery()
	{
		/** Base Query
		SELECT *
		FROM M_Product p
 		 INNER JOIN M_ProductPrice pr ON (p.M_Product_ID=pr.M_Product_ID)
 		 LEFT OUTER JOIN M_AttributeSet pa ON (p.M_AttributeSet_ID=pa.M_AttributeSet_ID)
		WHERE
		**/
		
		/***	Instance Attributes		*/		
		StringBuilder sb = new StringBuilder();
		//	Serial No
		String s = serNoField.getComponent().getText();
		if (s != null && s.length() > 0)
		{
			sb.append(" AND asi.SerNo");
			if (s.indexOf('%') == -1 && s.indexOf('_') == 1)
				sb.append("=");
			else
				sb.append(" LIKE ");
			sb.append(DB.TO_STRING(s));
		}
		//	Lot Number
		s = lotField.getComponent().getText();
		if (s != null && s.length() > 0)
		{
			sb.append(" AND asi.Lot");
			if (s.indexOf('%') == -1 && s.indexOf('_') == 1)
				sb.append("=");
			else
				sb.append(" LIKE ");
			sb.append(DB.TO_STRING(s));
		}
		//	Lot ID
		ListItem li = lotSelection.getSelectedItem();
		if(li != null && li.getValue() != null)
		{
			KeyNamePair pp = (KeyNamePair) li.getValue();
			if (pp != null && pp.getKey() != -1)
			{
				int ID = pp.getKey();
				sb.append(" AND asi.M_Lot_ID=").append(ID);
			}
		}
		
		//	Guarantee Date
		if (guaranteeDateField.getValue() != null)
		{
			Timestamp ts = new Timestamp(guaranteeDateField.getValue().getTime());
			sb.append(" AND TRUNC(asi.GuaranteeDate)");
			int index = guaranteeDateSelection.getSelectedIndex();	//	 < = >
			if (index == 0)
				sb.append("<");
			else if (index == 1)
				sb.append("=");
			else
				sb.append(">");
			sb.append(DB.TO_DATE(ts,true));
		}
		
		//	Instance Editors
		for (int i = 0; i < m_instanceEditors.size(); i++)
		{
			StringBuffer iAttr = new StringBuffer();
			Component c = (Component)m_instanceEditors.get(i);
			Component cTo = (Component)m_instanceEditorsTo.get(i);
			int M_Attribute_ID = Integer.parseInt(c.getId());
			if (c instanceof Listbox)
			{
				Listbox field = (Listbox)c;
				li = field.getSelectedItem();
				if(li != null && li.getValue() != null)
				{
					KeyNamePair pp = (KeyNamePair)li.getValue();
					if (pp != null && pp.getKey() != -1)
					{
						iAttr.append("M_Attribute_ID=").append(M_Attribute_ID)
							.append(" AND M_AttributeValue_ID=").append(pp.getKey());
					} 
				}
			}
			else if (c instanceof NumberBox)
			{
				NumberBox field = (NumberBox)c;
				BigDecimal value = (BigDecimal)field.getValue();
				NumberBox fieldTo = (NumberBox)cTo;
				BigDecimal valueTo = (BigDecimal)fieldTo.getValue();
				if (value != null || valueTo != null)
				{
					iAttr.append("M_Attribute_ID=").append(M_Attribute_ID)
						.append(" AND ValueNumber");
					if (value != null && valueTo == null)
						iAttr.append("=").append(value);
					else if (value == null && valueTo != null)
						iAttr.append("<=").append(valueTo);
					else if (value != null && valueTo != null)
						iAttr.append(" BETWEEN ").append(value)
							.append(" AND ").append(valueTo);
				} 
			}
			else
			{
				Textbox field = (Textbox)c;
				String value = field.getText();
				if (value != null && value.length() > 0)
				{
					iAttr.append("M_Attribute_ID=").append(M_Attribute_ID)
						.append(" AND Value");
					if (value.indexOf('%') == -1 && value.indexOf('_') == -1)
						iAttr.append("=");
					else
						iAttr.append(" LIKE ");
					iAttr.append(DB.TO_STRING(value));
				}
			}
			//	Add to where
			if (iAttr.length() > 0)
				sb.append(" AND asi.M_AttributeSetInstance_ID IN "
					+ "(SELECT M_AttributeSetInstance_ID FROM M_AttributeInstance "
					+ "WHERE ")
					.append(iAttr).append(")");
		}
		
		//	finish Instance Attributes
		if (sb.length() > 0)
		{
			sb.insert(0, " AND EXISTS (SELECT * FROM M_Storage s"
				+ " INNER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) "
				+ "WHERE s.M_Product_ID=p.M_Product_ID");
			sb.append(")");
		}
		
		
		//	Product Attributes 
		for (int i = 0; i < m_productEditors.size(); i++)
		{
			StringBuffer pAttr = new StringBuffer();
			Component c = (Component)m_productEditors.get(i);
			Component cTo = (Component)m_productEditorsTo.get(i);
			int M_Attribute_ID = Integer.parseInt(c.getId());
			if (c instanceof Listbox)
			{
				Listbox field = (Listbox)c;
				li = field.getSelectedItem();
				if(li != null && li.getValue() != null)
				{
					KeyNamePair pp = (KeyNamePair)li.getValue();
					if (pp != null && pp.getKey() != -1)
					{
						pAttr.append("M_Attribute_ID=").append(M_Attribute_ID)
							.append(" AND M_AttributeValue_ID=").append(pp.getKey());
					} 
				}
			}
			else if (c instanceof NumberBox)
			{
				NumberBox field = (NumberBox)c;
				BigDecimal value = (BigDecimal)field.getValue();
				NumberBox fieldTo = (NumberBox)cTo;
				BigDecimal valueTo = (BigDecimal)fieldTo.getValue();
				if (value != null || valueTo != null)
				{
					pAttr.append("M_Attribute_ID=").append(M_Attribute_ID)
						.append(" AND ValueNumber");
					if (value != null && valueTo == null)
						pAttr.append("=").append(value);
					else if (value == null && valueTo != null)
						pAttr.append("<=").append(valueTo);
					else if (value != null && valueTo != null)
						pAttr.append(" BETWEEN ").append(value)
							.append(" AND ").append(valueTo);
				} 
			}
			else
			{
				Textbox field = (Textbox)c;
				String value = field.getText();
				if (value != null && value.length() > 0)
				{
					pAttr.append("M_Attribute_ID=").append(M_Attribute_ID)
						.append(" AND Value");
					if (value.indexOf('%') == -1 && value.indexOf('_') == -1)
						pAttr.append("=");
					else
						pAttr.append(" LIKE ");
					pAttr.append(DB.TO_STRING(value));
				}
			}
			//	Add to Where
			if (pAttr.length() > 0)
				sb.append(" AND p.M_AttributeSetInstance_ID IN "
					+ "(SELECT M_AttributeSetInstance_ID "
					+ "FROM M_AttributeInstance WHERE ")
					.append(pAttr).append(")");
		}
		//
		m_query = null;
		if (sb.length() > 0)
			m_query = sb.toString();
		if (log.isLoggable(Level.CONFIG))
			log.config(m_query);		
		return m_query;
	}	//	createQuery

	/**
	 * 	Get where clause
	 *	@return query or null
	 */
	public String getWhereClause()
	{
		return m_query;
	}	//	getQuery

}
