/*
 * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,  
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.switchyard.transform.xslt.internal;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.namespace.QName;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;

import org.switchyard.ServiceDomain;
import org.switchyard.common.type.Classes;
import org.switchyard.config.model.Model;
import org.switchyard.config.model.switchyard.SwitchYardModel;
import org.switchyard.transform.Transformer;
import org.switchyard.transform.config.model.XsltTransformModel;
import org.switchyard.transform.internal.TransformLogger;
import org.switchyard.transform.internal.TransformMessages;
import org.switchyard.transform.internal.TransformerFactory;

/**
 * @author Alejandro Montenegro <a href="mailto:aamonten@gmail.com">aamonten@gmail.com</a>
 */
public final class XsltTransformFactory implements TransformerFactory<XsltTransformModel>{
    
    /**
     * Property used in a domain property to configure max transformer pool size.
     */
    public static final String MAX_POOL_SIZE = "org.switchyard.transform.xslt.maxPoolSize";
    /**
     * Default max size for transformer pool.
     */
    public static final int DEFAULT_MAX_POOL_SIZE = 50;
    
    /**
     * Create a {@link Transformer} instance from the supplied {@link XsltTransformModel}.
     * @param domain ServiceDomain instance.
     * @param model the JSON transformer model. 
     * @return the Transformer instance.
     */
    public Transformer newTransformer(ServiceDomain domain, XsltTransformModel model) {

        String xsltFileUri = model.getXsltFile();
        boolean failOnWarning = model.failOnWarning();
        QName to = model.getTo();
        QName from = model.getFrom();

        if (xsltFileUri == null || xsltFileUri.equals("")) {
            throw TransformMessages.MESSAGES.noXSLFileDefined();
        }

        try {
            InputStream stylesheetStream = Classes.getResourceAsStream(xsltFileUri);

            if (stylesheetStream == null) {
                TransformMessages.MESSAGES.failedToLoadXSLFile(xsltFileUri);
            }
            javax.xml.transform.TransformerFactory tFactory = javax.xml.transform.TransformerFactory.newInstance();
            tFactory.setErrorListener(new XsltTransformFactoryErrorListener(failOnWarning));
            tFactory.setURIResolver(new XsltUriResolver());
            Templates templates = tFactory.newTemplates(new StreamSource(stylesheetStream));
            
            return new XsltTransformer(from, to, templates, failOnWarning, getTransformPoolSize(model));
        } catch (TransformerConfigurationException e) {
            throw TransformMessages.MESSAGES.unexpectedErrorOcurred(e);
        } catch (IOException e) {
            throw TransformMessages.MESSAGES.unableToLocateXSLTFile(model.getXsltFile().toString(), e);
        }
    }
    
    // This method looks for the MAX_POOL_SIZE property within switchyard.xml to 
    // configure the default transformer pool size
    int getTransformPoolSize(XsltTransformModel model) {
        int poolSize = DEFAULT_MAX_POOL_SIZE;
        // attempt to navigate to parent
        Model root = model.getModelRoot();
        if (root instanceof SwitchYardModel) {
            SwitchYardModel syModel = (SwitchYardModel)root;
            if (syModel.getDomain() != null && syModel.getDomain().getProperties() != null) {
                String poolProp = syModel.getDomain().getProperties().getPropertyValue(MAX_POOL_SIZE);
                if (poolProp != null) {
                    poolSize = Integer.parseInt(poolProp);
                }
            }
        }
        return poolSize;
    }
    
    private class XsltTransformFactoryErrorListener implements ErrorListener {
        private boolean _failOnWarning;

        public XsltTransformFactoryErrorListener(boolean failOnWarning) {
             this._failOnWarning = failOnWarning;
        }
        
        @Override
        public void warning(TransformerException ex) throws TransformerException {
            if (_failOnWarning) {
                throw ex;
           } else {
               TransformLogger.ROOT_LOGGER.warningDuringCompilation(ex);
             }
        }

        @Override
        public void error(TransformerException ex) throws TransformerException {
            throw ex;
        }

        @Override
        public void fatalError(TransformerException ex) throws TransformerException {
            throw ex;
        }
    }

}
