/*
 * NIST HL7 Web Service
 * MessageValidationUtils.java Feb 15, 2008
 *
 * This code was produced by the National Institute of Standards and
 * Technology (NIST). See the "nist.disclaimer" file given in the distribution
 * for information on the use and redistribution of this software.
 */

package gov.nist.hl7.ws.messagevalidation;

import gov.nist.hl7.core.MalformedMessageException;
import gov.nist.hl7.core.MalformedProfileException;
import gov.nist.hl7.core.data.DataException;
import gov.nist.hl7.core.message.AbstractMessage;
import gov.nist.hl7.core.message.Er7Message;
import gov.nist.hl7.core.message.XmlMessage;
import gov.nist.hl7.core.profile.Profile;
import gov.nist.hl7.core.validation.ValidationConstants;
import gov.nist.hl7.core.validation.message.Er7MessageValidation;
import gov.nist.hl7.core.validation.message.MessageValidationContext;
import gov.nist.hl7.core.validation.message.MessageValidationResult;
import gov.nist.hl7.core.validation.message.TableProfileDocument;
import gov.nist.hl7.core.validation.message.XmlMessageValidation;
import gov.nist.hl7.core.validation.message.TableProfileDocument.TableProfile;
import gov.nist.hl7.core.validation.message.TableProfileDocument.TableProfile.TableDefinition;
import gov.nist.hl7.ws.JdbcRepositoryDao;
import gov.nist.hl7.ws.Logger;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.xmlbeans.XmlException;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DefaultHandler2;

public class MessageValidationUtils {

    public synchronized MessageValidationContext getMVC(String mvc) throws XmlException {
        //Logger.info("Hl7MessageValidationInterface - User sets a message validation context");
        MessageValidationContext messageValidContext = new MessageValidationContext();
        messageValidContext.load(mvc);
        return messageValidContext;
    }

    public synchronized MessageValidationResult validate(Profile profile, AbstractMessage message,
            MessageValidationContext messageValidContext, TableProfileDocument resource) throws Exception {
        MessageValidationResult mvResult = null;
        if (profile != null && message != null) {
            if (messageValidContext == null) {
                messageValidContext = ValidationConstants.DEFAULT_MESSAGE_VALIDATION_CONTEXT;
            }

           // Logger.info("Hl7MessageValidationInterface - User runs a Validation");

            // Get report
            if (message instanceof XmlMessage) {
                XmlMessageValidation xmlMV = new XmlMessageValidation();
                if (resource ==  null) {
                    mvResult = xmlMV.validate(profile, message, messageValidContext);
                } else {
                    mvResult = xmlMV.validate(profile, message, resource, messageValidContext);
                }
            } else if (message instanceof Er7Message) {
                Er7MessageValidation er7MV = new Er7MessageValidation();
                if (resource ==  null) {
                    mvResult = er7MV.validate(profile, message, messageValidContext);
                } else {
                    mvResult = er7MV.validate(profile, message, resource, messageValidContext);
                }
            }

            if (mvResult == null) {
                throw new Exception("Report is null. Please try again later.");
            }

        }
        return mvResult;
    }

    public synchronized Profile getProfile(String profileStr) throws Exception {
        Profile profile;
        String id = "noid";
        
        if (profileStr.length() > 2 * 1024 * 1024) {
            throw new Exception("File is too large (Max Size=2MB)");
        }

        // Log the file
        //Logger.info("Hl7MessageValidationInterface - User sets a profile: " + id);

        profile = new Profile(id, profileStr);

        SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssZ");

       // profile.getDocument().save(new File("../logs/HL7WS_logs/msgValid_prof_" + id + "_" + sdf.format(new Date()) + ".xml"));

        return profile;
    }

    public synchronized AbstractMessage getMessage(String messageStr) throws IOException, IllegalArgumentException, MalformedMessageException, SQLException, DataException, ParserConfigurationException, SAXException {
        AbstractMessage message = null;

        SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssZ");
        /*File f = new File("../logs/HL7WS_logs/msgValid_msg_" + sdf.format(new Date()) + ".xml");
        PrintWriter out = new PrintWriter(new FileWriter(f));
        out.print(messageStr);
        out.close();*/

       // Logger.info("Hl7MessageValidationInterface - User sets a message");

        // determine if the message is ER7 or XML
        if (AbstractMessage.isER7(messageStr)) {
            message = new Er7Message(messageStr);
        } else {
            // parse with sax: xml file
            InputSource inputSource = new InputSource(new BufferedReader(new StringReader(messageStr)));
            SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
            saxParser.parse(inputSource, new DefaultHandler2());
            message = new XmlMessage(messageStr);
        }
        return message;
    }

    public synchronized Map<String, Object> useHandle(String handleOID, JdbcRepositoryDao rDao) throws SQLException, MalformedProfileException, XmlException {
        String [] bindings = rDao.getBindings(handleOID);
        TableProfileDocument tempResource = TableProfileDocument.Factory.newInstance();
        TableProfile tables = tempResource.addNewTableProfile();

        int blength = bindings.length;
        if (blength > 1) {
            // map to the resource
            //TableType [] tt = new TableType [blength -1];
            // [0] is the profile OID
            for (int i = 1; i < blength; i++) {
                TableProfileDocument curResource = rDao.getResource(bindings[i]);
                // add the current resource to the main object.
                List<TableDefinition> curTableList = curResource.getTableProfile().getTableDefinitionList();
                //.getTables().getTableList();
                tables.setTableDefinitionArray(curTableList.toArray(new TableDefinition[curTableList.size()]));
                //tables.setTableArray(curTableList.toArray(new TableType[curTableList.size()]));
            }
        } else {
            throw new SQLException("This handle cannot be used for the validation.");
        }
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("resource", tempResource);
        map.put("profile", rDao.getProfile(bindings[0]));
        return map;
    }
}
