package be.kzen.FacadeService;

import be.kzen.FacadeService.Logging.LogError;
import be.kzen.FacadeService.Logging.LoggingAttributes;
import be.kzen.FacadeService.recording.PatientRec;
import be.kzen.FacadeService.recording.PatientRecHistory;
import be.kzen.config.ClientProperties;
import be.kzen.config.WhoAmI;
import be.kzen.ws.Adres;
import be.kzen.ws.Patient;
import org.apache.axis.MessageContext;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.components.uuid.UUIDGen;
import org.apache.axis.components.uuid.UUIDGenFactory;
import org.apache.axis.encoding.XMLType;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.apache.sandesha.Constants;
import org.apache.sandesha.RMReport;
import org.apache.sandesha.SandeshaContext;

import javax.xml.namespace.QName;
import javax.xml.rpc.ParameterMode;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;

/**
 * controller class to call on the clients-webservices
 */
public class Controller {

    private static final String NS = "http://www.kzen.be/stage2006/logs";

    private Collection lijst;
    private String defaultServerPort;
    private String defaultClientPort;
    private String targetURL;
    private String localIp;

    private static Logger log = Logger.getLogger(Controller.class);

    /**
     * constuctor
     */
    public Controller() {
        log.addAppender(new ConsoleAppender(new SimpleLayout()));
        log.setLevel(Level.ALL);
        log.info("FacadeService initialized");
    }

    /**
     * run method of the controller, can be used as webservice
     *
     * @return String transactionID
     */
    public String run() {

        log.debug("FacadeService - run invoked");

        String transactionID = getUniekID();

        Collection pl = new LinkedList();
        pl = getPatient(transactionID, pl);

        log.debug("WebServiceGetPatient - getPatient completed");

        if (pl == null) {
            LoggingAttributes.setErrorOccured(true);
            LogError.logError();
        } else if (pl.size() == 0) {
            LoggingAttributes.setErrorMessage("There was no data available for reading");
            LoggingAttributes.setErrorOccured(true);
            LogError.logError();
            return null;
        } else {
            checkHistory(pl);
            if (!LoggingAttributes.isErrorOccured()) {
                if (pl.size() == 0) {
                    LoggingAttributes.setErrorMessage("There was no new data found to be transfered");
                    LoggingAttributes.setTransactionID(transactionID);
                    LoggingAttributes.setErrorOccured(true);
                    LogError.logError();
                    return transactionID;
                }
            }

            addPatient(transactionID, pl);

            log.debug("WebServiceInsertPatient - insertPatient completed");

            if (LoggingAttributes.isErrorOccured()) LogError.logError();
            else makeTransactionLog(pl, transactionID);

            return transactionID;
        }
        return null;
    }

