/* 
* Copyright 2016 Agencia Estatal de Administración Tibutaria
*
* Licencia con arreglo a la EUPL, Versión 1.1 o –en cuanto
* sean aprobadas por la Comisión Europea– versiones
* posteriores de la EUPL (la «Licencia»);
* Solo podrá usarse esta obra si se respeta la Licencia.
* Puede obtenerse una copia de la Licencia en:
*  
* https://joinup.ec.europa.eu/software/page/eupl
*  
* Salvo cuando lo exija la legislación aplicable o se acuerde
* por escrito, el programa distribuido con arreglo a la
* Licencia se distribuye «TAL CUAL», SIN GARANTÍAS NI CONDICIONES
* DE NINGÚN TIPO, ni expresasni implícitas.
*
* Véase la Licencia en el idioma concreto que rige los permisos y 
* limitaciones que establece la Licencia.
*/ 

package gestor.utiles;

import java.awt.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.regex.*;

public class ConfiguracionBase {

   protected LinkedHashMap<String,Dato> data;
   String nombreFichero;
   int dummyKey = 0;
   Pattern regex1 = Pattern.compile("\\{\\$.+?\\}");
   Pattern regex2 = Pattern.compile("\\{\\#.+?\\}");
   java.util.logging.Logger log = null;
   protected Map mapaMacros;

   public ConfiguracionBase() {
      data = new LinkedHashMap<>();
      mapaMacros = data;
   }

   public ConfiguracionBase(java.util.logging.Logger log) {
      this();
      this.log = log;
   }

   public boolean cargar(String fichero) {
      nombreFichero = fichero;
      try {
         File file = new File(fichero);
         if (!file.exists()) {
            return false;
         }
         BufferedReader bf = new BufferedReader(new FileReader(file));
         String linea;
         while ((linea = bf.readLine()) != null) {
            add(linea);
         }
         bf.close();
      } catch (Exception exc) {
         System.out.println(exc.getMessage());
         return false;
      }
      for (String key : data.keySet()) {
         Dato dato = data.get(key);
         if (!dato.clave.equals("##")) {
            String nuevoValor = sustituye(dato.valor, regex1, mapaMacros);
            dato.valor = nuevoValor;
         }
      }
      return true;
   }

   void add(String linea) {
      linea = linea.trim();
      // guardar linea vacias y comentarios
      if (linea.length() == 0 || linea.startsWith("#")) {
         Dato dato = new Dato("##", linea);
         String clave = "&" + (dummyKey++);
         data.put(clave, dato);
         return;
      }
      // procesar las demas lineas, sobreescribiendo las repetidas
      String pp[] = linea.split("=", 2);
      String clave = pp[0].trim();
      String valor = "";
      if (pp.length > 1) {
         valor = pp[1].trim();
      }
      ponValor(clave, valor);
   }

   protected void ponValor(String clave, String valor) {
      String key = clave.toLowerCase();
      Dato dato = buscar(key);
      if (dato == null) {
         dato = new Dato(clave, valor);
         data.put(key, dato);
      } else {
         dato.valor = valor;
         dato.valorOriginal = valor;
      }
   }

   public String sustituye(String cadena) {
      return sustituye(cadena, regex1, data);
   }

   public String sustituye(String cadena, Pattern regex, Map mapa) {
      Matcher match = regex.matcher(cadena);
      String result = cadena;
      while (match.find()) {
         String patron = cadena.substring(match.start(), match.end());
         String key = patron.substring(1, patron.length() - 1);
         String[] params = key.split(",");
         if (params.length > 1) {
            key = params[0].toLowerCase().trim();
            if (mapa.containsKey(key)) {
               HashMap<String,String> args = new HashMap<>();
               for (int i = 1; i < params.length; i++) {
                  args.put("#" + i, params[i].trim());
               }
               String patronExpandido = sustituye(mapConTipo(mapa, key), regex2, args);
               result = replace(result, patron, patronExpandido);
            }
         } else if (mapa.containsKey(key)) {
            result = replace(result, patron, mapConTipo(mapa, key));
         }
      }
      return result;
   }

   String mapConTipo(Map mapa, Object key) {
      Object obj = mapa.get(key);
      if (obj instanceof Dato) {
         return ((Dato) obj).valor;
      } else {
         return (String) obj;
      }
   }

