/*
 * HVB.java
 * Created on May 11, 2005 
 * Author: 	L�szl� Felf�ldi, Etixpert GmbH
 * mail:	laszlo.felfoldi@etixpert.com		
 */

package com.etixpert.evolution.app.bonus;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringEscapeUtils;

import com.etixpert.evolution.app.utils.DateUtil;
import com.etixpert.evolution.value.DistributorValue;
import com.etixpert.evolution.value.order.BankAccountValue;
import com.etixpert.evolution.value.order.OrderHeaderValue;

public class SEPA {
	private static DecimalFormat nf = new DecimalFormat(".00", new DecimalFormatSymbols(Locale.ENGLISH));
	private static SimpleDateFormat df  = new SimpleDateFormat("yyyy-MM-dd");
	private static SimpleDateFormat df2 = new SimpleDateFormat("yyyyMMddHHmmssSSS");
	private static SimpleDateFormat df3 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
	
	private String bic;
	private String iban;
	
	private static String format(String val, String def) {
		String ret = def + val;
		return ret.substring(ret.length() - def.length());
	}
	
	private static String format(long val, String def) {
		return format("" + val, def);
	}
	
	private static String format(String val) {
		return StringEscapeUtils.escapeXml(val);
	}
	
	private static Long toNumber(String src) {
	    try {
	        return new Long(src.replaceAll("[^0-9]", ""));
	    } catch (Exception e) {
	        return null;
	    }
	}	
	
	private static boolean isEmpty(String value) {
		return value == null || value.trim().equals("");
	}
	
	public SEPA(String aBankCode, String aAccountNumber) {
		bic = aBankCode;
		iban = aAccountNumber;
	}
	
