/*
 * Universidade Federal de Pernambuco
 * Centro de Informática
 *
 * SAC - Sociedade beneficente de Amparo aos portadores de AIDS e do Cancer
 *
 * Tipo: Droga
 *
 * Esta classe representa uma droga
 *
 * @author Centro de Informatica - UFPE
 * @version  0.1 -  25/10/2001
 * @since JDK 1.2
 */

package sac.droga;

import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;

import java.sql.PreparedStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;

import sac.persistencia.OIDFactory;
import sac.persistencia.CacheObjetos;
import sac.persistencia.PoolConexoes;
import sac.persistencia.PersistenceException;
import sac.persistencia.OID;

import sac.exception.NullArgumentException;

public class DBDroga implements RepositorioDroga {
    private final static String GET_OID_NOME_SQL =
      "select CD_DROGA from Droga where NM_DROGA like ?";
    private final static String GET_DROGAS_ACOES_SQL =
      "select * from Droga where DS_ACOES_TERAPEUTICAS like ? ";
    private final static String PROCURAR_DROGA_NOME_EXATO_SQL =
      "select * from Droga where NM_DROGA = ?";
    private final static String PROCURAR_DROGA_NOME_SQL =
      "select * from Droga where NM_DROGA like ?";
    private final static String PROCURAR_DROGA_CD_SQL =
      "select * from Droga where CD_DROGA = ?";
    private final static String INSERE_DROGA_SQL =
      "insert into Droga (CD_DROGA, NM_DROGA, DS_ACOES_TERAPEUTICAS, " +
      "DS_PROPRIEDADES, DS_DOSAGEM, DS_INDICACOES, DS_CONTRA_INDICACOES, " +
      "DS_REACOES_ADVERSAS, DS_PRECAUCOES, DS_INTERACOES) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
    private final static String ALTERA_DROGA_SQL =
      "update Droga set NM_DROGA = ?, DS_ACOES_TERAPEUTICAS = ?, "+
      "DS_PROPRIEDADES = ?, DS_DOSAGEM = ?, DS_INDICACOES = ?, DS_CONTRA_INDICACOES = ?, " +
      "DS_REACOES_ADVERSAS = ?, DS_PRECAUCOES = ?, DS_INTERACOES = ? where CD_DROGA = ?";

    private final static String PROCURAR_DROGAS = "select * from Droga";

    private PoolConexoes poolConexoes;
    private CacheObjetos cache;
    private OIDFactory oidfactory;
    private static DBDroga instancia;

    /**
    * Construtor da Classe
    */
    public DBDroga() throws PersistenceException {
        poolConexoes = PoolConexoes.getInstancia();
        cache = CacheObjetos.getInstancia();
        oidfactory = OIDFactory.getInstancia();
    }


    public static synchronized DBDroga getInstancia() throws PersistenceException {
        if (instancia == null) {
            instancia = new DBDroga();
        }
        return instancia;
    }

/*Inserir---------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
    /**
     * Insere uma nova droga
     *
     * @param droga      Droga a ser cadastrada <code>Droga</code>
     * @exception DrogaJaCadastradaException - Levantada quando tenta-se
     *            cadastrar uma droga ja cadastrada.
     */
    public void inserir(Droga droga) throws DrogaJaCadastradaException,
        PersistenceException, NullArgumentException, DrogaJaCadastradaException {

        Connection conexao = null;
        OID oid = null;
        Object obj;

        try {
            conexao =  poolConexoes.obterConexao();
            conexao.setAutoCommit(false);

            String nome = droga.getNome();
            if(nome != null && existeDroga(nome, conexao)) {
              throw new DrogaJaCadastradaException("Droga já cadastrada. Por favor, verifique os dados da droga.");
            }

            oid = oidfactory.novoOID();
            droga.setID(oid);
            inserir(droga, conexao);
            conexao.commit();
            cache.inserirObjeto(oid, droga);
        } catch (SQLException excecao) {
              if(oid != null) {
                cache.invalidarObjeto(oid);
              }
              try {
                  conexao.rollback();
              } catch (Exception ex) {
                  throw new PersistenceException ("Nao foi possivel realizar RollBack. ", ex);
              }
              throw new PersistenceException(excecao.getMessage());
        } catch (PersistenceException pe){
            try {
             conexao.rollback();
            } catch (Exception excecao) {
                throw new PersistenceException ("Nao foi possivel realizar RollBack. ", excecao);
            }
            throw new PersistenceException(pe.getMessage());
        } finally {
              if(conexao != null) {
                try {
                  poolConexoes.liberarConexao(conexao);
                } catch (SQLException excecao) {
                  throw new PersistenceException(excecao.getMessage());
                }
            }
        }
    }

