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

package com.etixpert.evolution.app.order;

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.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 String namespace;
	private String creditorId;
	
	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, String aCreditorId, String aNamespace) {
		bic = aBankCode;
		iban = aAccountNumber;
		namespace = aNamespace;
		creditorId = aCreditorId;
	}
	
	public String generate(Date date, boolean first, String prefix, List orders, List environment, Set processedOrders, Set firstSepaTransactions, boolean d1Contract) {
		String msgId = "EVO-" + df2.format(date);
		
		long sum = 0;
		
		StringBuffer paymentInfo = new StringBuffer();
		paymentInfo.append("  <!-- payment information block -->\n");
		paymentInfo.append("  <PmtInf>\n");
		paymentInfo.append("   <PmtInfId>" + msgId + "-1</PmtInfId>\n");
		paymentInfo.append("   <PmtMtd>DD</PmtMtd>\n");
		paymentInfo.append("   <PmtTpInf>\n");
		paymentInfo.append("    <SvcLvl>\n");
		paymentInfo.append("     <Cd>SEPA</Cd>\n");
		paymentInfo.append("    </SvcLvl>\n");
		paymentInfo.append("    <LclInstrm>\n");
		paymentInfo.append("     <Cd>" + (d1Contract ? "COR1" : "CORE") + "</Cd>\n");
		paymentInfo.append("    </LclInstrm>\n");
		paymentInfo.append("    <SeqTp>" + (first ? "FRST" : "RCUR") + "</SeqTp>\n");
		paymentInfo.append("   </PmtTpInf>\n");
		paymentInfo.append("   <ReqdColltnDt>" + df.format(DateUtil.addWorkingDay(date, d1Contract ? 2 : first ? 6 : 3)) + "</ReqdColltnDt>\n");
		paymentInfo.append("   <Cdtr>\n");
		paymentInfo.append("    <Nm>Evolution Gmbh</Nm>\n");
		paymentInfo.append("   </Cdtr>\n");
		paymentInfo.append("   <CdtrAcct>\n");
		paymentInfo.append("    <Id>\n");
		paymentInfo.append("     <IBAN>" + iban + "</IBAN>\n");
		paymentInfo.append("    </Id>\n");
		paymentInfo.append("   </CdtrAcct>\n");
		paymentInfo.append("   <CdtrAgt>\n");
		paymentInfo.append("    <FinInstnId>\n");
		paymentInfo.append("     <BIC>" + bic + "</BIC>\n");
		paymentInfo.append("    </FinInstnId>\n");
		paymentInfo.append("   </CdtrAgt>\n");
		paymentInfo.append("   <CdtrSchmeId>\n");
		paymentInfo.append("    <Id>\n");
		paymentInfo.append("     <PrvtId>\n");
		paymentInfo.append("      <Othr>\n");
		paymentInfo.append("       <Id>" + creditorId + "</Id>\n");
		paymentInfo.append("       <SchmeNm>\n");
		paymentInfo.append("        <Prtry>SEPA</Prtry>\n");
		paymentInfo.append("       </SchmeNm>\n");
		paymentInfo.append("      </Othr>\n");
		paymentInfo.append("     </PrvtId>\n");
		paymentInfo.append("    </Id>\n");
		paymentInfo.append("   </CdtrSchmeId>\n");
		
		paymentInfo.append("   <!-- direct debit transaction information -->\n");

		
		int numTransactions = 0;
		for (Iterator it = environment.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();
				
				for (Iterator it2 = orders.iterator(); it2.hasNext(); ) {
					OrderHeaderValue ov = (OrderHeaderValue) it2.next();
					BankAccountValue ba = ov.getBankAccount();
					
					if (ov.getOrderer().equals(dv) && 
							ba != null && !isEmpty(ba.getIban()) /*&& !isEmpty(ba.getBic())*/ &&
							ov.getPaymentMethodId().equals(OrderHeaderValue.PAYMENT_METHOD_BANK) &&
							ba.getMandateSignDate() != null &&
							ba.isRecurring() == !first) {
						
						Double val = (Double) vals.get(ba);
						if (val == null) {
							val = new Double(0);
							vals.put(ba, val);
						}
						vals.put(ba, new Double(val.doubleValue() + ov.getBrutto()));
						processedOrders.add(ov.getOrderId());
						
						if (first && firstSepaTransactions != null) 
							firstSepaTransactions.add(ba);
					}
				}
			
				for (Iterator it2 = vals.keySet().iterator(); it2.hasNext(); ) {
					BankAccountValue ba = (BankAccountValue) it2.next();
					double val = ((Double) vals.get(ba)).doubleValue();
					
					numTransactions++;
					sum += (long) (val * 100);

					paymentInfo.append("   <DrctDbtTxInf>\n");
					paymentInfo.append("    <PmtId>\n");
					paymentInfo.append("     <EndToEndId>" + msgId + "-1-" + String.valueOf(10000 + numTransactions).substring(1) + "</EndToEndId>\n");
					paymentInfo.append("    </PmtId>\n");
					paymentInfo.append("    <InstdAmt Ccy=\"EUR\">" + nf.format(val) + "</InstdAmt>\n");
					paymentInfo.append("     <DrctDbtTx>\n");
					paymentInfo.append("     <MndtRltdInf>\n");
					paymentInfo.append("      <MndtId>" + dv.getId() + "</MndtId>\n");
					paymentInfo.append("      <DtOfSgntr>" + df.format(ba.getMandateSignDate()) + "</DtOfSgntr>\n");
					paymentInfo.append("     </MndtRltdInf>\n");
					paymentInfo.append("    </DrctDbtTx>\n");
					paymentInfo.append("    <DbtrAgt>\n");
					paymentInfo.append("     <FinInstnId>\n");
					paymentInfo.append("      <BIC>" + (isEmpty(ba.getBic()) ? "NOTAVAIL" : ba.getBic()) + "</BIC>\n");
					paymentInfo.append("     </FinInstnId>\n");
					paymentInfo.append("    </DbtrAgt>\n");
					paymentInfo.append("    <Dbtr>\n");
					paymentInfo.append("     <Nm>" + format(dv.getName()) + "</Nm>\n");
					paymentInfo.append("    </Dbtr>\n");
					paymentInfo.append("    <DbtrAcct>\n");
					paymentInfo.append("     <Id>\n");
					paymentInfo.append("      <IBAN>" + ba.getIban() + "</IBAN>\n");
					paymentInfo.append("     </Id>\n");
					paymentInfo.append("    </DbtrAcct>\n");
					paymentInfo.append("    <RmtInf>\n");
					paymentInfo.append("     <Ustrd>Evolution</Ustrd>\n");
					paymentInfo.append("    </RmtInf>\n");
					paymentInfo.append("   </DrctDbtTxInf>\n");
				}
			}
		}
		
		paymentInfo.append(" </PmtInf>\n");
		
		if (sum <= 0) return null;
		
		StringBuffer ret = new StringBuffer();
		ret.append("");
		ret.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
		ret.append("<Document xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"" + namespace + "\">\n");
		ret.append(" <CstmrDrctDbtInitn>\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("   <InitgPty>\n");
		ret.append("    <Nm>Evolution GmbH</Nm>\n");
		ret.append("   </InitgPty>\n");
		ret.append("  </GrpHdr>\n");
		
		ret.append(paymentInfo);

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