package q53_util;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * Esta clase actua como loggeadora, de este modo se puede establecer distintos
 * niveles de log con solo cambiar el LOGGING_LEVEL desde DEBUG hasta ERROR,
 * siendo DEBUG el maximo log y ERROR el minimo recomendado <br>
 * <br>
 * <br>
 * NONE : No muestra ninguna traza <br>
 * ERROR: Muestra unicamente mensajes de error y stacktraces <br>
 * LOG : Ademas de lo anterior muestra informacion de transacciones realizadas
 * <br>
 * INFO : Lo anterior con informacion adicional <br>
 * SQL : Lo anterior y ademas las sentencias SQL que se ejecutan <br>
 * DEBUG: Se muestran todos los logs <br>
 * <br>
 * Si se establece a FALSE el valor de "LOG_RECURSIVO" SOLO se lanzara al log el
 * tipo de log seleccionado, util si solo se quieren ver las SQLs que se
 * ejecutan, por ejemplo... <br>
 * <br>
 * Los valores de recursividad y nivel de log se leen del fichero .properties
 * <br>
 * <br>
 */








public class q53Logger {

	//Variables
	// estaticas-------------------------------------------------------
	//private static final int NONE = 0;
	private static final int				ERROR			   = 6;
	private static final int				WARNING			= 5;
	private static final int				SQL				= 4;
	private static final int				INFO			   = 3;
	private static final int				LOG				= 2;
	private static final int				DEBUG			   = 1;

	private q53PrintStreamWrapper				ps				=  new q53PrintStreamWrapper();
	/**
	 * <code>LOGGING_LEVEL</code> Indica el nivel de log que se va a utilizar:
	 * de NONE -> DEBUG
	 */
	private static int						LOGGING_LEVEL	= DEBUG;

	/**
	 * *<code>LOG_RECURSIVO</code> Indica si se van a hacer trazas de todos
	 * los * niveles inferiores al actual o solo del actual <br>* P.ej. Si
	 * LOGGING_LEVEL = 2 y LOG_RECURSIVO = true se imprimiran los * mensajes de
	 * log, info, sql, warning y error. Si LOG_RECURSIVO = false * solo se
	 * imprimen los de LOG
	 */
	private static boolean					LOG_RECURSIVO	= true;
	private static String					USUARIO 		= "";
	private static String					OFICINA 		= "";
	
	private Pattern			patron 			= Pattern.compile("[^.]*.([^.]*.[\\s\\S]*)");
	/**
	 * Nivel de la pila (stacktrace) que se tiene que utilizar para obtener el metodo y la clase que llaman a log
	 */
	private int 							level		=	4;				
	/**
	 * *<code>sdf</code> Formato de fecha: "yyyy-MM-dd'_'HH:mm:ss.SSS" para *
	 * todos los logs
	 */
	public SimpleDateFormat	sdf				= new SimpleDateFormat("yyyyMMdd-HH:mm:ss.SSS");
	
	public q53Logger(){
//		 try{
//	        	ps = new q53PrintStreamWrapper(null);
//	        }
//	        catch (Exception e){
//	        	System.out.println("Q53: ATENCION! No ha sido posible redirigir la salida estandar");
//	        }
	}
	//Funciones
	// estaticas-------------------------------------------------------
	//publicas------------------------------------------------------------------
	/**
	 * Establece el nivel de log de la aplicacion.
	 * 
	 * @param newLevel
	 */
	public void setLevel(int newLevel, boolean recursivo) {
		q53Logger.LOGGING_LEVEL = newLevel;
		q53Logger.LOG_RECURSIVO = recursivo;
		printWarning("Nivel de loggeo establecido a " + newLevel + " recursivo: " + recursivo);
	}	
  
	/**
	 * Imprime un mensaje de error al log
	 * 
	 * @param mensaje
	 */
	public void printError(String mensaje, Exception e) {
		printTraceIfRequired(LOG, "LOG", e.getMessage());
		ps.println("ERROR!!!  caused by: \n" + e.getClass().getName() +" : " + e.getCause() + "\n" + mensaje);
		e.printStackTrace();
	}

	/**
	 * Imprime un mensaje de loggeo al log
	 * 
	 * @param mensaje
	 */
	public void printLog(String mensaje) {
		printTraceIfRequired(LOG, "LOG", mensaje);
	}

	/**
	 * Imprime un mensaje de informacion al log
	 * 
	 * @param mensaje
	 */
	public void printInfo(String mensaje) {
		printTraceIfRequired(INFO, "INFO", mensaje);
	}

	/**
	 * * Imprime una sentencia sql al log * *
	 * 
	 * @param mensaje
	 */
	public void printSql(String mensaje) {
		printTraceIfRequired(SQL, "SQL", mensaje);
	}
	
	/**
	 * Imprime un mensaje al log
	 * 
	 * @param mensaje
	 */
	public void printDebug(String mensaje) {
		printTraceIfRequired(DEBUG, "DEBUG", mensaje);
	}

	/**
	 * Imprime un mensaje de warning al log
	 * 
	 * @param mensaje
	 */
	public void printWarning(String mensaje) {
		printTraceIfRequired(WARNING, "WARNING!!!: ", mensaje);
	}