	public String generate(Date date, String prefix, boolean vat, 
				Set distributorsWithBonus, Set distributorsWithOpenOrders,
				Set distributorsWithPayingNotAllowed, Set distributorsWithError, Set distributorsWithoutFirstOrder,
				Map environment) {
		String msgId = "EVO-" + df2.format(date);
		
		double sum = 0;
		
		Calendar payDate = new GregorianCalendar();
		payDate.setTime(date);
		payDate.set(Calendar.DAY_OF_MONTH, 15);
		
		StringBuffer paymentInfo = new StringBuffer();
		paymentInfo.append("  <!-- payment information block -->\n");
		paymentInfo.append("  <PmtInf>\n");
		paymentInfo.append("   <PmtInfId>" + msgId + "-1</PmtInfId>\n");
		paymentInfo.append("   <PmtMtd>TRF</PmtMtd>\n");
		paymentInfo.append("   <BtchBookg>false</BtchBookg>\n");
		paymentInfo.append("   <PmtTpInf>\n");
		paymentInfo.append("    <SvcLvl>\n");
		paymentInfo.append("     <Cd>SEPA</Cd>\n");
		paymentInfo.append("    </SvcLvl>\n");
		paymentInfo.append("   </PmtTpInf>\n");

		paymentInfo.append("   <ReqdExctnDt>"  + df.format(payDate.getTime()) + "</ReqdExctnDt>\n");
		paymentInfo.append("   <Dbtr>\n");
		paymentInfo.append("   <Nm>Evolution GmbH</Nm>\n");
		paymentInfo.append("   </Dbtr>\n");
		paymentInfo.append("   <DbtrAcct>\n");
		paymentInfo.append("    <Id>\n");
		paymentInfo.append("     <IBAN>" + iban + "</IBAN>\n");
		paymentInfo.append("    </Id>\n");
		paymentInfo.append("    <Ccy>EUR</Ccy>\n");
		paymentInfo.append("   </DbtrAcct>\n");
		paymentInfo.append("   <DbtrAgt>\n");
		paymentInfo.append("    <FinInstnId>\n");
		paymentInfo.append("     <BIC>" + bic + "</BIC>\n");
		paymentInfo.append("    </FinInstnId>\n");
		paymentInfo.append("   </DbtrAgt>\n");
		paymentInfo.append("   <ChrgBr>SLEV</ChrgBr>\n");

		paymentInfo.append("   <!-- credit transfer transaction information -->\n");
		
		int numTransactions = 0;
		for (Iterator it = environment.values().iterator(); it.hasNext(); ) {
			DistributorValue dv = (DistributorValue) it.next();
		
			// only deal with german orders with type
			if (dv.getId().toString().startsWith(prefix)) {
				Map vals = new Hashtable();
				
			    double netto = Math.round(dv.getNettoBonusToPay() * 100) * 0.01;
				double val = netto + (vat ? Math.round(netto * dv.getVATFactor()) * 0.01 : 0);

		      	if (val > 0) {
		      		BankAccountValue ba = dv.getBankAccountValue(); 
		      		if (dv.getHasOpenOrders()) {
		      			distributorsWithOpenOrders.add(dv);
		      		} else if (!dv.getTransferBonus()) {
		      			distributorsWithPayingNotAllowed.add(dv);
		      		} else if (dv.getWithoutFirstOrder()) {
		      			distributorsWithoutFirstOrder.add(dv);
		      		} else if (ba == null || isEmpty(ba.getIban())/* || isEmpty(ba.getBic())*/) {
		      			distributorsWithError.add(dv);
		      		} else {
			      		distributorsWithBonus.add(dv);

						numTransactions++;
						sum += val;
						
						paymentInfo.append("   <CdtTrfTxInf>\n");
						paymentInfo.append("    <PmtId>\n");
						paymentInfo.append("     <EndToEndId>" + msgId + "-1-" + String.valueOf(10000 + numTransactions).substring(1) + "</EndToEndId>\n");
						paymentInfo.append("    </PmtId>\n");
						paymentInfo.append("    <Amt>\n");
						paymentInfo.append("     <InstdAmt Ccy=\"EUR\">" + nf.format(val) + "</InstdAmt>\n");
						paymentInfo.append("    </Amt>\n");
						paymentInfo.append("    <CdtrAgt>\n");
						paymentInfo.append("     <FinInstnId>\n");
						paymentInfo.append("      <BIC>" + (isEmpty(ba.getBic()) ? "NOTAVAIL" : ba.getBic()) + "</BIC>\n");
						paymentInfo.append("     </FinInstnId>\n");
						paymentInfo.append("    </CdtrAgt>\n");
						paymentInfo.append("    <Cdtr>\n");
						paymentInfo.append("     <Nm>" + format(dv.getName()) + "</Nm>\n");
						paymentInfo.append("    </Cdtr>\n");
						paymentInfo.append("    <CdtrAcct>\n");
						paymentInfo.append("     <Id>\n");
						paymentInfo.append("      <IBAN>" + ba.getIban() + "</IBAN>\n");
						paymentInfo.append("     </Id>\n");
						paymentInfo.append("    </CdtrAcct>\n");
						paymentInfo.append("    <Purp>\n");
						paymentInfo.append("     <Cd>GDDS</Cd>\n");
						paymentInfo.append("    </Purp>\n");
						paymentInfo.append("    <RmtInf>\n");
						paymentInfo.append("     <Ustrd>Monthly Bonus</Ustrd>\n");
						paymentInfo.append("    </RmtInf>\n");
						paymentInfo.append("   </CdtTrfTxInf>\n");
					}
		      	}
			}
		}
		
		paymentInfo.append(" </PmtInf>\n");
		
		if (sum <= 0) return null;
		
		StringBuffer ret = new StringBuffer();
		
		ret.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
		ret.append("<Document xmlns=\"urn:iso:std:iso:20022:tech:xsd:pain.001.001.03\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n");
		ret.append(" <CstmrCdtTrfInitn>\n");
		ret.append("  <GrpHdr>\n");
		ret.append("   <MsgId>" + msgId + "</MsgId>\n");
		ret.append("    <CreDtTm>" + df3.format(date) + "</CreDtTm>\n");
		ret.append("    <NbOfTxs>" + numTransactions + "</NbOfTxs>\n");
		ret.append("    <CtrlSum>" + nf.format(sum) + "</CtrlSum>\n");
		ret.append("    <InitgPty>\n");
		ret.append("     <Nm>Evolution Gmbh</Nm>\n");
		ret.append("    </InitgPty>\n");
		ret.append("   </GrpHdr>\n");

		ret.append(paymentInfo);

		ret.append(" </CstmrCdtTrfInitn>\n");
		ret.append("</Document>\n");
		 	
		return ret.toString();
	}
	
	/*
	public static void main(String[] args) {
		SEPA sepa = new SEPA("HYVEDEMMXXX", "DE07700202700665876594");
		DistributorValue dv1 = new DistributorValue(new Integer(490000001), null, null, null, null, "name", null, null, false, new BankAccountValue("bank", "bankCode", "accountNo", "bic1", "iban1", new Date(), false), 0, null, true, true, null, 0);
		dv1.setNettoBonusToPay(100);
		DistributorValue dv2 = new DistributorValue(new Integer(490000002), null, null, null, null, "name", null, null, false, new BankAccountValue("bank", "bankCode", "accountNo", "bic2", "iban2", new Date(), false), 0, null, true, true, null, 0);
		dv2.setNettoBonusToPay(200);
		HashMap environment = new HashMap();
		environment.put(dv1.getId(), dv1);
		environment.put(dv2.getId(), dv2);
		System.out.println(sepa.generate(new Date(), "49", true, new HashSet(), new HashSet(), new HashSet(), new HashSet(), new HashSet(), environment));
	}
	*/
}
