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

import java.util.logging.Logger;

import cz.cuni.amis.utils.ExceptionToString;

/**
 * Ancestor of all runtime exceptions of the Pogamut platform. It automatically logs the exception to the 
 * platform log of the respective agent were the exception occured.
 * <p><p>
 * There is an exception to the rule (of all exceptions ;-): CommunicationException extends IOException for the sake of simplicity.
 * 
 * @author Jimmy
 */
@SuppressWarnings("serial")
public class PogamutRuntimeException extends RuntimeException {
	
	/**
	 * Whether the exception has been logged to the Platform logger
	 */
	private boolean hasBeenLogged = false;
	
	/**
	 * Object that has thrown this exception.
	 */
	private Object origin;
	
	/**
	 * Constructs a new exception with the specified detail message.
	 * <p><p>
	 * Not logging anything anywhere on its own.
	 * 
	 * @param message
	 * @param origin which object does produced the exception
	 */
	public PogamutRuntimeException(String message, Object origin) {
		super(origin.toString() + ": " + message);
		this.origin = origin;
	}
	
	/**
	 * Constructs a new exception with the specified detail message and cause.
	 * <p><p>
	 * Not logging anything anywhere on its own.
	 * 
	 * @param message
	 * @param cause
	 * @param origin object that thrown the exception
	 */
	public PogamutRuntimeException(String message, Throwable cause, Object origin) {
		super(origin.toString() + ": " + message + " (caused by: " + cause.getMessage() + ")", cause);
		this.origin = origin;
	}

	
	/**
	 * Constructs a new exception with the specified detail message.
	 * <p><p>
	 * Logs the exception via specified Logger.
	 * 
	 * @param message
	 * @param origin which object does produced the exception
	 */
	public PogamutRuntimeException(String message, Logger log, Object origin){
		super(origin.toString() + ": " + message);
		this.origin = origin;
		logException(log);
	}
	
	/**
	 * Constructs a new exception with the specified detail message and cause.
	 * <p><p>
	 * Logs the exception via specified Logger.
	 * 
	 * @param message
	 * @param cause
	 * @param origin object that thrown the exception
	 */
	public PogamutRuntimeException(String message, Throwable cause, Logger log, Object origin) {
		super(origin.toString() + ": " + message + " (caused by: " + cause.getMessage() + ")", cause);
		this.origin = origin;
		logException(log);		
	}
	
	/**
	 * Whether the exception has been logged to any logger.
	 * <p><p>
	 * Note that sometimes it's not possible to log the exception as it is created
	 * due to lack of the logger - therefore we propagate this information through
	 * the stack and as soon as it encounters some Pogamut-platform catch block
	 * it will be logged.
	 * 
	 * @return
	 */
	public boolean isLogged() {
		return hasBeenLogged;
	}
	
	/**
	 * Set whether the exception has been logged to the Platform logger, if not
	 * it will be logged as soon as it encounters some catch block from the Pogamut
	 * platform.
	 * 
	 * @param logged
	 */
	public void setLogged(boolean logged) {
		this.hasBeenLogged = logged;
	}
	
	/**
	 * Returns the object that has thrown the exception.
	 * @return
	 */
	public Object getOrigin() {
		return origin;
	}
	
	/**
	 * Serialize the exception to String.
	 */
	public String toString() {
		return getClass().getSimpleName() + "[" + getMessage() + "]";
	}
	
	/**
	 * Logs the exception to the log + sets isLogged() to true.
	 */
	public synchronized void logException(Logger log) {
		try {
			log.severe(ExceptionToString.process(getMessage(), this));
			this.hasBeenLogged = true;			
		} catch (Exception e) {
			System.err.println("PogamutRuntimeException (and can't log to log '"+log.getName()+"' because \""+e.getMessage()+"\"), exception: " + toString());			
			this.printStackTrace(System.err);
		}			
	}
	
	/**
	 * Logs the exception to the log iff !isLogged().
	 * <p><p>
	 * Logs the exception only once! Successive calls do nothing.
	 */
	public synchronized void logExceptionOnce(Logger log) {
		if (isLogged()) return;
		logException(log);			
	}


}

