package de.tu_dresden.diplom.richter_mirko_mat2628335.witness.account;

import org.apache.log4j.Logger;

import java.util.Vector;

import de.tu_dresden.diplom.richter_mirko_mat2628335.common.account.Amount;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.account.IFAmount;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.account.IFCurrency;

/**
 * 
 */
public class Account implements IFAccountModifieable{

   private static final Logger logger = Logger.getLogger(Account.class);

   private String identification = null;
   private IFAmount amount = null;

   private IFAmount credit = null;

   private Vector virtualDebits = null;
   private Vector virtualCredits = null;

   public Account(IFAmount amount) {
      virtualDebits = new Vector();
      virtualCredits = new Vector();
      if(!setAmount(amount)){
         setAmount(new Amount(0, IFCurrency.CURRENCY_COUNTER_INST));
      }
      credit = new Amount(0, IFCurrency.CURRENCY_COUNTER_INST);
   }

   public String getIdentification() {
      return identification;
   }

   public void setIdentification(String identification) {
      this.identification = identification;
   }

   public synchronized boolean setCredit(IFAmount credit){
      final String fn = "[setCredit] ";
      boolean result = false;
      if(credit != null){
         if(credit.getAmount() > 0){
            if(getAmountWithoutVirtualDebits().isNegative()){
               if(Math.abs(getAmountWithoutVirtualDebits().getAmount()) < credit.getAmount()){
                  this.credit = credit;
                  result = true;
               }else{
                  logger.warn(fn + "couldn't set credit because it is less than the account is negative (abs())");
               }
            }else{
               result = true;
               this.credit = credit;
            }
         }else{
            logger.error(fn + "credit with negative amount cannot be set!");
         }
      }else{
         if(!getAmountWithoutVirtualDebits().isNegative()){
            this.credit = credit;
            result = true;
         }else{
            logger.warn(fn + "can't remove credit if account is negative");
         }
      }
      return result;
   }

   public IFAmount getCredit() {
      return credit;
   }

   public IFAmount getAmount() {
      return amount;
   }

   public boolean setAmount(IFAmount amount) {
      boolean result = false;
      if(amount != null && !amount.isNegative()){
         result = true;
         this.amount = amount;
      }
      return result;
   }

   public synchronized boolean addVirtualDebit(IFVirtualDebit virtualDebit){
      boolean result = false;
      if(virtualDebit != null && !virtualDebits.contains(virtualDebit) && !virtualDebit.isNegative()){
         IFAmount virtualState = getAmountWithoutVirtualDebits();
         virtualState.increase(credit);
         if(virtualState.getAmount() >= virtualDebit.getAmount()){
            virtualDebits.add(virtualDebit);
            result = true;
         }
      }
      return result;
   }

   public void addVirtualCredit(IFVirtualCredit virtualCredit){
      if(virtualCredit != null && !virtualCredits.contains(virtualCredit) && ! virtualCredit.isNegative()){
         virtualCredits.add(virtualCredit);
      }
   }

   public IFAmount getVirtualCreditSum(){
      Amount result = new Amount(0, IFCurrency.CURRENCY_COUNTER_INST);
      for (int i = 0; i < virtualCredits.size(); i++) {
         IFVirtualCredit virtualCredit = (IFVirtualCredit) virtualCredits.elementAt(i);
         result.increase(virtualCredit);
      }
      return result;
   }

   public IFAmount getAmountWithoutVirtualDebits(){
      IFAmount result = new Amount(getAmount().getAmount(), getAmount().getCurrency());
      for (int i = 0; i < virtualDebits.size(); i++) {
         VirtualDebit debit = (VirtualDebit) virtualDebits.elementAt(i);
         result.decrease(debit);
      }
      return result;
   }

   public void removeVirtualDebit(IFVirtualDebit vd){
      virtualDebits.remove(vd);
   }

   public void performVirtualDebit(IFVirtualDebit vd){
      for (int i = 0; i < virtualDebits.size(); i++) {
         IFVirtualDebit virtualDebit = (IFVirtualDebit) virtualDebits.elementAt(i);
         if(virtualDebit == vd){
            decrease(virtualDebit);
            virtualDebits.remove(i);
            break;
         }
      }
   }

   public IFAmount getVirtualDebitSum(){
      Amount result = new Amount(0, IFCurrency.CURRENCY_COUNTER_INST);
      for (int i = 0; i < virtualDebits.size(); i++) {
         IFVirtualDebit virtualDebit = (IFVirtualDebit) virtualDebits.elementAt(i);
         result.increase(virtualDebit);
      }
      return result;
   }

   public void removeVirtualCredit(IFVirtualCredit vc){
      virtualCredits.remove(vc);
   }

   public void performVirtualCredit(IFVirtualCredit vc){
      for (int i = 0; i < virtualCredits.size(); i++) {
         IFVirtualCredit virtualCredit = (IFVirtualCredit) virtualCredits.elementAt(i);
         if(virtualCredit == vc){
            increase(virtualCredit);
            virtualCredits.remove(i);
            break;
         }
      }
   }

   protected boolean increase(IFAmount amountToIncrease){
      boolean result = false;
      if(amountToIncrease != null && !amountToIncrease.isNegative()){
         result = true;
         amount.increase(amountToIncrease);
      }
      return result;
   }

   protected boolean decrease(IFAmount amountToDecrease){
      boolean result = false;
      if(amountToDecrease != null && !amountToDecrease.isNegative()){
         result = true;
         amount.decrease(amountToDecrease);
      }
      return result;
   }

   public String toString() {
      StringBuffer result = new StringBuffer("[Account::");
      result.append(" identification=").append(getIdentification());
      result.append(" | amount=").append(getAmount());
      result.append("]");
      return result.toString();
   }

}