   String replace(String dest, String viejo, String nuevo) {
      int pos = dest.indexOf(viejo);
      int n = viejo.length();
      return dest.substring(0, pos) + nuevo + dest.substring(pos + n);
   }

   public Dato buscar(String clave) {
      return data.get(clave.toLowerCase());
   }

   public boolean guardar() {
      try {
         PrintWriter pw = new PrintWriter(new FileOutputStream(nombreFichero, false));
         for (Iterator<String> it = data.keySet().iterator(); it.hasNext();) {
            Dato d = data.get(it.next());
            if (d.clave.equals("##")) {
               pw.println(d.valorOriginal);
            } else {
               pw.println(d.clave + "=" + d.valorOriginal);
            }
         }
         pw.close();
         return true;
      } catch (Exception exc) {
         System.out.println(exc.getMessage());
         return false;
      }
   }

   protected String valor(String clave, String porDefecto) {
      Dato dato = buscar(clave);
      if (dato == null) {
         return porDefecto;
      } else {
         String s = dato.valor;
         s = s.replaceFirst("^\"", "");
         s = s.replaceFirst("\"$", "");
         return s;
      }
   }

   protected int valor(String clave, int porDefecto) {
      Dato dato = buscar(clave);
      if (dato == null) {
         return porDefecto;
      }
      try {
         return Integer.parseInt(dato.valor);
      } catch (NumberFormatException exc) {
         if (log != null) {
            String msg = "Valor numerico ERRONEO en campo " + clave + " en " + nombreFichero;
            log.warning(msg);
         }
      }
      return porDefecto;
   }

   protected long valor(String clave, long porDefecto) {
      Dato dato = buscar(clave);
      if (dato == null) {
         return porDefecto;
      }
      try {
         return Long.parseLong(dato.valor);
      } catch (NumberFormatException exc) {
         if (log != null) {
            String msg = "Valor numerico ERRONEO en campo " + clave + " en " + nombreFichero;
            log.warning(msg);
         }
      }
      return porDefecto;
   }

   protected double valor(String clave, double porDefecto) {
      Dato dato = buscar(clave);
      if (dato == null) {
         return porDefecto;
      }
      try {
         return Double.parseDouble(dato.valor);
      } catch (NumberFormatException exc) {
         if (log != null) {
            String msg = "Valor numerico ERRONEO en campo " + clave + " en " + nombreFichero;
            log.warning(msg);
         }
      }
      return porDefecto;
   }

   protected float valor(String clave, float porDefecto) {
      Dato dato = buscar(clave);
      if (dato == null) {
         return porDefecto;
      }
      try {
         return Float.parseFloat(dato.valor);
      } catch (NumberFormatException exc) {
         if (log != null) {
            String msg = "Valor numerico ERRONEO en campo " + clave + " en " + nombreFichero;
            log.warning(msg);
         }
      }
      return porDefecto;

   }

   protected boolean valor(String clave, boolean porDefecto) {
      Dato dato = buscar(clave);
      if (dato == null) {
         return porDefecto;
      }
      return dato.valor.equalsIgnoreCase("si");
   }

   protected Color valorColor(String clave, Color porDefecto) {
      Dato dato = buscar(clave);
      if (dato != null) {
         try {
            String[] rgb = dato.valor.split(",");
            Color color = new Color(Integer.parseInt(rgb[0].trim()), Integer.parseInt(rgb[1].trim()), Integer.parseInt(rgb[2].trim()));
            return color;
         } catch (Exception exc) {
         }
      }
      return porDefecto;
   }

   protected Date valor(String clave, Date porDefecto) {
      Dato dato = buscar(clave);
      if (dato != null) {
         try {
            java.text.SimpleDateFormat dateFormatDMA = new java.text.SimpleDateFormat("dd/MM/yyyy");
            return dateFormatDMA.parse(dato.valor);
         } catch (ParseException exc) {
            if (log != null) {
               String msg = "Valor de fecha ERRONEO en campo " + clave + " en " + nombreFichero;
               log.warning(msg);
            }
         }
      }
      return porDefecto;
   }

   protected class Dato {

      public String clave;
      public String valor;
      public String valorOriginal;

      public Dato(String p1, String p2) {
         clave = p1;
         valor = p2;
         valorOriginal = p2;
      }
   }
}