    /**
     * the addPatient invoke method
     *
     * @param transactionID
     * @param pl
     */
    private void addPatient(String transactionID, Collection pl) {
        Call call = null;
        MessageContext mc = null;
        SandeshaContext ctx = null;
        try {
            //init for webservice connection
            Service service = new Service();
            call = (Call) service.createCall();

            //configuration for the bean
            QName qn = new QName("urn:BeanService", "Patient");
            QName qn2 = new QName("urn:BeanService", "Adres");
            call.registerTypeMapping(Patient.class, qn,
                    new org.apache.axis.encoding.ser.BeanSerializerFactory(Patient.class, qn),
                    new org.apache.axis.encoding.ser.BeanDeserializerFactory(Patient.class, qn));
            call.registerTypeMapping(Adres.class, qn2,
                    new org.apache.axis.encoding.ser.BeanSerializerFactory(Adres.class, qn2),
                    new org.apache.axis.encoding.ser.BeanDeserializerFactory(Adres.class, qn2));

            targetURL = "http://10.0.0.22" + ":" +
                    defaultServerPort + "/axis/services/WebServiceInsertPatient";

            //sandesha context
            ctx = new SandeshaContext();
            ctx.setAcksToURL("http://" + localIp + ":" + defaultClientPort + "/axis/services/RMService");
            ctx.setReplyToURL("http://" + localIp + ":" + defaultClientPort + "/axis/services/RMService");

            ctx.initCall(call, targetURL, "urn:wsrm:addPatient", Constants.ClientProperties.IN_OUT);

            call.setOperationName(new QName("urn:addPatient", "addPatient"));
            call.addParameter("transactionID", XMLType.SOAP_STRING, ParameterMode.IN);
            call.addParameter("patientenLijst", XMLType.SOAP_VECTOR, ParameterMode.IN);
            call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING);
            ctx.setLastMessage(call);

            // addPatient
            Iterator patientenLijstIterator = pl.iterator();
            Vector patientenLijstVector = new Vector();
            while (patientenLijstIterator.hasNext()) {
                patientenLijstVector.add(patientenLijstIterator.next());
            }

            call.invoke(new Object[]{transactionID, patientenLijstVector});
            //call.invoke(new Object[]{patientenLijstVector});

            RMReport report = ctx.endSequence();

            /*
            if (report != null) {
                System.out.println("******************************************************");
                System.out.println("\nPrinting RM Report:");
                System.out.println("Is all messages acked     - " + report.isAllAcked());
                System.out.println("No of response messages   - " + report.getNumberOfReturnMessages());
                System.out.println("******************************************************");
            }
            */

        } catch (Exception e) {
            String operation = "urn:wsrm:addPatient";
            LoggingAttributes.setErrorMessage(e.toString());
            LoggingAttributes.setOperation(operation);
            LoggingAttributes.setTransactionID(transactionID);
            LoggingAttributes.setErrorOccured(true);
        }
    }

    /**
     * the getPatient invoke method
     *
     * @param transactionID
     * @param pl
     * @return Collection
     */
    private Collection getPatient(String transactionID, Collection pl) {

        ClientProperties cp = this.setProperties();
        localIp = WhoAmI.getLocalIp();
        defaultServerPort = cp.getServerPort();
        defaultClientPort = cp.getClientPort();
        //targetURL = "http://" + cp.getTargetGetIp() + ":" +
        //defaultServerPort + "/axis/services/WebServiceGetPatient";
        targetURL = "http://10.0.0.22" + ":" +
                defaultServerPort + "/axis/services/WebServiceGetPatient";

        lijst = pl;

        Call call2 = null;
        SandeshaContext ctx = null;

        try {
            //init for webservice connection
            Service service = new Service();

            call2 = (Call) service.createCall();

            //configuration for the bean
            QName qn = new QName("urn:BeanService", "Patient");
            call2.registerTypeMapping(Patient.class, qn,
                    new org.apache.axis.encoding.ser.BeanSerializerFactory(Patient.class, qn),
                    new org.apache.axis.encoding.ser.BeanDeserializerFactory(Patient.class, qn));
            QName qnAdres = new QName("urn:BeanService", "Adres");
            call2.registerTypeMapping(Adres.class, qnAdres,
                    new org.apache.axis.encoding.ser.BeanSerializerFactory(Adres.class, qnAdres),
                    new org.apache.axis.encoding.ser.BeanDeserializerFactory(Adres.class, qnAdres));

            //sandesha context
            ctx = new SandeshaContext();

            ctx.setAcksToURL("http://" + localIp + ":" + defaultClientPort + "/axis/services/RMService");
            ctx.setReplyToURL("http://" + localIp + ":" + defaultClientPort + "/axis/services/RMService");
            ctx.initCall(call2, targetURL, "urn:wsrm:getPatient", Constants.ClientProperties.IN_OUT);

            //getPatient
            call2.addParameter("transactionID", XMLType.XSD_STRING, ParameterMode.IN);
            call2.setOperationName(new QName("urn:getPatient", "getPatient"));
            call2.setReturnType(new QName("urn:vector"), Vector.class);

            ctx.setLastMessage(call2);

            Vector v = (Vector) call2.invoke(new Object []{transactionID});
            //Vector v = (Vector) call.invoke(new Object []{});

            RMReport report = ctx.endSequence();

            Iterator it = v.iterator();
            while (it.hasNext()) {
                lijst.add(it.next());
            }

            /*
            if (report != null) {
                System.out.println("******************************************************");
                System.out.println("RM Report:");
                System.out.println("Is all messages acked     - " + report.isAllAcked());
                System.out.println("No of response messages   - " + report.getNumberOfReturnMessages());
                System.out.println("******************************************************");
            }
            */

            return lijst;
        }
        catch (Exception e) {
            String operation = "urn:wsrm:getPatient";
            if (e.toString() == "Inactivity Timeout Reached, No Response from the Server") {
                LoggingAttributes.setErrorMessage(e.toString());
                LoggingAttributes.setErrorOccured(true);
            } else {
                LoggingAttributes.setErrorMessage(e.toString());
                LoggingAttributes.setTransactionID(transactionID);
                LoggingAttributes.setOperation(operation);
                LoggingAttributes.setErrorOccured(true);
            }
            return null;
        }
    }

    /**
     * generates a unique identifier wich will be used as transactionID
     *
     * @return String ID
     */
    private String getUniekID() {
        UUIDGen uuidGen = UUIDGenFactory.getUUIDGen(); //Can use this for continuous testing.
        return uuidGen.nextUUID();
    }

    /**
     * registers all patients wich are handeld in this transaction
     *
     * @param c
     * @param transactionID
     */
    private void makeTransactionLog(Collection c, String transactionID) {
        PatientRecHistory prh = new PatientRecHistory("c:/history.xml");
        Iterator it = c.iterator();
        while (it.hasNext()) {
            Patient p = (Patient) it.next();
            prh.addPatientRec(new PatientRec(String.valueOf(p.getId()), transactionID));
        }
        prh.addToHistory();
    }

    /**
     * gets the history of already handled patients
     * deletes those who have already been transfered
     *
     * @param current
     */
    private void checkHistory(Collection current) {
        PatientRecHistory prh = new PatientRecHistory("c:/history.xml");
        Collection history = prh.readHistory();
        Iterator hisIt = history.iterator();
        while (hisIt.hasNext()) {
            {
                PatientRec pr = (PatientRec) hisIt.next();
                Iterator curIt = current.iterator();
                while (curIt.hasNext()) {
                    Patient pt = (Patient) curIt.next();

                    if (pr.getPatientID().equals(String.valueOf(pt.getId()))) {
                        curIt.remove();
                    }
                }

            }
        }
    }

    private ClientProperties setProperties() {
        ClientProperties temp = new ClientProperties();
        temp.loadProperties();
        return temp;
    }

}
