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

import org.apache.log4j.Logger;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.context.IFCoordinationContext;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.context.comm.IFContextCreationRequest;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.context.comm.IFContextCreationRequest;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.handler.data.IFSignatureHandlingInfo;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.digestAgreement.IFDigestAgreementRequest;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.account.AccountManagerFactory;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.account.IFAccount;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.digestAgreement.IFDigestAgreementRequest;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.evidence.WitnessEvidenceManagerFactory;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.evidence.IFBodyEvidence;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.keystore.ApplicationSpecificKeystoreUtility;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.context.ex.ContextInfoInvalidException;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.context.ex.UnknownEntityException;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.context.ex.UnknownEntityException;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.process.result.IFDigestAgreementConfirmationResult;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.process.result.DigestAgreementConfirmationResult;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.process.result.IFContextRemovalResult;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.entity.IFEntity;

import java.util.Map;
import java.util.HashMap;

/**
 * 
 */
public class WitnessContextManager implements IFWitnessContextManager {

   private static final Logger logger = Logger.getLogger(WitnessContextManager.class);
   private static final Logger stateMonitor = Logger.getLogger("monitor.state.context.witness");

   private WitnessContextInfoPool witnessContextInfoPool = null;
   private WitnessContextLifeCycleManager lifeCycleManager = null;

   protected WitnessContextManager() {
      witnessContextInfoPool = new WitnessContextInfoPool();
      lifeCycleManager = new WitnessContextLifeCycleManager(witnessContextInfoPool);
   }

   public synchronized IFWitnessContextInfo registerContext(IFCoordinationContext coordinationContext, IFContextCreationRequest request, IFSignatureHandlingInfo signatureHandlingInfo) throws UnknownEntityException {
      final String fn = "[registerContext] ";
      IFAccount account = AccountManagerFactory.getAccountManager().locateAccount(signatureHandlingInfo.getActingEntity(), request.getAccountIdentification());
      if (account == null) {
         throw new UnknownEntityException("No Account known for the given Entity!");
      }
      WitnessContextInfo result = null;
      try {
         result = new WitnessContextInfo();
         result.setContextObject(coordinationContext);
         result.setSuccessfullCreated(true);
         result.setOwningEntity(signatureHandlingInfo.getActingEntity());
         result.setSourceAccount(account);
         logger.debug(fn + "managing new context: " + result);
         witnessContextInfoPool.addContextInfo(result);
         if (stateMonitor.isDebugEnabled()) {
            StringBuffer mon = new StringBuffer(fn);
            mon.append(witnessContextInfoPool.getManagedContextCount()).append(" registered WitnessContextInfos");
            stateMonitor.debug(mon);
         }
      } catch (ContextInfoInvalidException e) {
         e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      }
      return result;
   }

   public IFWitnessContextInfo getContext(IFCoordinationContext coordinationContext) {
      return lifeCycleManager.getContext(coordinationContext);
   }

   public IFWitnessContextInfo getContext(String ident) {
      return lifeCycleManager.getContext(ident);
   }

   public synchronized IFContextRemovalResult removeContext(IFWitnessContextInfo context2Remove) {
      return lifeCycleManager.removeContext(context2Remove);
   }

