package de.tu_dresden.diplom.richter_mirko_mat2628335.clients;

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.MDC;
import org.apache.axis.client.Service;
import org.apache.axis.client.Call;
import org.apache.axis.encoding.ser.BeanSerializerFactory;
import org.apache.axis.encoding.ser.BeanDeserializerFactory;
import org.apache.axis.encoding.XMLType;
import org.apache.axis.MessageContext;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.context.comm.ContextCreationRequest;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.context.comm.ContextCreationResponse;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.context.*;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.account.AccountIdentification;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.Constants;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.services.bookTickets.BookingRequest;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.dependency.SOAPDependency;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.dependency.SOAPDependencyStatement;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.dependency.IFSOAPDependencyStatement;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.evidence.ParticipantCoordinationContextEvidence;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.listener.*;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.listener.event.IFDigestAgreementClosedEvent;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.evidence.ParticipantCoordinationContextEvidence;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.handler.data.IFHandlingInfo;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.handler.BaseHandler;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.services.reserveTickets.ReservationResponse;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.services.reserveTickets.ReservationRequest;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.services.bookTickets.BookingResponse;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.services.bookTickets.BookingRequest;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.services.bookTickets.BookingResponse;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.services.reserveTickets.ReservationRequest;
import de.tu_dresden.diplom.richter_mirko_mat2628335.clients.evidence.ClientEvidenceManagerFactory;
import de.tu_dresden.diplom.richter_mirko_mat2628335.clients.context.IFClientContextInfo;
import de.tu_dresden.diplom.richter_mirko_mat2628335.clients.context.ClientContextManagerFactory;
import de.tu_dresden.diplom.richter_mirko_mat2628335.Configuration;

import javax.xml.rpc.ServiceException;
import javax.xml.rpc.ParameterMode;
import javax.xml.namespace.QName;
import java.rmi.RemoteException;
import java.net.URL;
import java.net.MalformedURLException;

/**
 * A sample client-service that is intended to perform several requests
 * against several services within one single context.
 */
public class ClientService1 {

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

   public static String lastResponseDigestReference = null;

   private static Configuration configuration = null;

   /**
    * Starts the ClientService1 (create context at the witness and than reserve/book tickets) 
    * @param args
    * @throws ServiceException
    * @throws MalformedURLException
    * @throws RemoteException
    */
   public static void main(String[] args) throws ServiceException, MalformedURLException, RemoteException {
      configuration = new Configuration(Configuration.APPLICATION_CLIENT1);
      System.out.println(configuration.getLog4jFile());
      initLog4j(configuration);
      ClientService1 client = new ClientService1();
      ClientContextManagerFactory.getContextManager().addDigestAgreementClosedListener(client.new LastDigestReferenceHelper());
      client.registerDefaultClientListeners();
      IFCountingContext countingContext = client.createContext();
      client.reserveTicket(countingContext);
      client.bookTicket(new SOAPDependencyStatement[]{new SOAPDependencyStatement(lastResponseDigestReference)});
   }

   /**
    * Creates a new context by sending a request to the witness.
    * @return The countingContext created by the witness.
    * @throws ServiceException
    * @throws MalformedURLException
    * @throws RemoteException
    */
   private IFCountingContext createContext() throws ServiceException, MalformedURLException, RemoteException {
      final String fn = "[createContext]";
      MDC.put("logInfo", "Client1Service.createContext");
      String endPoint = Constants.SERVICE_ENDPOINT_WITNESS_CONTEXT_REGISTRATOR;
      System.setProperty("axis.ClientConfigFile", configuration.getModuleUsageCounterHome() + "/WEB-INF/client1_requestContext_config.wsdd");
      Service service = new Service();
      QName qnameRequest = new QName("http://diplom.compago.de", "ContextCreationRequest");
      QName qnameResponse = new QName("http://diplom.compago.de", "ContextCreationResponse");
      Call call = (Call) service.createCall();
      call.setTargetEndpointAddress(new URL(endPoint));
      call.setOperationName("createContext");
      call.addParameter("request", qnameRequest, ParameterMode.IN);
      call.setReturnType(qnameResponse);
      ContextCreationRequest creationRequest = new ContextCreationRequest();
      ContextCreationResponse response = (ContextCreationResponse) call.invoke(new Object[]{creationRequest});
      IFHandlingInfo handlingInfo = BaseHandler.getHandlingInfo();
      //BaseHandler.removeHandlingInfo();
      IFCountingContext countingContext = handlingInfo.getCountingContext();
      if (response != null && response.isSuccess()) {
         IFClientContextInfo ctxInfo = ClientContextManagerFactory.getContextManager().registerContext(countingContext, handlingInfo);
         ClientEvidenceManagerFactory.getClientEvidenceManager().registerSuccessfullCreatedContext(ctxInfo, new ParticipantCoordinationContextEvidence(handlingInfo));
         if (logger.isDebugEnabled()) logger.debug(fn + " successfully aggreed with WITNESS upon a context! ");
      } else {
         logger.warn(fn + " failed on aggreement for a new context with WITNESS!");
      }
      MDC.remove("logInfo");
      return countingContext;
   }

