/*
 * Copyright 2013 gathanas.
 *
 * 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 eu.dnetlib.espas.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.apache.xml.serialize.DOMSerializer;
import org.apache.xml.serialize.XMLSerializer;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.referencing.factory.web.WebCRSFactory;
import org.geotoolkit.referencing.factory.wkt.DirectPostgisFactory;
import org.geotoolkit.xml.MarshallerPool;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.w3c.dom.Element;
import org.w3c.dom.Node;



/**
 *
 * @author gathanas
 */
public class MetadataHandler {
    private static final Logger _logger = Logger.getLogger(MetadataHandler.class);
    
    protected static final String GMD_NAMESPACE = "http://www.isotc211.org/2005/gmd";
    protected static final String GCO_NAMESPACE = "http://www.isotc211.org/2005/gco";
    protected static final String GML32_NAMESPACE = "http://www.opengis.net/gml/3.2";
    protected static final String XLINK1999_NAMESPACE = "http://www.w3.org/1999/xlink";

    protected static final boolean USE_EWKT = false;
    /** This connection to an associated postgis db instance is used for retrieving the Coordinate System reference codes
     * defined in the spatial_ref_sys table. If codes defined in this table are expected to be used for the instantiation of
     * coordinate systems and related transformations then this connection should be initialized.
     */
    //          initializing the jdbc connection to the postgis db instnace
    protected static String connectionURL = "jdbc:postgresql://dl121.madgik.di.uoa.gr/dnet_espas_v2";
    protected static MarshallerPool metadataMarshPool;
    protected static Connection postgisDBConnection;
    protected static Map<String, CRSMapping> supportedCRSMappings;

    
    static {
        try {
            Class.forName("org.postgresql.Driver");
            postgisDBConnection = DriverManager.getConnection(connectionURL, "dnet", "dnetPwd");
            loadCRSToSRIDMappings();
        } catch (ClassNotFoundException ex) {
            _logger.log(Priority.ERROR, "Postgress driver is not available", ex);
        } catch (SQLException ex) {
            _logger.log(Priority.ERROR, "Could not establish a connection to postgis db instnace. Connection url is:" + connectionURL, ex);
        }

    }
    //////////////////////////////////////////////////////////////////////////
    /** Loads CRS mappings from the postgis database. Information is extracted from the values of the crs_spatial_ref_sys table
     */
    protected static void loadCRSToSRIDMappings() throws SQLException {
        String retrieveCRSMappingsQuery = "select crs, srid,  sridName, inverse_x_y_order, z_scale_factor " + "from crs_spatial_ref_sys";
        ResultSet queryResults = postgisDBConnection.createStatement().executeQuery(retrieveCRSMappingsQuery);
        if (queryResults != null) {
            supportedCRSMappings = new HashMap<String, CRSMapping>();
            while (queryResults.next()) {
                CRSMapping mapping = new CRSMapping(queryResults.getString("crs"), queryResults.getInt("srid"), queryResults.getString("sridname"), queryResults.getBoolean("inverse_x_y_order"), queryResults.getDouble("z_scale_factor"));
                supportedCRSMappings.put(mapping.getEspasCRS(), mapping);
            }
            queryResults.close();
        }
        _logger.debug("CRS to ESPAS SRS mappings have been initialized");
    }

    /** Serializes the provided node elements to string representations that are latter on used for logging purposes.
     */
    protected static String serializeNode(Node extentContent) throws IOException {
        XMLSerializer xmlSerializer = new XMLSerializer();
        ByteArrayOutputStream btArrStream = new ByteArrayOutputStream();
        xmlSerializer.setOutputByteStream(btArrStream);
        xmlSerializer.setNamespaces(true);
        DOMSerializer dmSerializer = xmlSerializer.asDOMSerializer();
        _logger.info("DM serializer is :"+(dmSerializer==null)+" extent is :"+(extentContent==null));
        String strCont="";
        if(dmSerializer!=null && extentContent!=null){
            dmSerializer.serialize((Element) extentContent);
            strCont = new String(btArrStream.toByteArray());
        }
        return strCont;
    }

    /** Internal method used for retrieving an appropriate CRS out of a WTK factory that is
     */
    static CoordinateReferenceSystem getCRSForSRSName(String srsCode) {
        try{
        CRSAuthorityFactory authorityFactory;
        //        Initialize either the Postgis factory if the connection is online or otherwise the WebFactory.
        Hints hintsforAuthorityFactory = new Hints();
        if (postgisDBConnection != null)
            try {
                _logger.info("Initilizing PostgisFactory for CRS codes ");
                authorityFactory = new DirectPostgisFactory(hintsforAuthorityFactory, postgisDBConnection);
                _logger.info("PostgisFactory for CRS codes has been initialized");
            } catch (Exception ex) {
                _logger.info("Postgis CRSFactory could not be initialized", ex);
                authorityFactory = new WebCRSFactory(hintsforAuthorityFactory);
            }
        else
            authorityFactory = new WebCRSFactory(hintsforAuthorityFactory);
        
        /***
         * Mappings of ESPAS defined CRS to standard ones.
         * @note We need to confirm that this mapping is correct.
         **/
        
        _logger.info("SRS Code is :"+srsCode+" is in the list :"+supportedCRSMappings.containsKey(srsCode));
        CoordinateReferenceSystem result = null;
        if (srsCode.equals("http://ontology.espas-fp7.eu/crs/GEI") || srsCode.equals("http://ontology.espas-fp7.eu/crs/J2000spherical"))
            result = authorityFactory.createCoordinateReferenceSystem("EPSG:4326");
        else if (supportedCRSMappings.containsKey(srsCode))
            result = authorityFactory.createCoordinateReferenceSystem(supportedCRSMappings.get(srsCode).getSridCode());
        return result;
    }
        catch(Exception ex){
                _logger.error("Retrieval of Coordinate reference system failed! \n", ex);
                return null;
        }
    }
    
    
}






/** Class used for holding mappings of ESPAS CRS to standard ones used in postgis.
 */
class CRSMapping {
    private String espasCRS;
    private String sridCode;
    private int srid;
    private boolean invertedXY;
    private double zScaleFactor;

    public CRSMapping(String espasCRS, int srid, String sridCode, boolean invertedXY, double zScaleFactor) {
        this.espasCRS = espasCRS;
        this.srid = srid;
        this.sridCode = sridCode;
        this.invertedXY = invertedXY;
        this.zScaleFactor = zScaleFactor;
    }

    public String getEspasCRS() {
        return espasCRS;
    }

    public int getSrid() {
        return srid;
    }
    
    public String getSridCode() {
        return sridCode;
    }

    public boolean isInvertedXY() {
        return invertedXY;
    }

    public double getzScaleFactor() {
        return zScaleFactor;
    }
}