/******************************************************************************
 * Copyright (C) 2012 Elaine Tan                                              *
 * Copyright (C) 2012 Trek Global
 * 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.compiere.grid;

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

import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.GridTab;
import org.compiere.model.MBankAccountProcessor;
import org.compiere.model.MSysConfig;
import org.compiere.model.PO;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Trx;
import org.compiere.util.TrxRunnable;

/**
 * Base class for payment form
 * @author Elaine
 */
public abstract class PaymentForm implements IPaymentForm {

	/**	Logger			*/
	protected transient CLogger log = CLogger.getCLogger(getClass());
	
	/**	Window						*/
	private int                 m_WindowNo = 0;
	/**	Tab							*/
	private GridTab         	m_mTab;
	
	// Data from Order/Invoice
	protected String 				m_DocStatus = null;
	/** Start Payment Rule */
	protected String 				m_PaymentRule = "";
	/** Start Acct Date */
	protected Timestamp 			m_DateAcct = null;

	/** Invoice Currency */
	protected int 					m_C_Currency_ID = 0;
	protected int 					m_AD_Client_ID = 0;
	protected boolean 				m_Cash_As_Payment = true;
	protected int 					m_AD_Org_ID = 0;
	protected int 					m_C_BPartner_ID = 0;
	protected BigDecimal 			m_Amount = Env.ZERO; // Payment Amount
	
	protected boolean				m_needSave = false;
	/** Only allow changing Rule        */
	protected boolean 				m_onlyRule = false;
	/** Is SOTrx					*/
	protected boolean				m_isSOTrx = true;
	
	/**
	 * @param WindowNo
	 * @param mTab
	 */
	public PaymentForm(int WindowNo, GridTab mTab) {
		m_WindowNo = WindowNo;
		m_isSOTrx = "Y".equals(Env.getContext(Env.getCtx(), WindowNo, "IsSOTrx"));
		m_mTab = mTab;
	}
	
	@Override
	public boolean dynInit() throws Exception {
		m_DocStatus = (String) m_mTab.getValue("DocStatus");
		log.config(m_DocStatus);

		if (m_mTab.getValue("C_BPartner_ID") == null) {
			throw new AdempiereException("SaveErrorRowNotFound");
		}

		// DocStatus
		if (m_DocStatus == null)
			m_DocStatus = "";
		// Is the Trx closed? Reversed / Voided / Cloased
		if (m_DocStatus.equals("RE") || m_DocStatus.equals("VO") || m_DocStatus.equals("CL"))
			return false;
		
		// Document is not complete - allow to change the Payment Rule only
		if (m_DocStatus.equals("CO") || m_DocStatus.equals("WP"))
			m_onlyRule = false;
		else
			m_onlyRule = true;
		// PO only Rule
		if (!m_onlyRule // Only order has Warehouse
				&& !m_isSOTrx && m_mTab.getValue("M_Warehouse_ID") != null)
			m_onlyRule = true;
		
		//  Amount
		m_Amount = (BigDecimal)m_mTab.getValue("GrandTotal");
		if (!m_onlyRule && m_Amount.compareTo(Env.ZERO) == 0)
		{
			throw new AdempiereException("PaymentZero");
		}
		
		/**
		 *	Get Data from Grid
		 */
		m_AD_Client_ID = ((Integer)m_mTab.getValue("AD_Client_ID")).intValue();
		m_Cash_As_Payment = MSysConfig.getBooleanValue(MSysConfig.CASH_AS_PAYMENT,true, m_AD_Client_ID);
		m_AD_Org_ID = ((Integer)m_mTab.getValue("AD_Org_ID")).intValue();
		m_C_BPartner_ID = ((Integer)m_mTab.getValue("C_BPartner_ID")).intValue();
		m_PaymentRule = (String)m_mTab.getValue("PaymentRule");
		m_C_Currency_ID = ((Integer)m_mTab.getValue("C_Currency_ID")).intValue();
		m_DateAcct = (Timestamp)m_mTab.getValue("DateAcct");
		
		/**
		 *	Payment Combo
		 */
		if (m_PaymentRule == null)
			m_PaymentRule = "";

		loadData();
		
		return true;
	}
	
	@Override
	public boolean saveChanges() {
		// BF [ 1920179 ] perform the save in a trx's context.
		final boolean[] success = new boolean[] { false };
		final TrxRunnable r = new TrxRunnable() {
			public void run(String trxName) {
				//  only Payment Rule
				if (isOnlyRule())
					success[0] = true;
				else
					success[0] = saveChangesInTrx(trxName);
			}
		};
		try {
			Trx.run(r);
		} catch (AdempiereException ae) {
			success[0] = false;
			throw ae;
		} catch (Throwable e) {
			success[0] = false;
			throw new AdempiereException("PaymentError", e);
		}
		
		afterSave(success[0]);
		
		return success[0];
	}
	