   /**
    * Calls the TicketReservation-Service and tries to reserve a ticket.
    * @param countingContext the CountingContext that was formerly obtained from the witness
    * (means the "surrounding context").
    * @throws ServiceException
    * @throws MalformedURLException
    * @throws RemoteException
    */
   private void reserveTicket(IFCountingContext countingContext) throws ServiceException, MalformedURLException, RemoteException {
      final String fn = "[reserveTicket]";
      MDC.put("logInfo", "Client1Service.reserveTicket");
      String endPoint = Constants.SERVICE_ENDPOINT_TICKET_RESERVATION;
      System.setProperty("axis.ClientConfigFile", configuration.getModuleUsageCounterHome() + "/WEB-INF/client1_requestService_config.wsdd");
      Service service = new Service();
      QName qnameRequest = new QName("http://diplom.compago.de", "ReservationRequest");
      QName qnameResponse = new QName("http://diplom.compago.de", "ReservationResponse");
      Call call = (Call) service.createCall();
      call.setTargetEndpointAddress(new URL(endPoint));
      call.setOperationName("makeReservation");
      call.addParameter("request", qnameRequest, ParameterMode.IN);
      call.setReturnType(qnameResponse);
      StepContext stepContext = new StepContext();
      stepContext.setPerformDigestAgreementInStep(true);
      stepContext.setNonce("" + System.currentTimeMillis());
      stepContext.setCoordinationCtxIdentification(countingContext.getIdentification());
      countingContext.setStepContext(stepContext);
      ClientContextManagerFactory.getContextManager().registerCommunicationStarted(countingContext);
      ReservationResponse response = (ReservationResponse) call.invoke(new Object[]{new ReservationRequest()});
      MDC.remove("logInfo");
   }

   /**
    * Books the ticket within the current active context (the context that housed the reservation too).
    * @param depStatements Dependencies that shall be included into the call (normally the
    * dependency to the formerly made Reservation)
    * @throws ServiceException
    * @throws MalformedURLException
    * @throws RemoteException
    */
   private void bookTicket(IFSOAPDependencyStatement[] depStatements) throws ServiceException, MalformedURLException, RemoteException {
      MDC.put("logInfo", "Client1Service.bookTicket");
      String endPoint = Constants.SERVICE_ENDPOINT_TICKET_BOOKING;
      System.setProperty("axis.ClientConfigFile", configuration.getModuleUsageCounterHome() + "/WEB-INF/client1_requestService_config.wsdd");
      Service service = new Service();
      QName qnameRequest = new QName("http://diplom.compago.de", "BookingRequest");
      QName qnameResponse = new QName("http://diplom.compago.de", "BookingResponse");
      Call call = (Call) service.createCall();
      call.setTargetEndpointAddress(new URL(endPoint));
      call.setOperationName("book");
      call.addParameter("request", qnameRequest, ParameterMode.IN);
      call.setReturnType(qnameResponse);
      StepContext stepContext = new StepContext();
      stepContext.setPerformDigestAgreementInStep(true);
      stepContext.setNonce("" + System.currentTimeMillis());
      IFHandlingInfo handlingInfo = BaseHandler.getHandlingInfo();
      IFCountingContext countingContext = handlingInfo.getCountingContext();
      stepContext.setCoordinationCtxIdentification(countingContext.getIdentification());
      if (depStatements != null) {
         SOAPDependency dep = new SOAPDependency();
         dep.setDependencyStatements(depStatements);
         stepContext.setDependency(dep);
      }
      countingContext.setStepContext(stepContext);
      ClientContextManagerFactory.getContextManager().registerCommunicationStarted(countingContext);
      BookingResponse response = (BookingResponse) call.invoke(new Object[]{new BookingRequest()});
      MDC.remove("logInfo");
   }

   /**
    * registers Standardlisteners for logging (monitoring) at the ContextManager.
    */
   private void registerDefaultClientListeners() {
      ClientContextManagerFactory.getContextManager().addCommunicationInitiatedListener(new LoggingCommunicationInitiatedListener());
      ClientContextManagerFactory.getContextManager().addCommunicationClosedListener(new LoggingCommunicationClosedListener());
      ClientContextManagerFactory.getContextManager().addDigestAgreementInitiatedListener(new LoggingDigestAgreementInitiatedListener());
      ClientContextManagerFactory.getContextManager().addDigestAgreementClosedListener(new LoggingDigestAgreementClosedListener());
   }

   /**
    * Initializes the Logging-Mechanism Log4J.
    */
   private static void initLog4j(Configuration conf) {
      String fn = "[initializeLog4J]";
      String log4jConfFilePath = conf.getLog4jFile();
      System.out.println("ClientService1: Initializing Log4J with file '" + log4jConfFilePath + "'");
      PropertyConfigurator.configure(log4jConfFilePath);
      logger.info(fn + " Logger is up and running!");
   }

   /**
    * Helper to kep track of the reference of the last made request/response-Pair. This is needed
    * to make a referenciation from ticketReservation to ticketBooking.
    */
   private class LastDigestReferenceHelper implements IFDigestAgreementClosedListener {
      public void eventOccurred(IFDigestAgreementClosedEvent ev) {
         final String fn = "[LastDigestReferenceHelper] ";
         if (ev != null) {
            if(logger.isDebugEnabled())logger.debug(fn + "catched DigestAgreementClosedEvent");
            if (ev.getPhase() == IFDigestAgreementClosedEvent.PHASE_RESPONSE) {
               if (logger.isDebugEnabled()) logger.debug(fn + "Setting reference '" + ev.getResponse().getAgreementReference() + "' from Response-Phase DAR");
               lastResponseDigestReference = ev.getResponse().getAgreementReference();
            }else{
               if(logger.isDebugEnabled())logger.debug(fn + "Skipping reference '" + ev.getResponse().getAgreementReference() + "' because it is Request-Phase DAR");
            }
         }else{
            if(logger.isDebugEnabled())logger.debug(fn + "catched Event but was NULL!");
         }
      }
   }


}
