/*
 * RHQ Management Platform
 * Copyright (C) 2005-2011 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package org.rhq.helpers.inventoryserializer;

import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;

import org.apache.log4j.Logger;

import org.rhq.enterprise.server.util.HibernateDetachUtility;
import org.rhq.helpers.inventoryserializer.util.ChildFirstClassLoader;
import org.rhq.test.ObjectCollectionSerializer;

/**
 * 
 *
 * @author Lukas Krejci
 */
public class Main {

    private static final Logger log = Logger.getLogger(Main.class);

    private Main() {

    }

    private static void usage() {
        System.out.println("Usage: inventory-serializer.[sh|bat] -c [-diupjs] <JPQL Query> [more JPQL quries]");
        System.out.println("Required arguments:");
        System.out.println("-c --connection-url : The JDBC connection URL to the database.");
        System.out.println();
        System.out.println("Optional arguments:");
        System.out.println("-d --driver-class : The class of the JDBC driver to use. The driver must be on the classpath. Defaults to org.postgresql.Driver.");
        System.out.println("-i --dialect : The Hibernate dialect to use. Defaults to org.hibernate.dialect.PosgreSQLDialect.");
        System.out.println("-u --username : The username to use when connecting to the database.");
        System.out.println("-p --password : The database password.");
        System.out.println("-j --jars : The comma-separated list of jars containing the entities. If none specified, the current classpath is used.");
        System.out.println("-s --persistence-unit : The persistence unit to execute the queries with. Defaults to 'rhqpu'.");
    }

    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        
        
        LongOpt[] longOptions = new LongOpt[7];

        longOptions[0] = new LongOpt("driver-class", LongOpt.OPTIONAL_ARGUMENT, null, 'd');
        longOptions[1] = new LongOpt("dialect", LongOpt.OPTIONAL_ARGUMENT, null, 'i');
        longOptions[2] = new LongOpt("connection-url", LongOpt.REQUIRED_ARGUMENT, null, 'c');
        longOptions[3] = new LongOpt("username", LongOpt.OPTIONAL_ARGUMENT, null, 'u');
        longOptions[4] = new LongOpt("password", LongOpt.OPTIONAL_ARGUMENT, null, 'p');
        longOptions[5] = new LongOpt("jars", LongOpt.OPTIONAL_ARGUMENT, null, 'j');
        longOptions[6] = new LongOpt("persistence-unit", LongOpt.OPTIONAL_ARGUMENT, null, 's');

        Getopt options = new Getopt("iventory-serializer serializer", args, "d:i:c:u:p:j:s:", longOptions);

        String driverClass = "org.postgresql.Driver";
        String dialect = "org.hibernate.dialect.PostgreSQLDialect";
        String connectionUrl = null;
        String username = null; 
        String password = null;
        String persistenceUnit = "rhqpu";
        List<String> jars = new ArrayList<String>();
        List<String> queries = new ArrayList<String>();

        int option;
        while ((option = options.getopt()) != -1) {
            switch (option) {
            case 'd':
                driverClass = options.getOptarg();
                break;
            case 'i':
                dialect = options.getOptarg();
                break;
            case 'c':
                connectionUrl = options.getOptarg();
                break;
            case 'u':
                username = options.getOptarg();
                break;
            case 'p':
                password = options.getOptarg();
                break;
            case 'j':
                jars.addAll(extractCommaSeparated(options.getOptarg()));
                break;
            case 's':
                persistenceUnit = options.getOptarg();
            }
        }

        for (int i = options.getOptind(); i < args.length; i++) {
            queries.add(args[i]);
        }

        if (driverClass == null || dialect == null || connectionUrl == null || queries.isEmpty()) {
            usage();
            System.exit(1);
        }

         ClassLoader classLoaderToUse = Main.class.getClassLoader();

        if (jars.size() > 0) {
            URL[] jarUrls = getUrls(jars);
            classLoaderToUse = new ChildFirstClassLoader(jarUrls, Main.class.getClassLoader());
        }

        ObjectCollectionSerializer serializer = new ObjectCollectionSerializer();

        EntityManager em = null;
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(classLoaderToUse);
            em = getEntityManager(driverClass, dialect, connectionUrl, username, password, persistenceUnit);
            
            em.getTransaction().begin();
            
            for (String query : queries) {
                log.info("Executing query: " + query);

                Query q = em.createQuery(query);

                @SuppressWarnings("unchecked")
                List<Object> results = q.getResultList();

                for(Object result : results) {
                    HibernateDetachUtility.nullOutUninitializedFields(result, HibernateDetachUtility.SerializationType.SERIALIZATION);
                }
                
                serializer.addObjects(results);
            }
                        
            em.close();            
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }

        serializer.serialize(System.out);        
    }

    /**
     * @param jars
     * @return
     */
    private static URL[] getUrls(List<String> jars) {
        URL[] ret = new URL[jars.size()];

        int idx = 0;
        for (String jar : jars) {
            File f = new File(jar);
            if (!f.exists()) {
                throw new IllegalArgumentException("Could not find the jar '" + jar + "'.");
            }

            try {
                ret[idx++] = f.toURI().toURL();
            } catch (MalformedURLException e) {
                //doesn't happen
            }
        }

        return ret;
    }

    private static EntityManager getEntityManager(String driverClass, String dialect, String connectionUrl,
        String username, String password, String persistenceUnit) {

        HashMap<String, String> overrides = new HashMap<String, String>();

        overrides.put("javax.persistence.jtaDataSource", null);
        overrides.put("javax.persistence.nonJtaDataSource", null);
        overrides.put("hibernate.dialect", dialect);
        overrides.put("hibernate.connection.driver_class", driverClass);
        overrides.put("hibernate.connection.url", connectionUrl);
        overrides.put("hibernate.connection.user", username);
        overrides.put("hibernate.connection.password", password);

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnit, overrides);

        return emf.createEntityManager(overrides);
    }

    private static List<String> extractCommaSeparated(String arg) {
        String[] vals = arg.split("\\s*,\\s*");

        return Arrays.asList(vals);
    }
}
