/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package eu.espas.carbon.pip;

import eu.espasFp7.schemas.policy.AttributeListDocument;
import eu.espasFp7.schemas.policy.AttributeType;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlException;
import org.wso2.carbon.identity.entitlement.pip.AbstractPIPAttributeFinder;
import org.xml.sax.SAXException;

/** This is the default attribute finder introduced in ESPAS to support the retrieval of information that will be used in the specification of the 
 * ESPAS XACML policies. The supported attribute list is customized to the ESPAS needs but can be extended in the future with additional ones.
 *
 * @author gathanas
 */

public class EspasAttributeFinder extends AbstractPIPAttributeFinder {
    private static final Logger _logger = Logger.getLogger(EspasAttributeFinder.class);
    
    private static final String CONF_FILE = System.getProperty("carbon.config.dir.path")+"/AttributeConfigurationList.xml";
    private static final String DB_DRIVER = "org.postgresql.Driver";

    private static final String DB_URL_PROPERTY = "espas.db.url";
    private static final String DB_USER_PROPERTY = "espas.db.user";
    private static final String DB_PASS_PROPERTY = "espas.db.passwd";

    private static String DB_URL;
    private static String DB_USER;
    private static String DB_PASS;
    
    private Map<String, AttributeQuery> supportedAttributes = new HashMap<String, AttributeQuery>();
    private static Connection espasDBConnection;

    /** Initializes the ESPAS AttributeFinder using provided properties for connecting to the ESPAS db 
     * and the XML configuration file <code>AttributeQueryList.xml</code> for retrieving attribute details.
     */
    
    public void init(Properties properties) throws Exception {
        _logger.debug("Initializing db connection properties for ESPAS donain attributes");
        DB_URL = properties.getProperty(DB_URL_PROPERTY);
        DB_USER = properties.getProperty(DB_USER_PROPERTY);
        DB_PASS = properties.getProperty(DB_PASS_PROPERTY);
        Class.forName("org.postgresql.Driver");

        prepareDBConnection();
       
        this.initAttributesMap();
    }

    private void prepareDBConnection() throws ClassNotFoundException, SQLException{
        if(espasDBConnection==null || espasDBConnection.isClosed())
            espasDBConnection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
    }
    
    public Set<String> getAttributeValues(String subjectId, String resourceId, String actionId,String environmentId, String attributeId, URI issuer) throws Exception {
        prepareDBConnection();
        Set<String> attributeValues = new HashSet<String>();
        AttributeQuery attrQuery = supportedAttributes.get(attributeId);
        if (attrQuery != null) {
            String query = attrQuery.query;
            for (String param : attrQuery.parameters)
                if (param.equalsIgnoreCase("userId"))
                    query= query.replace("$"+param+"$", "'"+subjectId+"'");
                else if (param.equalsIgnoreCase("resourceId"))
                    query=query.replace("$"+param+"$", "'"+resourceId+"'");
                else if (param.equalsIgnoreCase("actionId"))
                    query=query.replace("$"+param+"$", "'"+actionId+"'");
                else if (param.equalsIgnoreCase("environmentId"))
                    query=query.replace("$"+param+"$", "'"+environmentId+"'");
                else if (param.equalsIgnoreCase("attributeId"))
                    query=query.replace("$"+param+"$", "'"+attributeId+"'");
                else if (param.equalsIgnoreCase("issuer"))
                    query=query.replace("$"+param+"$", "'"+issuer.toString()+"'");
                else
                    continue;

            _logger.debug("Query to be executed is:"+query);
            ResultSet result = espasDBConnection.createStatement().executeQuery(query);
            
            if(result !=null)
                while(result.next()){
                    attributeValues.add(result.getString(1));
                }
            result.close();
        }

        _logger.debug("Returned results are #:"+attributeValues.size());
        return attributeValues;
    }
    
    @Override
    public String getModuleName() {
        return "ESPAS Attribute Finder";
    }


    
    @Override
    public Set<String> getSupportedAttributes() {
        return supportedAttributes.keySet();
    }
    
    
///////////////////////////////////////////////////////////////////////////
//    internal methods used for performing private operations
    
private void initAttributesMap() throws ParserConfigurationException, SAXException, IOException, XmlException{
        _logger.info("Retrieving configuration from :"+CONF_FILE);
        InputStream configurationFileStrm = new FileInputStream(CONF_FILE);
        _logger.info("Input stream to configuration file is: "+configurationFileStrm==null?"null":"not null");
        AttributeListDocument attributeListDoc  = AttributeListDocument.Factory.parse(configurationFileStrm);
//        init query and add it to the hashmap
        
        for(int i=0; i<attributeListDoc.getAttributeList().sizeOfAttributeArray();i++){
            AttributeType attribute = attributeListDoc.getAttributeList().getAttributeArray(i);
            AttributeQuery query = new AttributeQuery(attribute.getQuery());
            for(String param: attribute.getParameters().getParameterArray())
                query.addParameter(param);
            
            this.supportedAttributes.put(attribute.getName(), query);
            _logger.debug("Added query:"+query.query+" with params size #"+query.parameters.size() );
        }
        
    }    

    @Override
    public Set<String> getAttributeValues(String string, String string1, String string2, String string3, String string4, String string5) throws Exception {
        return this.getAttributeValues(string, string1, string2, string3, string4, (string5!=null?URI.create(string5):null));
    }


 private class AttributeQuery {
     List<String> parameters = new LinkedList<String>();
     String query;
     
        public AttributeQuery(String query) {
            this.query = query;
        }
        
        public void addParameter(String param){
            parameters.add(param);
        }
  }

}
