package exemplo;

import java.io.PrintStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.HashMap;

public class MecanismoPersistenciaAdHoc 
    implements exemplo.MecanismoPersistencia {

    private static int numConexoes = 12;
    private java.sql.Connection conexoesCriadas[];
    private java.sql.Connection conexoesLivres[];
    private java.util.HashMap conexoesAlocadas;
    private java.lang.String classeDoDriver;
    private java.lang.String url;
    private java.lang.String login;
    private java.lang.String senha;
    private boolean indisponivel;

    public MecanismoPersistenciaAdHoc(java.lang.String url, java.lang.String login, java.lang.String senha, java.lang.String classeDoDriver) throws exemplo.TransacaoBDException {
        conexoesAlocadas = new HashMap();
        this.classeDoDriver = classeDoDriver;
        this.url = url;
        this.login = login;
        this.senha = senha;
        indisponivel = false;
        try {
            java.lang.Class.forName(classeDoDriver);
        }
        catch (java.lang.ClassNotFoundException e) {
            throw new TransacaoBDException(e, "EXC_CLASSE_NAO_ENCONTRADA");
        }
    }

    public synchronized void conectar() throws exemplo.TransacaoBDException {
        if (conexoesCriadas == null) {
            try {
                conexoesLivres = new java.sql.Connection[exemplo.MecanismoPersistenciaAdHoc.numConexoes];
                conexoesCriadas = new java.sql.Connection[exemplo.MecanismoPersistenciaAdHoc.numConexoes];
                for (int i = 0; i < exemplo.MecanismoPersistenciaAdHoc.numConexoes; i++) {
                    conexoesCriadas[i] = java.sql.DriverManager.getConnection(url, login, senha);
                    conexoesLivres[i] = conexoesCriadas[i];
                }

            }
            catch (java.lang.Exception e) {
                throw new TransacaoBDException(e, "EXC_CONECTAR");
            }
        }
    }

    public synchronized void desconectar() throws exemplo.TransacaoBDException {
        try {
            if (conexoesCriadas != null) {
                int fechadas = 0;
                for (int i = 0; i < exemplo.MecanismoPersistenciaAdHoc.numConexoes; i++) {
                    java.lang.System.out.println("Conexao " + i + " " + conexoesLivres[i]);
                }

                java.lang.System.out.println("Conexoes alocadas " + conexoesAlocadas.size());
                for (int i = 0; i < exemplo.MecanismoPersistenciaAdHoc.numConexoes; i++) {
                    if (conexoesCriadas[i] != null) {
                        conexoesCriadas[i].close();
                        fechadas++;
                    }
                }

                conexoesCriadas = null;
                java.lang.System.out.println("Foram fechadas " + fechadas + " conexoes");
            }
        }
        catch (java.lang.Exception e) {
            throw new TransacaoBDException(e, "EXC_DESCONECTAR");
        }
    }

    public synchronized void iniciarTransacao() throws exemplo.TransacaoBDException {
        try {
            while (indisponivel)  {
                wait();
            }
            java.sql.Connection con = (java.sql.Connection)getCanalComunicacao(true);
            con.setAutoCommit(false);
        }
        catch (java.lang.Exception e) {
            throw new TransacaoBDException(e, "EXC_INICIAR_TRANSACAO");
        }
    }

    public synchronized void confirmarTransacao() throws exemplo.TransacaoBDException {
        try {
            java.sql.Connection con = (java.sql.Connection)getCanalComunicacao(true);
            con.commit();
            con.setAutoCommit(true);
            liberarCanalComunicacao(con, true);
        }
        catch (java.lang.Exception e) {
            throw new TransacaoBDException(e, "EXC_CONFIRMAR_TRANSACAO");
        }
        finally {
            notifyAll();
        }
    }

    public synchronized void cancelarTransacao() throws exemplo.TransacaoBDException {
        try {
            java.sql.Connection con = (java.sql.Connection)getCanalComunicacao(true);
            con.rollback();
            con.setAutoCommit(true);
            liberarCanalComunicacao(con, true);
        }
        catch (java.lang.Exception e) {
            throw new TransacaoBDException(e, "EXC_CANCELAR_TRANSACAO");
        }
        finally {
            try {
                notifyAll();
            }
            catch (java.lang.Exception e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized java.lang.Object getCanalComunicacao() {
        return getCanalComunicacao(false);
    }

    /**
     * Retorna um java.sql.Statement
     */
    private synchronized java.lang.Object getCanalComunicacao(boolean porTransacao) {
        java.lang.Object resposta = null;
        try {
            java.lang.Thread currentThread = java.lang.Thread.currentThread();
            int threadId = currentThread.hashCode();
            if (conexoesAlocadas.containsKey("" + threadId)) {
                Connection con = conexoesAlocadas.get("" + threadId);
                resposta = con.createStatement();
            } else {
                boolean achou = false;
                do {
                    if (achou) {
                        break;
                    }
                    for (int i = 0; !achou && i < exemplo.MecanismoPersistenciaAdHoc.numConexoes; i++) {
                        if (conexoesLivres[i] == null) {
                            continue;
                        }
                        achou = true;
                        resposta = conexoesLivres[i];
                        conexoesLivres[i] = null;
                        conexoesAlocadas.put("" + threadId, resposta);
                        if (porTransacao) {
                            conexoesAlocadas.put("T" + threadId, null);
                        }
                    }

                    if (!achou) {
                        indisponivel = true;
                        wait();
                    }
                } while (true);
            }
        }
        catch (java.lang.Exception ex) {
            ex.printStackTrace();
        }
        return resposta;
    }

    public synchronized void liberarCanalComunicacao() {
        liberarCanalComunicacao(false);
    }

    private synchronized void liberarCanalComunicacao(boolean porTransacao) {
        try {
            java.lang.Thread currentThread = java.lang.Thread.currentThread();
            int threadId = currentThread.hashCode();
            if (porTransacao || !porTransacao && !conexoesAlocadas.containsKey("T" + threadId)) {
                java.lang.Object canal = conexoesAlocadas.get("" + threadId);
                boolean achou = false;
                for (int i = 0; !achou && i < exemplo.MecanismoPersistenciaAdHoc.numConexoes; i++) {
                    if (conexoesLivres[i] != null) {
                        continue;
                    }
                    achou = true;
                    conexoesAlocadas.remove("" + threadId);
                    if (conexoesAlocadas.containsKey("T" + threadId)) {
                        conexoesAlocadas.remove("T" + threadId);
                    }
                    conexoesLivres[i] = (java.sql.Connection)canal;
                }

                indisponivel = false;
            }
        }
        catch (java.lang.Exception ex) {
            ex.printStackTrace();
        }
        finally {
            notifyAll();
        }
    }

}
