/* 
* 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 servidor.datos.base;

import java.util.*;
import java.sql.*;
import java.util.regex.*;
import servidor.datos.*;

public abstract class MemTabla implements ITablaReactivable {

   protected String sql;
   protected ArrayList<MemRegistro> filas;
   protected SentenciaSQL updateStmt;
   protected SentenciaSQL insertStmt;
   protected SentenciaSQL deleteStmt;
   protected int semillaAutoIncremento;

   public MemTabla() {
      filas = new ArrayList<>(100);
   }

   public String select() {
      return sql;
   }

   public abstract MemRegistro nuevoRegistro();

   public int recargar() {
      BaseDatos bd = BaseDatos.bd();
      ResultSet rs = bd.abrirTabla(this);
      filas.clear();
      try {
         while (rs.next()) {
            MemRegistro mr = nuevoRegistro();
            mr.cargar(rs);
            filas.add(mr);
         }
         rs.close();
      } catch (SQLException exc) {
         exc.printStackTrace();
      }
      calcularSemillaAutoincremento();
      return filas.size();
   }

   public int cargar(String sql) {
      this.sql = sql;
      return recargar();
   }

   public ArrayList<MemRegistro> registros() {
      return filas;
   }

   public MemCursor getCursor() {
      Cursor cur = new Cursor();
      cur.principio();
      return cur;
   }

   abstract public void borrarTabla();

   public int count() {
      return filas.size();
   }

   String nombreTabla(String sql) {
      String nombre = "";
      Pattern regex = Pattern.compile("from\\s+(\\w+)", Pattern.CASE_INSENSITIVE);
      Matcher match = regex.matcher(sql);
      if (match.find()) {
         nombre = match.group(1);
      }
      return nombre;
   }

   void calcularSemillaAutoincremento() {
      semillaAutoIncremento = 0;
      String nombre = nombreTabla(sql);
      try {
         Statement stmt = BaseDatos.bd().getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
         String pepe = "EXEC ('DBCC CHECKIDENT(\"" + nombre + "\",NORESEED)')";
         stmt.execute(pepe);
         SQLWarning w = stmt.getWarnings();
         String s = "";
         if ((w = w.getNextWarning()) != null) {
            s = w.getMessage();
            Pattern regex = Pattern.compile("\\D*'(\\d+)'.*");
            Matcher match = regex.matcher(s);
            if (match.matches()) {
               semillaAutoIncremento = Integer.parseInt(match.group(1));
            }
         }
         stmt.close();
      } catch (Exception exc) {
      }
   }

   int autoNumericoObtenido() {
      int r = 0;
      if (BaseDatos.bd().recuperacionActivada) {
         return calcularUltimoNumero();
      } else {
         try {
            ResultSet rs = insertStmt.getGeneratedKeys();
            rs.next();
            r = rs.getInt(1);
            rs.close();
         } catch (SQLException exc) {
            r = calcularUltimoNumero();
         }
         return r;
      }
   }

   abstract int calcularUltimoNumero();

   public abstract void insert(MemRegistro reg) throws SQLException;

   public abstract void update(MemRegistro reg) throws SQLException;

   public abstract void delete(MemRegistro reg);

   void errorAccesoBDSevero(SQLException ex) throws SQLException {
      String msg = ex.getMessage();
      if (msg.startsWith("Invalid state, the Connection object is closed") ||
              msg.startsWith("I/O Error: Connection reset by peer: socket write error")) {
         servidor.comunicaciones.GestorColas.getGestor().paradaCautelar("El servidor se cierra por problemas con el acceso a la Base de Datos");
         throw ex;
      } else {
         // TODO establecer criterio para otros errores de BD
         ex.printStackTrace();
      }
   }

   public class Cursor implements MemCursor {

      int actual;

      @Override
      public void principio() {
         actual = -1;
      }

      @Override
      public boolean haySiguiente() {
         return (actual < filas.size() - 1);
      }

      @Override
      public MemRegistro siguiente() {
         actual++;
         return filas.get(actual);
      }

      @Override
      public int count() {
         return filas.size();
      }
   }
}
