package cz.cuni.amis.pogamut.base.utils;

import cz.cuni.amis.pogamut.base.exceptions.PogamutException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerFactory;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

/**
 * Trida pro veci konfigurovatelne pres centralni property file, jmx, adresare, rmi ...
 * @author Ik
 */
public class DefaultPogamutPlatform implements PogamutPlatform {

    Logger log = Logger.getLogger("Pogamut platform");
    //public static PogamutPlatform
    static MBeanServer mBeanServer = null;
    static JMXConnectorServer cs = null;
    JMXServiceURL jmxServiceURL = null;

    @Override
    public JMXServiceURL getMBeanServerURL() throws PogamutException {
        try {
            String hostNameProp = getProperty("pogamut.jmx.server.address");
            String hostName = hostNameProp != null ? hostNameProp : InetAddress.getLocalHost().getHostName();
            return new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + hostName + ":" + getRMIPort() + "/server");
        } catch (Exception ex) {
            throw new PogamutException("Error creating the JMX service URL.", ex, this);
        }
    }

    protected int getRMIPort() {
        return Integer.parseInt(getProperty("pogamut.jmx.server.rmi.port"));
    }

    /** 
     * Returns MBeans server for the Pogamut Platform. All MBeans connected from
     * the platform should be registered in this server. There is also default 
     * RMI connector for this server running on service:jmx:rmi:///jndi/rmi://localhost:9999/server
     * @return
     * @throws cz.cuni.amis.pogamut.base.exceptions.PogamutException
     */
    public MBeanServer getMBeanServer() throws PogamutException {
        try {

            if (mBeanServer == null) {
                //start a RMI registry
                LocateRegistry.createRegistry(getRMIPort());
                mBeanServer = MBeanServerFactory.createMBeanServer();

                // also create connector for this server, server without connector
                // would be unreachable outside this JVM
                cs = JMXConnectorServerFactory.newJMXConnectorServer(getMBeanServerURL(), null, mBeanServer);
                cs.start();

            }
            return mBeanServer;
        } catch (Exception ex) {
            throw new PogamutException("Error during JMX initialization.", ex);
        }
    }

    public void close() throws PogamutException {
        try {
            cs.stop();
        } catch (IOException ex) {
            throw new PogamutException("Exception during platform shutdown.", ex);
        }
    }
    /** Properties loaded from the platform property file. */
    Properties platformProperties = null;

    /**
     * @see DefaultPogamutPlatform.getProperty(String)
     * @param key
     * @param def
     * @return
     */
    public String getProperty(String key, String def) {
        try {
            String val = getProperty(key);
            return val;
        } catch (PropertyNotFoundException ex) {
            return def;
        }
    }

    /**
     * Returns property value. The search order for finding the value is:
     * <ol>
     *  <li>Get property from -D option supplied when running the JVM</li>
     * <li>Load property from PogamutPlatformCustom.properties file in processe's working directory</li>
     * <li>Load property from default platform property file inside Gavialib</li>
     * <li>Get the property from system properties (eg. specified by <code>set MY_PROP=hello</code>)</li>
     * </ol>
     * @param key
     * @return null if no such property was found
     */
    public String getProperty(String key) {
        // 1) first try if the property was specified in commandline
        String value = System.getProperty(key);
        if (value == null) {
            // 2} the property wasn't there, load the property from pogamut platform property file
            if (platformProperties == null) {
                // load default properties stored in the jar file
                Properties platformDefault = new Properties();
                try {
                    platformDefault.load(getClass().getResourceAsStream("/cz/cuni/amis/pogamut/PogamutPlatform.properties"));
                } catch (IOException ex) {
                    throw new RuntimeException("Default platform properties are missing in the jar file.", ex);
                }
                // add properties in the working dir
                platformProperties = new Properties(platformDefault);
                try {
                    platformProperties.load(new FileReader("PogamutPlatformCustom.properties"));
                } catch (FileNotFoundException ex) {
                    log.info("Properties file not found.");
                } catch (IOException ex) {
                    throw new RuntimeException("I/O exception while reading the properties file.", ex);
                }
            }
            value = platformProperties.getProperty(key);
        }
        if (value == null) {
            // 3) last option try to use SYSTEM property
            value = System.getenv(key);
        }

        /*
        if (value == null) {
            throw new PropertyNotFoundException("Property " + key + " was not found.");
        }
        */
        return value;
    }
    protected MBeanServerConnection mbsc = null;

    /**
     * @return Connection to a remote MBeanServer
     * @throws cz.cuni.amis.pogamut.base.exceptions.PogamutException
     */
    public MBeanServerConnection getMBeanServerConnection() throws PogamutException {
        // connect through RMI and get the proxy
        try {
            if (mbsc == null) {
                JMXServiceURL url = new JMXServiceURL(getProperty("pogamut.jmx.client.url"));
                JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
                mbsc = jmxc.getMBeanServerConnection();
            }
            return mbsc;
        } catch (IOException iOException) {
            throw new PogamutException("IO exception occured while creating remote MBeanServer connector.",
                    iOException);
        }
    }
}
