package es.ejie.frmk.infraestructura.logs.base.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.log4j.Layout;
import org.apache.log4j.spi.LoggingEvent;

import es.ejie.frmk.infraestructura.logs.base.Q70ConstantesLogs;
import es.ejie.frmk.infraestructura.logs.base.Q70LogInterno;
import es.ejie.frmk.infraestructura.logs.base.interfaces.Q70GestorDatosComunes;


/**
 * Layout especifico para el framework que formatea la información de forma encolumnada
 *  y que trunca a un tamaño pasado entre paréntesis "COMPONENTE(29),ERROR(10)".
 *  Trunca eliminando los mensajes por el final al contrario que el pattern layout.
 *  Considera que el mensaje es un elemento de tipo mensaje que contiene los valores de varias columnas.
 */
public class Q70MensajeColumnaLayout extends Layout
{
	private class Columna
	{
		Columna(String nombre, int size)
		{
			this.nombre = nombre;
			this.size = size;
		}
		String nombre;
		int size;
	}
	//tamaño por defecto para las columnas que no lo especifican;
	private final static int DEFAULT_SIZE = 15;
	private final static int DATE_SIZE = Q70ConstantesLogs.DATE_FORMAT.length();

	//contiene las columnas globales y su tamaño
	//	Map globalColumns= new HashMap();
	List globalColumns = new ArrayList();

	//contiene las columnas del mensaje y su tamaño
	List messageColumns = new ArrayList();

	//utilizado para realizar el parseo de las columnas
	StringTokenizer st;
	String actualToken = null;

	//indica si se debe escribir el nombre del log o no.
	boolean addLoggerName = false;

	//	lee los tokens evitando caracteres en blanco 
	private String nextToken()
	{
		String token = null;
		do
		{
			if (st.hasMoreTokens())
				token = st.nextToken();
			else
				return null;
		}
		while (token.equals(" "));

		if (Q70LogInterno.isEnabled())
			Q70LogInterno.log("next token: " + token);
		return token;
	}

	private void column(List list)
	{
		int size = DEFAULT_SIZE;
		String columnName = nextToken();
		actualToken = nextToken();
		if (actualToken != null && actualToken.equals("("))
		{
			//viene el tamaño
			actualToken = nextToken();
			try
			{
				size = Integer.parseInt(actualToken);
			}
			catch (java.lang.NumberFormatException nfe)
			{
				if (Q70LogInterno.isEnabled())
					Q70LogInterno.log(actualToken + " no es un número");
				size = DEFAULT_SIZE;
			}
			actualToken = nextToken();
			if (actualToken != null && actualToken.equals(")"))
			{
				Columna col = new Columna(columnName, size);
				list.add(col);
				if (Q70LogInterno.isEnabled())
					Q70LogInterno.log("columna: " + columnName + " tamaño: " + size);
				actualToken = nextToken();
			}
		}
		else
		{
			Columna col = new Columna(columnName, DEFAULT_SIZE);
			list.add(col);
			//actualToken=nextToken();
			if (Q70LogInterno.isEnabled())
				Q70LogInterno.log("columna: " + columnName + " tamaño: " + DEFAULT_SIZE);
		}

	}

	private void parseColumns(String columns, List list)
	{
		if (columns != null && !columns.trim().equals(""))
		{
			st = new StringTokenizer(columns, ",()", true);

			if (st.hasMoreTokens())
			{
				column(list);

				while (actualToken != null)
				{
					if (actualToken.equals(","))
						column(list);
					else
					{
						if(Q70LogInterno.isEnabled())
							Q70LogInterno.log("Error en la expresion de columnas");
						return;
					}
				}
			}
		}

	}

	Q70GestorDatosComunes commonDataManager = null;
	public Q70MensajeColumnaLayout(Q70GestorDatosComunes commonDataManager, String globalColumns, String messageColumns)
	{

		this.commonDataManager = commonDataManager;
		parseColumns(globalColumns, this.globalColumns);
		parseColumns(messageColumns, this.messageColumns);

	}

	private String addSpaces(int spacesNumber)
	{
		StringBuffer s = new StringBuffer();
		for (int i = 0; i < spacesNumber; i++)
		{
			s.append(" ");
		}
		return s.toString();
	}

	private String getFormattedColumn(String value, int size)
	{
		//si el valor es mayor que el maximo entonces truncar
		String ret = null;
		if (size == 0)
			return value;
		if (value == null)
			ret = addSpaces(size);
		else
		{
			int longitud = value.length();
			if (longitud > size)
				ret = value.substring(0, size);
			else
				if (longitud == size)
					ret = value;
				else
					ret = value + addSpaces(size - longitud);
		}
		if (Q70LogInterno.isEnabled())
			Q70LogInterno.log("columna formateada:/" + ret + "/");
		return ret;
	}

	/**
	 * @see org.apache.log4j.Layout#format(org.apache.log4j.spi.LoggingEvent)
	 */
	public String format(LoggingEvent event)
	{
		//		Se da por supuesto que el mensage viene en una hashmap
		HashMap map = (HashMap)event.getMessage();

		StringBuffer formattedString = new StringBuffer();
		if (this.addLoggerName)
			formattedString.append(event.getLoggerName() + ": ");
		//incorporar siempre el timestamp
		SimpleDateFormat formatter = new SimpleDateFormat(Q70ConstantesLogs.DATE_FORMAT);
		formattedString.append(getFormattedColumn(formatter.format(new Date()), DATE_SIZE));
		formattedString.append(" ");

		if (Q70LogInterno.isEnabled())
			Q70LogInterno.log("formated String :" + formattedString);
		//incorporar los campos genericos
		//Iterator keyIterator = globalColumns.keySet().iterator();
		//while (keyIterator.hasNext())
		for (int i = 0; i < globalColumns.size(); i++)
		{
			//Object key = keyIterator.next();
			Columna col = (Columna)globalColumns.get(i);
			formattedString.append(getFormattedColumn(
			//(String)event.getMDC(col.nombre),
			this.commonDataManager.getProperty(col.nombre), col.size));
			if (Q70LogInterno.isEnabled())
				Q70LogInterno.log("formated String :" + formattedString);
			formattedString.append(" ");
		}

		//incorporar los valores del mensaje
		for (int i = 0; i < messageColumns.size(); i++)
		{
			//Object key = keyIterator.next();
			Columna col = (Columna)messageColumns.get(i);
			formattedString.append(getFormattedColumn((String)map.get(col.nombre), col.size));
			if (Q70LogInterno.isEnabled())
				Q70LogInterno.log("formated String :" + formattedString);
			formattedString.append(" ");
		}
		formattedString.append("\n");
		return formattedString.toString();

	}

	/**
	 * @see org.apache.log4j.Layout#ignoresThrowable()
	 */
	public boolean ignoresThrowable()
	{
		return true;
	}

	/** 
	 * @see org.apache.log4j.spi.OptionHandler#activateOptions()
	 */
	public void activateOptions()
	{
	}

	/**
	 * @param activate
	 */
	public void addLoggerName(boolean activate)
	{
		this.addLoggerName = activate;
	}
}
