package com.rjconsultores.ventaboletos.web.utilerias.menu;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Menu;
import org.zkoss.zul.Menuitem;
import org.zkoss.zul.Menupopup;
import org.zkoss.zul.api.Menubar;

import com.rjconsultores.ventaboletos.entidad.PerfilFuncion;
import com.rjconsultores.ventaboletos.entidad.UsuarioPerfil;
import com.rjconsultores.ventaboletos.web.utilerias.menu.exception.MenuConfiguracionException;
import com.rjconsultores.ventaboletos.web.utilerias.menu.exception.MenuNoExisteException;

public class MenuFactoryPropertiesImpl implements MenuFactory {

	private List<No> tree = new ArrayList<No>();// Una lista con los itens del menu
	private BufferedReader menuReader; // El reader para leer el properties como un archivo texto
	private Desktop desktop; // Desktop de la aplicación
	private List<UsuarioPerfil> listUsuarioPerfil;// El perfil del usuario
	private HashMap<String, Boolean> listClavesPermisos;

	public MenuFactoryPropertiesImpl() {
		
	}
	
	public MenuFactoryPropertiesImpl(Desktop desktop, List<UsuarioPerfil> listUsuarioPerfil) {
		super();
		this.desktop = desktop;
		this.listUsuarioPerfil = listUsuarioPerfil;
		initListClavesPermissos();
	}

	private void initListClavesPermissos() {
		listClavesPermisos = new HashMap<String, Boolean>();
		for (UsuarioPerfil up : listUsuarioPerfil) {
			List<PerfilFuncion> listPerfilFuncion = up.getPerfil().getPerfilFuncionList();
			for (PerfilFuncion pf : listPerfilFuncion) {
				listClavesPermisos.put(pf.getFuncionSistema().getDescruta(), pf.getIndLectura());
			}
		}
	}

	/**
	 * Empeza la construccion del menu
	 * 
	 * @param menuBar
	 */
	@Override
	public void construirMenu(Menubar menuBar) {
		try {
			this.cargarMenuProperties();
			this.contruir(null);

			for (No no : tree) {
				String klass = no.getKlass();

				Menu menu = this.createMenu(klass);
				menuBar.appendChild(menu);

				this.contruirMenus(no, menu);

			}
		} catch (IOException e) {
			new MenuConfiguracionException("No fue posible cargar el archivo de menu.", e);
		}
	}

	/**
	 * Hace la construccion de los menus
	 * 
	 * @param no
	 * @param menu
	 */
	private void contruirMenus(No no, Menu menu) {
		for (No noH : no.getHijos()) {
			String klass = noH.getKlass();
			if (noH.getHijos().isEmpty()) {
				Menupopup menupopup = menu.getMenupopup();
				if (menupopup == null) {
					menupopup = new Menupopup();
				}
				menupopup.appendChild(this.createMenuItem(klass));

				menu.appendChild(menupopup);
			} else {
				Menu menuHijo = this.createMenu(klass);

				Menupopup nuevoMenupopup = new Menupopup();
				nuevoMenupopup.appendChild(menuHijo);
				Menupopup menupopup = menu.getMenupopup();

				menupopup.appendChild(menuHijo);
				contruirMenus(noH, menuHijo);
			}
		}
	}

	/**
	 * Carga el archivo properties en una estructura de arbol
	 * 
	 * @param noPadre
	 * @throws IOException
	 */
	public void contruir(No noPadre) throws IOException {
		String linea = null;
		
		while((linea = menuReader.readLine()) != null){ 
			No no = new No(linea);
			
			if (no.getNivel() == 1){
				tree.add(no);
			}else{
				for(No n1: tree){
					if (adicionarFilho(no, n1)){
						break;
					}
				}
			}
		}
		
	}
	private boolean adicionarFilho(No noHijo,No noPadre){
		boolean encontrou = false;
		if (noPadre.getNombre().equals(noHijo.getPadre())){
			noPadre.getHijos().add(noHijo);
			encontrou=true;
		}else{
			for(No n1 : noPadre.getHijos()){
				encontrou=adicionarFilho(noHijo, n1);
			}
		}
		return encontrou;
	}

	/**
	 * Crea un nuevo menu
	 * 
	 * @param strClass
	 * @return
	 */
	private Menu createMenu(String strClass) {
		ItemMenuSistema item = this.crearNueboItem(strClass);

		if (item != null) {
			Menu j = new Menu(item.getDescItemMenu());
			return j;
		}
		return null;
	}