	/**
	 * After save and transaction have been committed/rollback
	 * @param success
	 */
	protected abstract void afterSave(boolean success);
	
	
	@Override
	public boolean needSave()
	{
		return m_needSave;
	}	//	needSave
	
	/**
	 *  Get Invoice ID for Order
	 *  @param C_Order_ID order
	 *  @return C_Invoice_ID or 0 if not found
	 */
	protected int getInvoiceID (int C_Order_ID)
	{
		int retValue = 0;
		String sql = "SELECT C_Invoice_ID FROM C_Invoice WHERE C_Order_ID=? "
			+ "AND DocStatus NOT IN ('VO','RE') "
			+ "ORDER BY C_Invoice_ID DESC";     //  last invoice
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try
		{
			pstmt = DB.prepareStatement(sql, null);
			pstmt.setInt(1, C_Order_ID);
			rs = pstmt.executeQuery();
			if (rs.next())
				retValue = rs.getInt(1);
		}
		catch (SQLException e)
		{
			log.log(Level.SEVERE, sql, e);
		}
		finally
		{
			DB.close(rs, pstmt);
			rs = null;
			pstmt = null;
		}
		
		return retValue;
	}   //  getInvoiceID
	
	@Override
	public void processOnline()
	{
		throw new AdempiereException(Msg.getMsg(Env.getCtx(), "ActionNotSupported"));
	}
	
	/**
	 * Is online payment processor have been configured for bank account
	 * @param ctx
	 * @param tender
	 * @param CCType
	 * @param AD_Client_ID
	 * @param C_Currency_ID
	 * @param PayAmt
	 * @param trxName
	 * @return true if online payment processor have been configured for bank account
	 */
	protected boolean isBankAccountProcessorExist(Properties ctx, String tender, String CCType, int AD_Client_ID, int C_Currency_ID, BigDecimal PayAmt, String trxName)
	{
		MBankAccountProcessor[] m_mBankAccountProcessors = MBankAccountProcessor.find(ctx, tender, CCType, AD_Client_ID, C_Currency_ID, PayAmt, trxName);
		//	Relax Amount
		if (m_mBankAccountProcessors == null || m_mBankAccountProcessors.length == 0)
			m_mBankAccountProcessors = MBankAccountProcessor.find(ctx, tender, CCType, AD_Client_ID, C_Currency_ID, Env.ZERO, trxName);
		if (m_mBankAccountProcessors == null || m_mBankAccountProcessors.length == 0)
			return false;
		return true;
	}
	
	/**
	 * Get online payment processor configuration
	 * @param ctx
	 * @param tender tender type
	 * @param CCType credit card type
	 * @param AD_Client_ID
	 * @param C_Currency_ID
	 * @param PayAmt
	 * @param trxName
	 * @return {@link MBankAccountProcessor}
	 */
	protected MBankAccountProcessor getBankAccountProcessor(Properties ctx, String tender, String CCType, int AD_Client_ID, int C_Currency_ID, BigDecimal PayAmt, String trxName)
	{
		MBankAccountProcessor[] m_mBankAccountProcessors = MBankAccountProcessor.find(ctx, tender, CCType, AD_Client_ID, C_Currency_ID, PayAmt, trxName);
		//	Relax Amount
		if (m_mBankAccountProcessors == null || m_mBankAccountProcessors.length == 0)
			m_mBankAccountProcessors = MBankAccountProcessor.find(ctx, tender, CCType, AD_Client_ID, C_Currency_ID, Env.ZERO, trxName);
		if (m_mBankAccountProcessors == null || m_mBankAccountProcessors.length == 0)
			return null;

		MBankAccountProcessor m_mBankAccountProcessor = null;
		//	Find the first right one
		for (int i = 0; i < m_mBankAccountProcessors.length; i++)
		{
			MBankAccountProcessor bap = m_mBankAccountProcessors[i];
			if (bap.accepts(tender, CCType))
			{
				m_mBankAccountProcessor = m_mBankAccountProcessors[i];
				break;
			}
		}
		//
		return m_mBankAccountProcessor;
	}
	
	/**
	 * @return {@link GridTab}
	 */
	public GridTab getGridTab()
	{
		return m_mTab;
	}
	
	@Override
	public boolean isOnlyRule()
	{
		return m_onlyRule;
	}
	
	@Override
	public boolean isApproved()
	{
		return true;
	}
	
	/**
	 * @return window no
	 */
	public int getWindowNo()
	{
		return m_WindowNo;
	}
	
	@Override
	public void setCustomizeValues(PO po) 
	{
		
	}
	
	@Override
	public void setBankAccountProcessor(MBankAccountProcessor bankAccountProcessor)
	{
		
	}		
}