    private void inserir(Droga droga, Connection conexao) throws SQLException {
        PreparedStatement statement = null;
        try {
            statement =  conexao.prepareStatement(INSERE_DROGA_SQL);
            statement.setLong(1, Long.parseLong(droga.getID().toString()));
            statement.setString(2, droga.getNome());
            statement.setString(3, droga.getAcoesTerapeuticas());
            statement.setString(4, droga.getPropriedades());
            statement.setString(5, droga.getDosagem());
            statement.setString(6, droga.getIndicacoes());
            statement.setString(7, droga.getContraIndicacoes());
            statement.setString(8, droga.getReacoesAdversas());
            statement.setString(9, droga.getPrecaucoes());
            statement.setString(10, droga.getInteracoes());

            statement.execute();
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
    }

/*Alterar----------------------------------------------------------------------/
/*-----------------------------------------------------------------------------/
    /**
     * Altera uma droga ja cadastrada
     *
     * @param droga   Droga a ser alterada <code>Droga</code>
     * @param droga   Droga com novos os parametros <code>Droga</code>
     */
    public void alterar(Droga droga, Droga newDroga) throws PersistenceException,
        DrogaNaoCadastradaException, NullArgumentException {

        Connection conexao = null;
        OID oid;

        try {
            conexao =  poolConexoes.obterConexao();
            conexao.setAutoCommit(false);
            oid = getOID(droga.getNome(), conexao);

            if(oid != null) {
              Droga temp = procurarPeloNomeExato(newDroga.getNome(), conexao);
              if(temp != null && !(temp.getID().equals(droga.getID()))) {
                throw new PersistenceException("Droga já existente. Por favor, modifique o nome da droga");
              }
              alterar(oid, newDroga, conexao);
              conexao.commit();
              cache.inserirObjeto(oid, newDroga);
            } else {
              throw new DrogaNaoCadastradaException("Droga nao cadastrada!");
            }
        } catch (SQLException excecao) {
            throw new PersistenceException(excecao.getMessage());
        } finally {
            if(conexao != null) {
                try {
                  poolConexoes.liberarConexao(conexao);
                } catch (SQLException excecao) {
                 throw new PersistenceException(excecao.getMessage());
                }
            }
        }
    }

    private void alterar(OID oid, Droga droga, Connection conexao) throws SQLException {
        PreparedStatement statement = null;
        try {
            statement =  conexao.prepareStatement(ALTERA_DROGA_SQL);
            statement.setString(1, droga.getNome());
            statement.setString(2, droga.getAcoesTerapeuticas());
            statement.setString(3, droga.getPropriedades());
            statement.setString(4, droga.getDosagem());
            statement.setString(5, droga.getIndicacoes());
            statement.setString(6, droga.getContraIndicacoes());
            statement.setString(7, droga.getReacoesAdversas());
            statement.setString(8, droga.getPrecaucoes());
            statement.setString(9, droga.getInteracoes());
            statement.setLong(10, oid.getLongValue());


            statement.executeUpdate();
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
    }
/*Procurar---------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------*/
   /**
     * Retorna uma droga com determinado nome
     *
     * @param nome    nome da droga <code>String</code>
     * @return droga procurada <code>Droga</code>
     */
    public List procurarPeloNome(String nome) throws PersistenceException,
        DrogaNaoCadastradaException, NullArgumentException {

        Connection conexao = null;

        try {
            conexao =  poolConexoes.obterConexao();
            OID oid = getOID(nome, conexao);
            if(oid == null) {
                throw new DrogaNaoCadastradaException("Droga não cadastrada!");
            }
            return procurarPeloNome(nome, conexao);;
        } catch (SQLException excecao) {
            throw new PersistenceException(excecao.getMessage());
        } finally {
            if(conexao != null) {
              try {
                poolConexoes.liberarConexao(conexao);
              } catch (SQLException excecao) {
                throw new PersistenceException(excecao.getMessage());
              }
            }
        }
    }

    private List procurarPeloNome(String nome, Connection conexao) throws SQLException, NullArgumentException{
        PreparedStatement statement = null;
        List drogas = null;
        try {
            statement =  conexao.prepareStatement(PROCURAR_DROGA_NOME_SQL);
            statement.setString(1, "%"+nome+"%");

            ResultSet resultSet = statement.executeQuery();
            drogas = obtemDrogas(resultSet);
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
        return drogas;
    }

    /**
     * Retorna uma droga com determinado ID
     *
     * @param id    id da droga <code>OID</code>
     * @return droga procurada <code>Droga</code>
     */
    public Droga procurar(OID id) throws PersistenceException, DrogaNaoCadastradaException, NullArgumentException {
        Connection conexao = null;
        Droga droga = null;

        try {
            droga = (Droga) cache.pegarObjeto(id);
            if(droga == null){
              conexao =  poolConexoes.obterConexao();
              droga = procurar(id, conexao);
              cache.inserirObjeto(id, droga);
            }
            return droga;
        } catch (SQLException excecao) {
            throw new PersistenceException(excecao.getMessage());
        } finally {
            if(conexao != null) {
              try {
                poolConexoes.liberarConexao(conexao);
              } catch (SQLException excecao) {
                throw new PersistenceException(excecao.getMessage());
              }
            }
        }
    }

    private Droga procurar(OID id, Connection conexao) throws SQLException, DrogaNaoCadastradaException, NullArgumentException {
        PreparedStatement statement= null;
        Droga droga = null;
        try {
            statement =  conexao.prepareStatement(PROCURAR_DROGA_CD_SQL);
            statement.setLong(1, id.getLongValue());
            ResultSet resultSet = statement.executeQuery();

            if(resultSet.next()) {
                droga = getDroga(resultSet);
            } else {
                throw new DrogaNaoCadastradaException ("Droga não Cadastrada");
            }
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
        return droga;
    }

    /**
     * Retorna uma lista de drogas com determinadas acoes terapeuticas
     *
     * @param acoesTerapeuticas    Acoes Terapeuticas da droga <code>String</code>
     * @return Lista de drogas <code>List</code>
     */
    public List procurarPorAcoesTerapeuticas(String acoesTerapeuticas) throws PersistenceException, DrogaNaoCadastradaException, NullArgumentException {
        Connection conexao = null;
        List oids = null;
        List drogas = null;

        try {
            conexao =  poolConexoes.obterConexao();
            drogas = procurarDrogasAcoesTerapeuticas(conexao, acoesTerapeuticas);
        } catch (SQLException excecao) {
            throw new PersistenceException(excecao.getMessage());
        } finally {
            if(conexao != null) {
              try {
                poolConexoes.liberarConexao(conexao);
              } catch (SQLException excecao) {
                throw new PersistenceException(excecao.getMessage());
              }
            }
        }
        return drogas;
    }

    private List procurarDrogasAcoesTerapeuticas(Connection conexao, String acoesTerapeuticas)
        throws PersistenceException, DrogaNaoCadastradaException,
        NullArgumentException, SQLException {

        PreparedStatement statement = null;
        List drogas = null;

        try {
            statement =  conexao.prepareStatement(GET_DROGAS_ACOES_SQL);
            statement.setString(1, "%"+acoesTerapeuticas+"%");
            ResultSet resultSet = statement.executeQuery();
            drogas = obtemDrogas(resultSet);
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
        return drogas;
    }

    public List obtemDrogas() throws PersistenceException, NullArgumentException {
        Connection conexao = null;
        List drogas = null;
        try {
            conexao =  poolConexoes.obterConexao();
            drogas = obtemDrogas(conexao);
            return drogas;
        } catch (SQLException excecao) {
            throw new PersistenceException(excecao.getMessage());
        } finally {
            if(conexao != null) {
              try {
                poolConexoes.liberarConexao(conexao);
              } catch (SQLException excecao) {
                throw new PersistenceException(excecao.getMessage());
              }
            }
        }
    }

    private List obtemDrogas(Connection conexao) throws SQLException, NullArgumentException {
        PreparedStatement statement= null;
        try {
            statement =  conexao.prepareStatement(PROCURAR_DROGAS);
            ResultSet resultSet = statement.executeQuery();

            return obtemDrogas(resultSet);
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
    }
/*Auxiliares---------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------*/
    private OID getOID(String nome, Connection conexao) throws SQLException {
        PreparedStatement statement = null;
        OID oid = null;
        try {
            statement =  conexao.prepareStatement(GET_OID_NOME_SQL);
            statement.setString(1, "%"+nome+"%");
            ResultSet resultSet = statement.executeQuery();

            if(resultSet.next()) {
                oid = new OID(resultSet.getLong("CD_DROGA"));
            }
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
        return oid;
    }

    private boolean existeDroga(String nome, Connection conexao) throws SQLException, NullArgumentException {
        PreparedStatement statement= null;
        try {
            if(nome == null) {
              throw new NullArgumentException("nome == null");
            }

            statement =  conexao.prepareStatement(PROCURAR_DROGA_NOME_EXATO_SQL);
            statement.setString(1, nome);
            ResultSet resultSet = statement.executeQuery();
            return resultSet.next();
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
    }

    private Droga procurarPeloNomeExato(String nome, Connection conexao) throws SQLException, NullArgumentException{
        Droga droga = null;
        PreparedStatement statement = null;
        try {
            statement =  conexao.prepareStatement(PROCURAR_DROGA_NOME_EXATO_SQL);
            statement.setString(1, nome);

            ResultSet resultSet = statement.executeQuery();
            if(resultSet.next()) {
                droga = getDroga(resultSet);
            }
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
        return droga;
    }

    private List obtemDrogas(ResultSet resultSet) throws SQLException, NullArgumentException {
        List drogas = new LinkedList();

        while(resultSet.next()) {
            OID oid = new OID(resultSet.getLong("CD_DROGA"));

            Droga droga = (Droga)cache.pegarObjeto(oid);

            if(droga == null) {
                droga = new Droga();

                droga.setID(oid);
                droga.setNome(resultSet.getString("NM_DROGA"));
                droga.setAcoesTerapeuticas(resultSet.getString("DS_ACOES_TERAPEUTICAS"));
                droga.setPropriedades(resultSet.getString("DS_PROPRIEDADES"));
                droga.setDosagem(resultSet.getString("DS_DOSAGEM"));
                droga.setIndicacoes(resultSet.getString("DS_INDICACOES"));
                droga.setContraIndicacoes(resultSet.getString("DS_CONTRA_INDICACOES"));
                droga.setReacoesAdversas(resultSet.getString("DS_REACOES_ADVERSAS"));
                droga.setPrecaucoes(resultSet.getString("DS_PRECAUCOES"));

                droga.setInteracoes(resultSet.getString("DS_INTERACOES"));

                cache.inserirObjeto(oid, droga);
            }
            drogas.add(droga);
        }
        return drogas;
    }

    private Droga getDroga(ResultSet resultSet) throws SQLException, NullArgumentException {
        Droga droga;

        droga = new Droga();
        droga.setID(new OID(resultSet.getLong("CD_DROGA")));
        droga.setNome(resultSet.getString("NM_DROGA"));
        droga.setAcoesTerapeuticas(resultSet.getString("DS_ACOES_TERAPEUTICAS"));
        droga.setPropriedades(resultSet.getString("DS_PROPRIEDADES"));
        droga.setDosagem(resultSet.getString("DS_DOSAGEM"));
        droga.setIndicacoes(resultSet.getString("DS_INDICACOES"));
        droga.setContraIndicacoes(resultSet.getString("DS_CONTRA_INDICACOES"));
        droga.setReacoesAdversas(resultSet.getString("DS_REACOES_ADVERSAS"));
        droga.setPrecaucoes(resultSet.getString("DS_PRECAUCOES"));
        droga.setInteracoes(resultSet.getString("DS_INTERACOES"));

        return droga;
    }
}
