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

import org.apache.log4j.Logger;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.dependency.IFSOAPDependency;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.dependency.IFSOAPDependencyStatement;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.process.result.IFDigestAgreementConfirmationResult;

import java.util.Vector;

/**
 * 
 */
public class DependencyManager implements IFDependencyManager {

   private static final Logger logger = Logger.getLogger(DependencyManager.class);
   private static final Logger dependencyMonitor = Logger.getLogger("monitor.state.dependency.witness");

   private Vector dependencyChains = null;

   public DependencyManager() {
      dependencyChains = new Vector();
   }

   /**
    * @see IFDependencyManager#checkDependency(de.tu_dresden.diplom.richter_mirko_mat2628335.common.dependency.IFSOAPDependency)
    */
   public boolean checkDependency(IFSOAPDependency dependency) {
      final String fn = "[checkDependency]";
      boolean result = true;
      if (dependency != null) {
         if (logger.isDebugEnabled()) logger.debug(fn + " dependency-object exists");
         IFSOAPDependencyStatement[] dependencyStatements = dependency.getDependencyStatements();
         if (dependencyStatements != null) {
            if (logger.isDebugEnabled()) logger.debug(fn + " dependency-object contains dependency-statements");
            for (int i = 0; i < dependencyStatements.length; i++) {
               IFSOAPDependencyStatement statement = dependencyStatements[i];
               boolean chainResult = false;
               for (int j = 0; j < dependencyChains.size(); j++) {
                  IFDependencyChain chain = (IFDependencyChain) dependencyChains.elementAt(j);
                  if (chain.containsDependencyStatement(statement)) {
                     chainResult = true;
                     break;
                  }
               }
               result = result && chainResult;
               if (result == false) {
                  logger.warn(fn + " dependencyStatement '" + statement + "' could not be met!");
                  break;
               }
            }
         } else {
            if (logger.isInfoEnabled()) logger.info(fn + " dependency-object contains no dependency-statements");
         }
      } else {
         if (logger.isDebugEnabled()) logger.debug(fn + " no dependency-object set");
      }
      return result;
   }

   public void addConfirmedDependency(IFSOAPDependency dependency, IFDigestAgreementConfirmationResult confirmationResult) {
      final String fn = "[addConfirmedDependency]";
      if (logger.isDebugEnabled()) logger.debug(fn + " adding digstAgreementConfimrationResult '" + confirmationResult + "' with dependencies '" + dependency + "'");
      boolean addedAtLeastOnce = false;
      if (confirmationResult != null) {
         if (dependency != null) {
            IFSOAPDependencyStatement[] dependencyStatements = dependency.getDependencyStatements();
            if (dependency.getDependencyStatements() != null) {
               for (int i = 0; i < dependencyStatements.length; i++) {
                  IFSOAPDependencyStatement statement = dependencyStatements[i];
                  Vector chains2Update = getDependencyChains(statement);
                  for (int j = 0; j < chains2Update.size(); j++) {
                     IFDependencyChain chain = (IFDependencyChain) chains2Update.elementAt(j);
                     addedAtLeastOnce = true;
                     if(dependencyMonitor.isDebugEnabled()) logDependencyChainToMonitor(fn, "changing dependency-chain from:", chain);
                     chain.addDependency(dependency, confirmationResult.getReference());
                     if(dependencyMonitor.isDebugEnabled()) logDependencyChainToMonitor(fn, "to:", chain);
                  }
               }
            }
         }
         if (!addedAtLeastOnce) {
            if (logger.isDebugEnabled()) logger.debug(fn + " dependency '" + confirmationResult.getReference() + "' added as root in a new chain!");
            DependencyChain newChain = new DependencyChain(new Dependency(confirmationResult.getReference()));
            addChain(newChain);
         }
      } else {
         logger.error(fn + " confirmationResult is not allowed to be NULL!");
      }
   }

   private void logDependencyChainToMonitor(String fn, String message, IFDependencyChain chain) {
      StringBuffer mon = new StringBuffer(message).append("\n");
      if(chain != null){
         IFDependency rootDep = chain.getRootDependency();
         mon.append("Root: ").append(rootDep.getReference());
      }else{
         mon.append("\tNULL");
      }
      dependencyMonitor.debug(fn + mon);
   }

   public boolean isEqual(IFSOAPDependency first, IFSOAPDependency second) {
      boolean result = true;
      if (first != null && second != null) {
         IFSOAPDependencyStatement[] firstStatements = first.getDependencyStatements();
         IFSOAPDependencyStatement[] secondStatements = second.getDependencyStatements();
         if(firstStatements == null && secondStatements == null){
            result = true;
         }else if (firstStatements == null ^ secondStatements == null) {
            result = false;
         } else {
            for (int i = 0; i < firstStatements.length; i++) {
               IFSOAPDependencyStatement firstStatement = firstStatements[i];
               boolean found = false;
               for (int j = 0; j < secondStatements.length; j++) {
                  IFSOAPDependencyStatement secondStatement = secondStatements[j];
                  if (firstStatement.getDependency().equals(secondStatement.getDependency())) {
                     found = true;
                     break;
                  }
               }
               if (!found) {
                  result = false;
                  break;
               }
            }
         }
      } else if (first == null ^ second == null) {
         result = false;
      }
      return result;
   }

   /**
    * Returns a list of dependencyChain-objects that contain the given statement
    *
    * @param statement
    * @return
    */
   private Vector getDependencyChains(IFSOAPDependencyStatement statement) {
      final String fn = "[getDependencyChains]";
      if (logger.isDebugEnabled()) logger.debug(fn + " looking for dependencyChains according to statement '" + statement + "' ...");
      Vector result = new Vector();
      for (int j = 0; j < dependencyChains.size(); j++) {
         IFDependencyChain chain = (IFDependencyChain) dependencyChains.elementAt(j);
         if (chain.containsDependencyStatement(statement)) {
            result.add(chain);
         }
      }
      if (logger.isDebugEnabled()) logger.debug(fn + " ... found '" + result.size() + "' items");
      return result;
   }

   private void addChain(IFDependencyChain chain) {
      if (chain != null) {
         dependencyChains.add(chain);
      }
   }
}
