/*******************************************************************************
 * Copyright (c) 2010 JVM Monitor project. All rights reserved. 
 * 
 * This code is distributed under the terms of the Eclipse Public License v1.0
 * which is available at http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.jboss.ide.eclipse.as.jmx.integration.jvmmonitor;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;

import javax.management.MBeanServerConnection;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.osgi.util.NLS;
import org.jboss.ide.eclipse.as.jmx.integration.JBossServerConnection;
import org.jboss.tools.jmx.jvmmonitor.core.AbstractJvm;
import org.jboss.tools.jmx.jvmmonitor.core.Activator;
import org.jboss.tools.jmx.jvmmonitor.core.IActiveJvm;
import org.jboss.tools.jmx.jvmmonitor.core.IJvm;
import org.jboss.tools.jmx.jvmmonitor.core.ISWTResourceMonitor;
import org.jboss.tools.jmx.jvmmonitor.core.JvmCoreException;
import org.jboss.tools.jmx.jvmmonitor.core.JvmModel;
import org.jboss.tools.jmx.jvmmonitor.core.JvmModelEvent;
import org.jboss.tools.jmx.jvmmonitor.core.JvmModelEvent.State;
import org.jboss.tools.jmx.jvmmonitor.core.cpu.ICpuProfiler;
import org.jboss.tools.jmx.jvmmonitor.core.mbean.IMBeanServer;
import org.jboss.tools.jmx.jvmmonitor.internal.core.AbstractMBeanServer;
import org.jboss.tools.jmx.jvmmonitor.internal.core.Messages;
import org.jboss.tools.jmx.jvmmonitor.internal.core.SWTResourceMonitor;
import org.jboss.tools.jmx.jvmmonitor.internal.core.Util;
import org.jboss.tools.jmx.jvmmonitor.internal.core.cpu.CpuProfiler;
import org.jboss.tools.jmx.jvmmonitor.ui.JvmMonitorPreferences;

/**
 * The active JVM.
 */
public class JBossActiveJvm extends AbstractJvm implements IActiveJvm {

    /** The state indicating if attach mechanism is supported. */
    private boolean isAttachSupported;

    /** The state indicating if simply connecting is supported. */
    private boolean isConnectSupported;

    /** The state indicating if JVM is running on remote host. */
    private boolean isRemote;

    /** The state indicating if JVM has agent attached. */
    private boolean isAttached;

    /** The MXBean server. */
    private JBossMBeanServer mBeanServer;

    /** The CPU profiler. */
    private ICpuProfiler cpuProfiler;

    /** The SWT resource monitor. */
    private ISWTResourceMonitor swtResourceMonitor;

    
    private JBossServerConnection jmxConnection;
    private IActiveJvm delegateJvm;
    
    public JBossActiveJvm(JBossServerConnection connection, IActiveJvm delegate) throws JvmCoreException {
        super(delegate.getPid(), delegate.getHost());
        
        isRemote = false;
        isConnectSupported = true;
        isAttachSupported = true;
        this.jmxConnection = connection;
        this.delegateJvm = delegate;
        initialize();
        connect();
        saveJvmProperties();
    }

    public void connect() throws JvmCoreException {
    	connect( JvmMonitorPreferences.getJvmUpdatePeriod());
    }
    
    /*
     * @see IActiveJvm#connect(int)
     */
    @Override
    public void connect(int updatePeriod) throws JvmCoreException {
    	connect(updatePeriod, false);
    }


    /*
     * @see IActiveJvm#connect(int, boolean)
     */
	@Override
	public void connect(int updatePeriod, boolean attach)
			throws JvmCoreException {
    	if( !isConnectSupported ) {
      		 throw new IllegalStateException(Messages.connectNotSupportedMsg);
      	}
        mBeanServer.connect(updatePeriod);
        
        if( attach ) 
        	attach(false);
        
        JvmModel.getInstance().fireJvmModelChangeEvent(
                new JvmModelEvent(State.JvmConnected, this));
	}
    
    public void attach() throws JvmCoreException {
    	attach(true);
    }
    
    private void attach(boolean fireEvent) throws JvmCoreException {
        if (!isAttachSupported) {
            throw new IllegalStateException(Messages.attachNotSupportedMsg);
        }

        if( !isAttached ) {
	        if (!isRemote) {
	            JvmModel.getInstance().getAgentLoadHandler().loadAgent(this);
	            isAttached = true;
	        }
	        if (swtResourceMonitor.isSupported()) {
	            swtResourceMonitor.setTracking(true);
	        }
        }
        if( fireEvent ) {
	        JvmModel.getInstance().fireJvmModelChangeEvent(
	                new JvmModelEvent(State.JvmModified, this));
        }
    }
    
