package gov.epa.state.sample.axis;

import gov.epa.cdx.axis.v10.NetworkNodePortType;
import java.rmi.RemoteException;

import gov.epa.cdx.axis.v10.vo.holders.ArrayofDocHolder;

import gov.epa.cdx.axis.v10.vo.*;
import gov.epa.cdx.axis.security.enum.AuthMethod;
import gov.epa.cdx.axis.fault.FaultErrorCode;
import gov.epa.cdx.axis.fault.FaultHandler;

import javax.servlet.http.*;

import java.math.BigInteger;


import gov.epa.cdx.commons.exception.*;
import gov.epa.cdx.commons.log.LogInterface;
import gov.epa.cdx.commons.log.LogFactory;
import gov.epa.state.sample.security.SecurityMgr;
import gov.epa.state.sample.axis.converter.ResultConverter;
import org.apache.axis.transport.http.HTTPConstants;
import org.apache.axis.AxisFault;

/**
 * @author David Dundua
 * @version 1.0
 */

public class DNCSampleImpl implements NetworkNodePortType
{
    private static LogInterface log = LogFactory.getLog(DNCSampleImpl.class.getName());

    public DNCSampleImpl()
    {
    }

    public String authenticate(String uid, String cred, String authMethod) throws RemoteException
    {
        String resultToken = null;
        log.info("[authenticate]: Enter.");
        log.debug("[authenticate]: uid=" + uid + " cred=" + cred + " authMethod=" + authMethod);

        try
        {
            resultToken = SecurityMgr.centralAuth(uid, cred, AuthMethod.password, this.getClientHost());
        }
        catch (Exception ex)
        {
            log.debug("[authenticate]: Authentication Error: UKNOWN_USER error thrown.");
            AxisFault fault = FaultHandler.getFault("The user name or password is invalid.",
                    "Login failure.", FaultErrorCode.UNKNOWN_USER);
            throw fault;

        }

        log.debug("[authenticate]: return=" + resultToken);
        //now lets authenticate the user
        log.info("[authenticate]: Exit.");
        return resultToken;
    }

    public String submit(String authToken, String transactionId, String dataflow, NodeDocument[] documents) throws RemoteException
    {
        log.info("[submit]: Enter.");
        this.validateToken(authToken, this.getClientHost());

        //lets get iterate over document array and see how we can access
        //some of the fields
        for (int counter = 0; counter < documents.length; counter++)
        {
            NodeDocument document = documents[counter];
            //lets get document's Name and type
            String name = document.getName();
            String type = document.getType();

            log.debug("[submit]: Sumbitted document name: " + name + " and type " + type);


            //lets get documents content as a byte array
            byte[] documentContent = document.obtainContentBytes();
            log.debug("[submit]: Sumbitted document contents size is " + documentContent.length + " bytes.");


        }

        log.info("[submit]: Exit.");
        return "Transaction ID from Submit";
    }

    public String getStatus(String authToken, String transid) throws RemoteException
    {
        log.info("[getStatus]: Enter.");
        this.validateToken(authToken, this.getClientHost());

        if ("This is a wrong tansaction".equals(transid))
        {
            //we need to throw fault
            log.info("[getStatus]: Unsupported request. Throwing fault.");
            throw FaultHandler.getFault("Invalid transaction Id.",
                    "Check transaction Id.", FaultErrorCode.TRANSACTION_ID);
        }
        log.info("[getStatus]: Exit.");
        return "RECEIVED";
    }
    public String notify(String authToken, String nodeAddress, String dataflow, NodeDocument[] documents) throws RemoteException
    {
        log.info("[notify]: Enter.");
        this.validateToken(authToken, this.getClientHost());
        log.info("[notify]: Exit.");
        return "Transaction ID from notify";
    }

