package org.dynamicjava.examples.jdk_log_delegate.internal;

import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;

public class JdkLogForwarder {
	
	public void start() {
		for (String loggerName : getLoggerNames()) {
			Logger logger = Logger.getLogger(loggerName);
			logger.setUseParentHandlers(false);
		}
		
		updateLogHandler();
		
		addLogServiceListener();
	}
	
	public void stop() {
		getBundleContext().removeServiceListener(getLogServiceServiceListener());
		
		for (String loggerName : getLoggerNames()) {
			Logger.getLogger(loggerName).removeHandler(getLastUsedHandler());
		}
		
		if (getLastUsedLogServiceRef() != null) {
			getBundleContext().ungetService(getLastUsedLogServiceRef());
		}
	}
	
	
	protected void updateLogHandler() {
		ServiceReference logServiceRef = getBundleContext().getServiceReference(LOG_SERVICE_CLASS_NAME);
		Handler logHandler = null;
		
		if (logServiceRef != null && logServiceRef == getLastUsedLogServiceRef()) {
			return;
		} if (logServiceRef != null) {
			logHandler = new OsgiLogDelegateHandler((LogService)getBundleContext().getService(logServiceRef));
		} else {
			logHandler = getDefaultHandler();
		}
		
		Handler lastUsedHandler = getLastUsedHandler();
		
		for (String loggerName : getLoggerNames()) {
			Logger logger = Logger.getLogger(loggerName);
			logger.removeHandler(lastUsedHandler);
			logger.addHandler(logHandler);
		}
		
		if (getLastUsedLogServiceRef() != null) {
			getBundleContext().ungetService(getLastUsedLogServiceRef());
		}
		
		setLastUsedLogServiceRef(logServiceRef);
		setLastUsedHandler(logHandler);
	}
	
	protected void addLogServiceListener() {
		try {
			getBundleContext().addServiceListener(getLogServiceServiceListener(),
					String.format("(%s=%s)", Constants.OBJECTCLASS, LOG_SERVICE_CLASS_NAME));
		} catch (InvalidSyntaxException ex) {
			/// This exception should not occur in the first place
			throw new RuntimeException(ex.getMessage());
		}
	}
	
	
	public JdkLogForwarder(BundleContext bundleContext, String[] loggerNames) {
		this(bundleContext, loggerNames, null);
	}
	
	public JdkLogForwarder(BundleContext bundleContext, String[] loggerNames, Handler defaultHandler) {
		this.bundleContext = bundleContext;
		this.loggerNames = loggerNames;
		
		this.defaultHandler = defaultHandler != null ? defaultHandler : new DummyLogHandler();
	}
	
	private final BundleContext bundleContext;
	protected BundleContext getBundleContext() {
		return bundleContext;
	}
	
	private final String[] loggerNames;
	protected String[] getLoggerNames() {
		return loggerNames;
	}
	
	private final Handler defaultHandler;
	protected Handler getDefaultHandler() {
		return defaultHandler;
	}
	
	private final ServiceListener logServiceServiceListener = new LogServiceServiceListener();
	protected ServiceListener getLogServiceServiceListener() {
		return logServiceServiceListener;
	}
	
	private ServiceReference lastUsedLogServiceRef;
	protected ServiceReference getLastUsedLogServiceRef() {
		return lastUsedLogServiceRef;
	}
	protected void setLastUsedLogServiceRef(ServiceReference lastUsedLogServiceRef) {
		this.lastUsedLogServiceRef = lastUsedLogServiceRef;
	}
	
	private Handler lastUsedHandler;
	protected Handler getLastUsedHandler() {
		return lastUsedHandler;
	}
	protected void setLastUsedHandler(Handler lastUsedHandler) {
		this.lastUsedHandler = lastUsedHandler;
	}
	
	
	protected class LogServiceServiceListener implements ServiceListener {
		//@Override
		public void serviceChanged(ServiceEvent serviceEvent) {
			try {
				updateLogHandler();
			} catch (Throwable ex) {
				System.out.println("Error: " + ex.getMessage());
				ex.printStackTrace();
			}
		}
	}
	
	protected class OsgiLogDelegateHandler extends Handler {
		@Override
		public void publish(LogRecord record) {
			if (record.getLevel() == Level.OFF) {
				return;
			}
			
			if (record.getThrown() != null) {
				getLogService().log(getToOsgiLogLevel(record.getLevel()),
						record.getMessage(), record.getThrown());
			} else {
				getLogService().log(getToOsgiLogLevel(record.getLevel()), record.getMessage());
			}
		}
		
		@Override
		public void close() throws SecurityException {
		}
		
		@Override
		public void flush() {
		}
		
		
		protected int getToOsgiLogLevel(Level level) {
			if (level == Level.SEVERE) {
				return LogService.LOG_ERROR;
			} else if (level == Level.WARNING) {
				return LogService.LOG_WARNING;
			} else if (level == Level.INFO || level == Level.CONFIG || level == Level.FINE) {
				return LogService.LOG_INFO;
			} else {
				return LogService.LOG_DEBUG;
			}
		}
		
		
		public OsgiLogDelegateHandler(LogService logService) {
			this.logService = logService;
		}
		
		private final LogService logService;
		protected LogService getLogService() {
			return logService;
		}
	}
	
	protected class DummyLogHandler extends Handler {
		@Override
		public void publish(LogRecord record) {
		}
		
		@Override
		public void close() throws SecurityException {
		}
		
		@Override
		public void flush() {
		}
	}
	
	protected static final String LOG_SERVICE_CLASS_NAME = "org.osgi.service.log.LogService";
	
}