   public synchronized boolean registerDigestAgreementRequest(IFDigestAgreementRequest digestAgreementRequest, IFEntity actingEntity, IFBodyEvidence bodyEvidence) {
      final String fn = "[registerDigestAgreementRequest] ";
      if (logger.isDebugEnabled()) logger.debug(fn + "registering digestAgreementRequest '" + digestAgreementRequest + "' by Entity '" + actingEntity + "'");
      boolean result = false;
      try {
         IFWitnessContextInfo witnessContextInfo = getContext(digestAgreementRequest.getContextID());
         if (witnessContextInfo != null && !witnessContextInfo.isInvalid()) {
            IFDigestAgreementProcess process = witnessContextInfo.getDigestAgreementProcess(digestAgreementRequest.getNonce());
            if (process == null) {
               process = new DigestAgreementProcess(witnessContextInfo);
               IFDigestAgreementProcess.IFProcessOperationResult procRes = process.setFirst(digestAgreementRequest, actingEntity, bodyEvidence);
               if (procRes.isSuccess()) {
                  if (logger.isDebugEnabled()) logger.debug(fn + "registering first part of the digestAgreementProcess '" + digestAgreementRequest.getNonce() + "'");
                  witnessContextInfo.addDigestAgreementProcess(digestAgreementRequest.getNonce(), process);
                  result = true;
                  if (stateMonitor.isDebugEnabled()) {
                     StringBuffer mon = new StringBuffer(fn);
                     mon.append("creating new DigestAgreementProcess\n");
                     mon.append("\tWitnessContextInfo#:\t").append(witnessContextInfo.getIdentification()).append("\n");
                     mon.append("\tActing Entity:\t\t").append(ApplicationSpecificKeystoreUtility.getEntityNameByID(actingEntity.getId())).append("\n");
                     mon.append("\tNonce:\t\t\t").append(digestAgreementRequest.getNonce()).append("\n");
                     mon.append("\tDigest:\t\t\t").append(digestAgreementRequest.getDigest()).append("\n");
                     mon.append("\tCommPhase:\t\t");
                     if(digestAgreementRequest.isRequestPhase()){
                        mon.append("REQUEST");
                     }else{
                        mon.append("RESPONSE");
                     }
                     mon.append("\n");
                     stateMonitor.debug(mon);
                  }
               } else {
                  logger.error(fn + "setting first request in newly created DigestAgreementProcess failed!");
               }
            } else {
               IFDigestAgreementProcess.IFProcessOperationResult procRes = process.setSecond(digestAgreementRequest, actingEntity, bodyEvidence);
               if (procRes.isSuccess()) {
                  if (logger.isDebugEnabled()) logger.debug(fn + "registering second part of the digestAgreementProcess '" + digestAgreementRequest.getNonce() + "'");
                  result = true;
                  if (stateMonitor.isDebugEnabled()) {
                     StringBuffer mon = new StringBuffer(fn);
                     mon.append("completing already existing DigestAgreementProcess\n");
                     mon.append("\tWitnessContextInfo#:\t").append(witnessContextInfo.getIdentification()).append("\n");
                     mon.append("\tActing Entity:\t\t").append(ApplicationSpecificKeystoreUtility.getEntityNameByID(actingEntity.getId())).append("\n");
                     mon.append("\tNonce:\t\t\t").append(digestAgreementRequest.getNonce()).append("\n");
                     mon.append("\tDigest:\t\t\t").append(digestAgreementRequest.getDigest()).append("\n");
                     mon.append("\tCommPhase:\t\t");
                     if(digestAgreementRequest.isRequestPhase()){
                        mon.append("REQUEST");
                     }else{
                        mon.append("RESPONSE");
                     }
                     mon.append("\n");
                     stateMonitor.debug(mon);
                  }
               } else {
                  logger.error(fn + "setting second request in already existing DigestAgreementProcess failed!");
               }
            }
         } else {
            if (witnessContextInfo == null) {
               logger.error(fn + " couldn't find a belonging contextInfo - object");
            } else {
               logger.error(fn + " belonging contextInfo - object was invalid!");
            }
         }
      } catch (ContextInfoInvalidException e) {
         e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      }

      return result;
   }

   public synchronized IFDigestAgreementConfirmationResult getConfirmationState(IFDigestAgreementRequest digestAgreementRequest) {
      final String fn = "[getConfirmationState] ";
      IFDigestAgreementConfirmationResult result = null;
      try {
         IFWitnessContextInfo witnessContextInfo = getContext(digestAgreementRequest.getContextID());
         if (witnessContextInfo != null && !witnessContextInfo.isInvalid()) {
            if (logger.isDebugEnabled()) logger.debug(fn + " found a belonging contextInfo - object");
            IFDigestAgreementProcess process = witnessContextInfo.getDigestAgreementProcess(digestAgreementRequest.getNonce());
            if (process != null) {
               if (process.isInvalid()) {
                  logger.warn(fn + "process for the confirmationStateRequest already INVALID!");
               } else {
                  result = process.getConfirmationResult(digestAgreementRequest);
                  if (process.isInvalid()) {
                     // both parties got their response
                     witnessContextInfo.removeDigestAgreementProcess(digestAgreementRequest.getNonce());
                     logger.info(fn + "both parties got their response");
                     if (result.isSuccess()) {
                        WitnessEvidenceManagerFactory.getEvidenceManager().registerSuccessfullPerformedDigestAgreementProcess(witnessContextInfo, process);
                     }
                  }
               }
            } else {
               logger.warn(fn + "no process found!");
               DigestAgreementConfirmationResult temp = new DigestAgreementConfirmationResult();
               temp.setResultCode(IFDigestAgreementConfirmationResult.NO_PROCESS);
               result = temp;
            }

         } else {
            if (witnessContextInfo == null) {
               logger.warn(fn + "belonging contextInfo - object was invalid");
               DigestAgreementConfirmationResult temp = new DigestAgreementConfirmationResult();
               temp.setResultCode(IFDigestAgreementConfirmationResult.CONTEXT_INVALID);
               result = temp;
            } else {
               logger.warn(fn + "couldn't find a belonging contextInfo - object");
               DigestAgreementConfirmationResult temp = new DigestAgreementConfirmationResult();
               temp.setResultCode(IFDigestAgreementConfirmationResult.NO_CONTEXT);
               result = temp;
            }
         }
         if (result.isSuccess()) {
            witnessContextInfo.getDependencyManager().addConfirmedDependency(digestAgreementRequest.getDependency(), result);
         }
      } catch (ContextInfoInvalidException e) {
         e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      }
      return result;
   }

   public String toString() {
      StringBuffer result = new StringBuffer("<WitnessContextManager>\n");
      result.append(witnessContextInfoPool.toString());
      result.append(lifeCycleManager.toString());
      result.append("\n</WitnessContextManager>");
      return result.toString();
   }
}