	/**
	 * Crea un nuevo menuItem
	 * 
	 * @param strClass
	 * @return
	 */
	private Menuitem createMenuItem(String strClass) {
		final ItemMenuSistema item = this.crearNueboItem(strClass);

		if (item != null) {
			Menuitem j = new Menuitem(item.getDescItemMenu());
			j.addEventListener("onClick", new EventListener() {
				@Override
				public void onEvent(Event arg0) throws Exception {
					item.ejecutar();
				}
			});
			if (item.getClaveMenu() != null) {
				j.setVisible(listClavesPermisos.containsKey(item.getClaveMenu()));
				item.setIsReadOnly(listClavesPermisos.get(item.getClaveMenu()) != null ? listClavesPermisos.get(item.getClaveMenu()) : Boolean.FALSE );
			}
			return j;
		}
		return null;
	}

	/**
	 * Crea una instancia del menu especificado
	 * 
	 * @param strClass
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private ItemMenuSistema crearNueboItem(String strClass) {
		ItemMenuSistema classe = null;
		try {
			Class c = Class.forName(strClass);

			classe = (ItemMenuSistema) c.newInstance();
			classe.setDesktop(desktop);
		} catch (ClassNotFoundException e) {
			throw new MenuNoExisteException("El menu " + strClass + " no fue encontrado.", e);
		} catch (InstantiationException e) {
			throw new MenuNoExisteException("No se puede crear el menu " + strClass + ".", e);
		} catch (IllegalAccessException e) {
			throw new MenuNoExisteException("No fue posible accesar " + strClass + ".", e);
		}

		return classe;
	}

	/**
	 * Hace la carga del properties
	 * 
	 * @throws IOException
	 */
	private void cargarMenuProperties() throws IOException {
		InputStream miStream = this.getClass().getResourceAsStream("/com/rjconsultores/ventaboletos/web/cliente/menu/menu.properties");
		menuReader = new BufferedReader(new InputStreamReader(miStream));
	}

	/**
	 * Clase con la estructura del menu leída del archivo properties
	 * 
	 * @author gleimar
	 * 
	 */
	public static class No {
		String nombre;
		List<No> hijos;
		String klass;
		String padre;
		String path;

		public No(String linea){
			hijos = new ArrayList<No>();;
			String[] arrayLinea = linea.split("=");
			
			path=arrayLinea[0];
			klass=arrayLinea[1];
			
			String[] tmp = path.split("\\.");
			if (tmp.length != 1){
				padre = tmp[tmp.length-2];
				nombre = tmp[tmp.length-1];
			}else{
				nombre = tmp[0];
			}
		}

		public No(String nombre, String hijo) {
			super();
			this.nombre = nombre;
			hijos = new ArrayList<No>();
			hijos.add(new No(hijo));
		}

		public No(String nombre, List<No> hijos) {
			super();
			this.nombre = nombre;
			this.hijos = hijos;
		}

		public String getNombre() {
			return nombre;
		}

		public void setNombre(String nombre) {
			this.nombre = nombre;
		}

		public List<No> getHijos() {
			return hijos;
		}

		public void setHijos(List<No> hijos) {
			this.hijos = hijos;
		}

		public boolean add(No e) {
			return hijos.add(e);
		}

		public int getNivel() {
			return path.split("\\.").length;
		}

		public String getKlass() {
			return klass;
		}

		public void setKlass(String klass) {
			this.klass = klass;
		}

		public String getPadre() {
			return padre;
		}
		
		
		
	}
	public void print(){
		Integer nivel = 1;
		for(No n : tree){
			System.out.println(StringUtils.repeat("*", nivel)+n.getNombre());
			print(n, nivel);
		}
	}
	private void print(No n,Integer nivel){
		int mNivel = nivel+1;
		for(No n1 : n.getHijos()){
			System.out.println(StringUtils.repeat("*", mNivel)+n1.getNombre());
			if (!n1.getHijos().isEmpty()){
				print(n1, mNivel);
			}
		}
	}
	public static void main(String args[]){
		try {
			MenuFactoryPropertiesImpl menuFactoryTestes = new MenuFactoryPropertiesImpl();
			menuFactoryTestes.cargarMenuProperties();
			menuFactoryTestes.contruir(null);
			menuFactoryTestes.print();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