	public void printVariable(Object obj, String var){
		try{
	        StackTraceElement ste =  new Throwable().fillInStackTrace().getStackTrace()[1];
	        Field fld = Class.forName(ste.getClassName()).getField(var);
	        fld.setAccessible(true);
	        printTraceIfRequired(LOG, "LOG", var + " = |" + fld.get(obj) + "|");
		}catch(Exception e){
			printError("Escribiendo el valor de una variable",e);
		}
    }
	
	//---------------------------------------------------------Getters,  setters
	
	public static String getUsuario(){
		return USUARIO;
	}
	
	public static String getOficina(){
		return OFICINA;
	}
	
	public static void setUsuario(String usuario){
		USUARIO = usuario;
	}
	
	public static void setOficina(String oficina){
		OFICINA = oficina;
	}
	
	//privadas------------------------------------------------------------------
	/**
	 * Saca la traza si se cumplen las condiciones para que se tenga que logear
	 * el nivel de logeo que se le pasa como parametro y lo escribe si es menester
	 * Trata de hacerlo en el printStream ps si este es diferente de null,
	 * si es null lo inicializa a un print
	 * stream con salida en el archivo especificado en la propiedad logger.log.file
	 * de el archivo de properties o bien, si esta establecido a noFile, no existe 
	 * o no se puede abrir, en la salida estandar 
	 * 
	 * @param nivelLog nivel de log que se quiere saber si puede sacar cosas por
	 *            System.out.println los valores posibles son ERROR, LOG, INFO,
	 *            SQL, DEBUG o NONE
	 * @param encabezadoNivelLog nivel de log en string
	 * @param mensaje mensaje que se quiere tracear
	 * @param e Excepcion en caso de que se trate de tracear un error, sino null
	 * @return
	 */
	private void printTraceIfRequired(int nivelLog, String encabezadoNivelLog, String mensaje) {
		if ((LOGGING_LEVEL == nivelLog) || ((LOGGING_LEVEL < nivelLog) && LOG_RECURSIVO)){
			try {
				String metodoactual = getMethodFromStackTrace(level);
				Matcher m = patron.matcher(metodoactual);
				if (m.find())
				    metodoactual =   m.group(1); 
				ps.println("(" + USUARIO + "/" + OFICINA + ")" + getStringDate() + " " +
						metodoactual +  "-->" + encabezadoNivelLog + ":" + "--" + mensaje);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	
	//private void printTraceIfRequired(int nivelLog, String encabezadoNivelLog, String mensaje) {
	//	printTraceIfRequired(nivelLog, encabezadoNivelLog, mensaje, 3);
	//}
	
	private String getMethodFromStackTrace(int level){
		StackTraceElement ste =  new Throwable().fillInStackTrace().getStackTrace()[level];
		return ste.getClassName() + "." + ste.getMethodName() + "(" +ste.getLineNumber() + ")"; 
	}
	/**
	* Establece el printstream que va a utilizarse para realizar el loggeo, sera un archivo especificado por
	* el directorio de 'directorioLog' y con el nombre especificado en 'nombreArchivo' unido a un identificador temporal
	* especificado en 'formatoFechaEnNombreDeArchivo' y con extension 'log'
	* 
	* Si no puede establecerse el printstream se le asigna el valor system.out
	*/
//	private void setPrintStream(){
//		 try{
//	        	if (ps == null){
//	        		SimpleDateFormat sdf2= new SimpleDateFormat(formatoFechaEnNombreDeArchivo);
//	        		sdf2.setTimeZone(TimeZone.getTimeZone("ECT"));
//	        		String dat = sdf2.format(new Date());
//	     	       	String nombreArchivo = directorioLog + raizNombreArchivo + dat + ".log";
//	        		ps = new PrintStream(new FileOutputStream(new File(nombreArchivo)));
//	        	}
//	        	System.setOut(ps);
//	        }
//	        catch (Exception e){
//	        	System.out.println("Q53: ATENCION! No ha sido posible redirigir la salida estandar");
//	        }
////		try {
////	 		 SimpleDateFormat sdf2= new SimpleDateFormat(formatoFechaEnNombreDeArchivo);
////	       sdf2.setTimeZone(TimeZone.getTimeZone("ECT"));
////	       String dat = sdf2.format(new Date());
////	       String nombreArchivo = directorioLog + raizNombreArchivo + dat + ".log";
////			 FileOutputStream out = new FileOutputStream(nombreArchivo, true);
////			 ps = new  PrintWriter(out);
////		} catch (Exception e) {
////			//ps = System.out;
////			System.out.println("ATENCION! NO SE PUEDE ESCRIBIR EL LOG EN SU ARCHIVO, se continua en la salida estandar");
////			System.out.println(e.getMessage());
////			e.printStackTrace();
////		}		
//	}
	
	/**
	 * Devuelve un String con la fecha en el formato definido por sdf
	 * 
	 * @return
	 */
	private String getStringDate() {
		try {
			Date now = new Date();
			return sdf.format(now);
		} catch (Exception e) {
			printError("Error al obtener la fecha ", e);
			return "";
		}
	}
}