    /*
     * @see IActiveJvm#disconnect()
     */
    @Override
    public void disconnect() {
        mBeanServer.dispose();
        try {
            if (swtResourceMonitor.isSupported()) {
                swtResourceMonitor.setTracking(false);
            }
        } catch (JvmCoreException e) {
            // do nothing
        }

        JvmModel.getInstance().fireJvmModelChangeEvent(
                new JvmModelEvent(State.JvmDisconnected, this));
    }
    
    /*
     * @see IActiveJvm#isAttached()
     */
	@Override
	public boolean isAttached() {
		return isAttached;
	}

    /*
     * @see IActiveJvm#isConnected()
     */
    @Override
    public boolean isConnected() {
        return jmxConnection.isConnected();
    }

    /*
     * @see IActiveJvm#isAttachSupported()
     */
    @Override
    public boolean isConnectionSupported() {
        return isAttachSupported;
    }

    /*
     * @see IActiveJvm#getErrorStateMessage()
     */
    @Override
    public String getErrorStateMessage() {
        return null;
    }

    /*
     * @see IActiveJvm#isRemote()
     */
    @Override
    public boolean isRemote() {
        return isRemote;
    }

    /*
     * @see IActiveJvm#getMBeanServer()
     */
    @Override
    public IMBeanServer getMBeanServer() {
        return mBeanServer;
    }

    /*
     * @see IActiveJvm#getCpuProfiler()
     */
    @Override
    public ICpuProfiler getCpuProfiler() {
        return cpuProfiler;
    }

    /*
     * @see IActiveJvm#getSWTResourceMonitor()
     */
    @Override
    public ISWTResourceMonitor getSWTResourceMonitor() {
        return swtResourceMonitor;
    }

    /*
     * @see Object#toString()
     */
    @Override
    public String toString() {
        return getMainClass();
    }

    /**
     * Saves the JVM properties.
     */
    public void saveJvmProperties() {
        IFileStore fileStore;
        try {
            fileStore = Util.getFileStore(IJvm.PROPERTIES_FILE,
                    getPersistenceDirectory());
            if (fileStore.fetchInfo().exists()) {
                return;
            }
        } catch (JvmCoreException e) {
            Activator.log(IStatus.ERROR, Messages.savePropertiesFileFailedMsg,
                    e);
            return;
        }

        Properties props = new Properties();
        OutputStream os = null;
        try {
            os = fileStore.openOutputStream(EFS.NONE, null);

            int pid = getPid();
            int port = getPort();
            String mainClass = getMainClass();

            props.setProperty(IJvm.PID_PROP_KEY, String.valueOf(pid));
            props.setProperty(IJvm.PORT_PROP_KEY, String.valueOf(port));
            if (mainClass != null) {
                props.setProperty(IJvm.MAIN_CLASS_PROP_KEY, mainClass);
            }
            props.setProperty(IJvm.HOST_PROP_KEY, getHost().getName());

            props.storeToXML(os, "JVM Properties"); //$NON-NLS-1$
        } catch (CoreException e) {
            Activator.log(IStatus.ERROR, NLS.bind(
                    Messages.openOutputStreamFailedMsg, fileStore.toURI()
                            .getPath()), e);
        } catch (IOException e) {
            try {
                fileStore.delete(EFS.NONE, null);
            } catch (CoreException e1) {
                // do nothing
            }
            Activator.log(IStatus.ERROR, NLS.bind(
                    Messages.writePropertiesFileFailedMsg, fileStore.toURI()
                            .getPath()), e);
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    // do nothing
                }
            }
        }
    }
    
    /**
     * Initializes the active JVM.
     * 
     */
    private void initialize() {
        mBeanServer = createMBeanServer();
        cpuProfiler = new CpuProfiler(this);
        swtResourceMonitor = new SWTResourceMonitor(this);
    }
    
    
    private static class JBossMBeanServer extends AbstractMBeanServer {
    	private JBossServerConnection jmxConn;
		protected JBossMBeanServer(JBossServerConnection connection, IActiveJvm jvm) {
			super(jvm);
			setJvmReachable(true);
			this.jmxConn = connection;
		}

		@Override
		public MBeanServerConnection getConnection() {
			return jmxConn.getActiveConnection();
		}

		@Override
		protected MBeanServerConnection createMBeanServerConnection()
				throws JvmCoreException {
			return getConnection();
		}
	    public void connect(int updatePeriod) throws JvmCoreException {
	    	super.connect(updatePeriod);
	    }		
	    public void dispose() {
	    	super.dispose();
	    }
    }
    
    private JBossMBeanServer createMBeanServer() {
    	return new JBossMBeanServer(jmxConnection, this);
    }
}
