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

import java.util.Map;
import java.util.logging.Level;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;

import cz.cuni.amis.pogamut.base.utils.logging.ILogCategories;
import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;

/**
 * JMX decorator for ILogCategories. Every new log category will implicitly have 
 * handler with JMXLogPublisher attached.
 * 
 * @author Jimmy
 */

public class JMXLogCategories implements ILogCategories, JMXLogCategoriesMBean {
	/**
	 * Type (name) of the log categories in the JMX Server (must be combined with the domain).
	 */
	public static final String JMX_NAME = "logCategories";	
	
        /**
         * 
         * @param jmxDomain
         * @param path strucured path to this MBean used as hierarchical type
         * @return
         */
	public static String getJMXLogCategoriesName(String jmxDomain, String path) {
            if(!path.isEmpty()) {
                return jmxDomain + ":type=" + path + ".logging,name=" + JMX_NAME;
            } else {
                return jmxDomain + ":type=logging,name=" + JMX_NAME;
            }
	}
	
	/**
	 * Wrapped categories we're decorating.
	 */
	protected ILogCategories logCategories;
	
	/**
	 * MBean server for the log categories.
	 */
	protected MBeanServer mBeanServer;
	
	/**
	 * Domain where to attach the log categories.
	 */
	protected String jmxDomain;

        protected String path = null;
        
	/**
	 * JMXLogCategories differs from LogCategories by implicit handler with JMXLogPublisher in every 
	 * log category it produces.
	 * <p><p>
	 * Can't be instantiated twice for one (JMX Domain,mBeanServer)!
	 * 
	 * @param mBeanServer
	 * @param jmxDomain
         * @param path hierarchical type of this MBean, should be used to stress composition
	 * @throws InstanceAlreadyExistsException raised if instantiated twice for one jmx domain
	 * @throws MBeanRegistrationException
	 * @throws NotCompliantMBeanException
	 * @throws MalformedObjectNameException
	 * @throws NullPointerException
	 */
	public JMXLogCategories(ILogCategories logCategories, MBeanServer mBeanServer, String jmxDomain, String path) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, MalformedObjectNameException, NullPointerException {
		this.logCategories = logCategories;
		this.mBeanServer = mBeanServer;
		this.jmxDomain = jmxDomain;
		this.path = path;
                mBeanServer.registerMBean(this, new ObjectName(getJMXName()));
		for (LogCategory category : getCategories().values()) {
			category.newHandler(newJMXLogPublisher(category.getName()));
		}		
	}
	
	/**
	 * Returns JMX name in the mBeanServer under which the object subscribed itself.
	 * @return
	 */
	public String getJMXName() {
		return JMXLogCategories.getJMXLogCategoriesName(jmxDomain, path);
	}
	
	/**
	 * Returns existing category by the name or adds new one.
	 * <p><p>
	 * Contains handler with JMXLogPublisher attached.
	 * <p><p>
	 * If you wish to add another handler do:
	 * LogCategory myCategory = categories.getCategory("my log"); // create new category
	 * myCategory.newHandler(new LogPublisher.ConsolePublisher()); // add new handler with output to the console
	 * 
	 * @param name
	 * @return
	 */
	public synchronized LogCategory getCategory(String name) {
		if (hasCategory(name)) {
			return logCategories.getCategory(name);
		} else {
			// creating new category
			LogCategory newCategory = logCategories.getCategory(name);
			newCategory.newHandler(newJMXLogPublisher(name));		
			return newCategory;
		}
	}
	
	private JMXLogPublisher newJMXLogPublisher(String name) {
		JMXLogPublisher logPublisher = new JMXLogPublisher(name);
		try {
			mBeanServer.registerMBean(logPublisher, new ObjectName(getJMXCategoryName(name)));
		} catch (Exception e) {
			System.out.println("Can't register JMXLogPublisher with name " + getJMXCategoryName(name) + " stack trace follows...");
			// can't happen... theoretically :-)
			e.printStackTrace();
		}
		return logPublisher;
	}
		
	@Override
	public String getJMXCategoryName(String categoryName) {
		return jmxDomain+":type="+path+".logging,name="+categoryName;
	}

	@Override
	public Map<String, LogCategory> getCategories() {
		return logCategories.getCategories();
	}

	@Override
	public String[] getCategoryNames() {
		return logCategories.getCategoryNames();
	}

	@Override
	public String[] getCategoryNamesSorted() {
		return logCategories.getCategoryNamesSorted();
	}

	@Override
	public boolean hasCategory(String name) {		
		return logCategories.hasCategory(name);
	}

	@Override
	public void setLevel(Level newLevel) {
		logCategories.setLevel(newLevel);
	}

}