    public void download(String authToken, String transid, String dataflow, ArrayofDocHolder documents) throws RemoteException
    {
        log.info("[download]: Enter.");
        this.validateToken(authToken, this.getClientHost());

        //this is reference to recieved document request
        NodeDocument[] requestedDocumentsArray = documents.value;

        //here we can erspond to the request with documents in this case we will simply iterate over the requested documents
        //create a new array of documents and repond
        NodeDocument[] responseDocumentsArray = new NodeDocument[requestedDocumentsArray.length];

        for (int counter = 0; counter < requestedDocumentsArray.length; counter++)
        {
            //requested document
            NodeDocument requestedDocument = requestedDocumentsArray[counter];

            //lets get the name and type and insert it into the new array
            NodeDocument responseDocument = new NodeDocument();
            //respond with the same document name as the name of the requested document
            responseDocument.setName(requestedDocument.getName());
            responseDocument.setType(requestedDocument.getType());
            log.debug("[download]: Download requested document name: " + requestedDocument.getName() + " and type " + requestedDocument.getType());

            //lets create a fake byte[] and use it as conent of the response document
            byte[] fakeContent = {111, 111};
            //note the use of particular content type -- this what makes DIME work
            responseDocument.putContent(fakeContent, NodeDocumentContentConverter.CONTENT_TYPE_ATTACHMENT);

            //we are done. Lets insert it into the responseDocumentsArray
            responseDocumentsArray[counter] = responseDocument;
        }

        //last but definetly not least insert this into the document holder
        documents.value = responseDocumentsArray;

        log.info("[download]: Exit.");
    }

    public String query(String authToken, String request, BigInteger rowId, BigInteger maxRows, String[] parameters) throws RemoteException
    {
        log.info("[query]: Enter.");
        this.validateToken(authToken, this.getClientHost());
        String res = null;
        try
        {
            res = ResultConverter.validateWellformness("<records><record>QueryResultRecord1</record><record>QueryResultRecord2</record></records>");
        }
        catch (CDXException ex)
        {
            //this will happen if document is not well formed XML document
            log.debug("[query]: Query Error: document we are trying to send back is not well formed XML document.");
            AxisFault fault = FaultHandler.getFault("Could not create XML correctly..",
                    "Could not create XML document.", FaultErrorCode.INTERNAL_ERROR);
            throw fault;
        }
        log.info("[query]: Exit.");
        return res;

    }
    public String execute(String authToken, String request, String[] parameters) throws RemoteException
    {
        log.info("[execute]: Enter.");
        this.validateToken(authToken, this.getClientHost());
        //here we need to check value of the request.  In real life,
        //we would see if we support this request and return Unsuported fault if we didnt
        //for now we check to see if the value is "This is a wrong request" and throw a fault
        if ("This is a wrong request".equals(request))
        {
            //we need to throw fault
            log.info("[execute]: Unsupported request. Throwing fault.");
            throw FaultHandler.getFault("Invalid request type",
                    "Usupported request request.", FaultErrorCode.FEATURE_UNSUPPORTED);
        }
        log.info("[execute]: Exit.");
        return "Return from Execute";
    }
    public String nodePing(String hello) throws RemoteException
    {
        log.info("[nodePing]: Enter.");
        log.info("[nodePing]: Exit.");
        return "Ready";
    }
    public String[] getServices(String authToken, String serviceType) throws RemoteException
    {
        String[] services = {"Service1", "Service2"};
        log.info("[getServices]: Enter.");
        this.validateToken(authToken, this.getClientHost());
        log.info("[getServices]: Exit.");
        return services;
    }
    public String solicit(String securityToken, String returnURL, String request, String[] parameters) throws RemoteException
    {
        log.info("[solicit]: Enter.");
        this.validateToken(securityToken, this.getClientHost());
        log.info("[solicit]: Exit.");
        return "Transaction ID from Solicit";
    }


    private java.lang.String getClientHost()
    {
        // Get clienthost from the Axis Servlet Request
        org.apache.axis.MessageContext msgContext = org.apache.axis.MessageContext.getCurrentContext();
        HttpServletRequest req = (HttpServletRequest) msgContext.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);
        String clientHost = req.getRemoteAddr();
        log.debug("[getclienthost]: clienthost = " + clientHost);
        return clientHost;
    }

    private boolean validateToken(String authToken, String clientHost) throws AxisFault
    {
        boolean valid = false;
        try
        {
            valid = SecurityMgr.isTokenValid(authToken, clientHost);
        }
        catch (RemoteException ex)
        {
            //something went wrong in connecting to NAAS
            AxisFault fault = FaultHandler.getFault("Unable to validate the security token, because of a probleming connecting to NAAS",
                    "Unable to connect to NAAS.", FaultErrorCode.INTERNAL_ERROR);
            throw fault;

        }

        if (!valid)
        {
            AxisFault fault = FaultHandler.getFault("Invalid or Expired authentication Token Provided.",
                    "Authentication failure.", FaultErrorCode.INVALID_TOKEN);
            throw fault;
        }
        else
            return true;
    }
}