/******************************************************************************
 * Product: iDempiere ERP & CRM Smart Business Solution                       *
 * Copyright (C) 1999-2012 ComPiere, Inc. All Rights Reserved.                *
 * 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.                     *
 * For the text or an alternative of this public license, you may reach us    *
 * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA        *
 * or via info@compiere.org or http://www.compiere.org/license.html           *
 *****************************************************************************/
package org.compiere.model;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;

import org.compiere.util.Env;
import org.compiere.util.Util;

/**
 * Landed cost for order
 * @author hengsin
 */
public class MOrderLandedCost extends X_C_OrderLandedCost {

	/**
	 * generated serial id
	 */
	private static final long serialVersionUID = 2629138678703667123L;

    /**
     * UUID based Constructor
     * @param ctx  Context
     * @param C_OrderLandedCost_UU  UUID key
     * @param trxName Transaction
     */
    public MOrderLandedCost(Properties ctx, String C_OrderLandedCost_UU, String trxName) {
        super(ctx, C_OrderLandedCost_UU, trxName);
    }

	/**
	 * @param ctx
	 * @param C_OrderLandedCost_ID
	 * @param trxName
	 */
	public MOrderLandedCost(Properties ctx, int C_OrderLandedCost_ID, String trxName) {
		super(ctx, C_OrderLandedCost_ID, trxName);
	}

	/**
	 * @param ctx
	 * @param rs
	 * @param trxName
	 */
	public MOrderLandedCost(Properties ctx, ResultSet rs, String trxName) {
		super(ctx, rs, trxName);
	}

	/**
	 * 	Get landed cost lines
	 * 	@param C_Order_ID
	 * 	@return array of MOrderLandedCost
	 */
	public static MOrderLandedCost[] getOfOrder (int C_Order_ID, String trxName)
	{
		StringBuilder whereClause = new StringBuilder(COLUMNNAME_C_Order_ID).append("=?");
		List<MOrderLandedCost> list = new Query(Env.getCtx(), I_C_OrderLandedCost.Table_Name, whereClause.toString(), trxName)
										.setParameters(C_Order_ID)
										.list();
		return list.toArray(new MOrderLandedCost[list.size()]);
	}	//	getOfOrder
	
	/**
	 * 	Get Lines of allocation
	 * 	@param whereClause starting with AND
	 * 	@return landed cost allocation lines
	 */
	public MOrderLandedCostAllocation[] getLines (String whereClause)
	{
		StringBuilder whereClauseFinal = new StringBuilder(COLUMNNAME_C_OrderLandedCost_ID).append("=?");
		if (!Util.isEmpty(whereClause))
			whereClauseFinal.append(" ").append(whereClause);
		List<MOrderLandedCostAllocation> list = new Query(getCtx(), I_C_OrderLandedCostAllocation.Table_Name, whereClauseFinal.toString(), get_TrxName())
										.setParameters(getC_OrderLandedCost_ID())
										.list();
		return list.toArray(new MOrderLandedCostAllocation[list.size()]);
	}	//	getLines
	
	/**
	 * create landed cost allocations 
	 * @return error message or null
	 */
	public String distributeLandedCost() {
		MOrderLandedCostAllocation[] lines = getLines("");
		if (lines.length == 0) {
			MOrder order = (MOrder) getC_Order();
			MOrderLine[] orderLines = order.getLines();
			if (orderLines.length > 0) {
				List<MOrderLandedCostAllocation> list = new ArrayList<MOrderLandedCostAllocation>();
				for(MOrderLine line : orderLines) {
					if (line.getM_Product_ID() > 0) {
						MOrderLandedCostAllocation allocation = new MOrderLandedCostAllocation(getCtx(), 0, get_TrxName());
						allocation.setC_OrderLandedCost_ID(getC_OrderLandedCost_ID());
						allocation.setC_OrderLine_ID(line.getC_OrderLine_ID());
						allocation.setClientOrg(getAD_Client_ID(), getAD_Org_ID());
						allocation.setAmt(BigDecimal.ZERO);
						allocation.setBase(BigDecimal.ZERO);
						allocation.setQty(BigDecimal.ZERO);
						allocation.saveEx();
						list.add(allocation);
					}
				}
				if (list.size() > 0) {
					lines = list.toArray(lines);
				}
			}
		}
		
		if (lines.length == 1) {
			MOrderLine orderLine = (MOrderLine) lines[0].getC_OrderLine();
			BigDecimal base = orderLine.getBase(getLandedCostDistribution());
			if (base.signum() == 0){
				StringBuilder msgreturn = new StringBuilder("Total of Base values is 0 - ").append(getLandedCostDistribution());
				return msgreturn.toString();
			}
			lines[0].setBase(base);
			lines[0].setQty(orderLine.getQtyOrdered());
			lines[0].setAmt(getAmt());
			lines[0].saveEx();
		} else if (lines.length > 1) {
			//	Calculate total & base
			BigDecimal total = Env.ZERO;
			for (MOrderLandedCostAllocation allocation : lines)
			{
				MOrderLine orderLine = (MOrderLine) allocation.getC_OrderLine();
				total = total.add(orderLine.getBase(getLandedCostDistribution()));
			}
			if (total.signum() == 0){
				StringBuilder msgreturn = new StringBuilder("Total of Base values is 0 - ").append(getLandedCostDistribution());
				return msgreturn.toString();
			}	
			//	Create Allocations
			for (MOrderLandedCostAllocation allocation : lines)
			{
				MOrderLine orderLine = (MOrderLine) allocation.getC_OrderLine();
				BigDecimal base = orderLine.getBase(getLandedCostDistribution());
				allocation.setBase(base);
				allocation.setQty(orderLine.getQtyOrdered());
				// end MZ
				if (base.signum() != 0)
				{
					BigDecimal result = getAmt().multiply(base);
					result = result.divide(total, orderLine.getParent().getC_Currency().getCostingPrecision(), RoundingMode.HALF_UP);
					allocation.setAmt(result.doubleValue(), orderLine.getParent().getC_Currency().getCostingPrecision());
				}
				allocation.saveEx();	
			}
			allocateLandedCostRounding(lines);
		}
		return "";
	}
	
	/**
	 * If there are difference between landed cost amount (getAmt()) and total landed cost allocation (MOrderLandedCostAllocation) amount,
	 * add the difference to the largest landed cost allocation line.
	 * @param lines 
	 */
	private void allocateLandedCostRounding(MOrderLandedCostAllocation[] lines)
	{
		MOrderLandedCostAllocation largestAmtAllocation = null;
		BigDecimal allocationAmt = Env.ZERO;
		for (MOrderLandedCostAllocation allocation : lines)
		{
			if (largestAmtAllocation == null
				|| allocation.getAmt().compareTo(largestAmtAllocation.getAmt()) > 0)
				largestAmtAllocation = allocation;
			allocationAmt = allocationAmt.add(allocation.getAmt());
		}
		BigDecimal difference = getAmt().subtract(allocationAmt);
		if (difference.signum() != 0)
		{
			largestAmtAllocation.setAmt(largestAmtAllocation.getAmt().add(difference));
			largestAmtAllocation.saveEx();
			if (log.isLoggable(Level.CONFIG)) log.config("Difference=" + difference
				+ ", C_OrderLandedCostAllocation_ID=" + largestAmtAllocation.getC_OrderLandedCostAllocation_ID()
				+ ", Amt" + largestAmtAllocation.getAmt());
		}
	}	//	allocateLandedCostRounding
}
