package sac.doenca;

import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
import java.lang.StringBuffer;

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;

import sac.droga.RepositorioDroga;
import sac.droga.DBDroga;
import sac.droga.Droga;
import sac.droga.DrogaNaoCadastradaException;

import java.util.List;
import sac.cid.CID;
import sac.medicamento.Medicamento;

/*
 * Universidade Federal de Pernambuco
 * Centro de Informática
 *
 * SAC - Sociedade beneficente de Amparo aos portadores de AIDS e do Cancer
 *
 * Tipo: DBDoenca
 *
 * Esta classe implementa a interface RepositorioDoenca
 *
 * @author Centro de Informatica - UFPE
 * @version  0.1 -  25/10/2001
 * @since JDK 1.3
 */

//Obterconexao foi foda... :P

public class DBDoenca implements RepositorioDoenca{

    private final String getOIDSQL = "select CD_DOENCA from doenca where NM_DOENCA = ?";
    private final String getOIDSQLPeloCID = "select CD_DOENCA from doenca where NM_CID = ?";
    private final String procurarDoencaPeloCodigoSQL = "select CD_DOENCA, NM_DOENCA, NM_CID, DS_HISTORICO, DS_SINTOMAS, DS_HEREDITARIEDADE from doenca where CD_Doenca = ?";
    private final String PROCURAR_PELO_NOME_PARECIDO_SQL = "select * from doenca where NM_DOENCA like ?";

    //Retorna todas as doencas
    private final String getTodasDoencas = "select CD_DOENCA, NM_DOENCA, NM_CID, DS_HISTORICO, DS_SINTOMAS, DS_HEREDITARIEDADE from doenca";
    private final String getDrogasRelacionadas = "Select CD_DROGA from Doenca_Droga where CD_DOENCA = ?";
//    private final String procurarDoencaPeloNomeSQL = "select CD_DOENCA, NM_DOENCA, CD_CID, DS_HISTORICO, DS_SINTOMAS, DS_HEREDITARIEDADE from doenca where NM_Doenca = ?";
//    private final String procurarDoencaPeloCIDSQL = "select CD_DOENCA, NM_DOENCA, CD_CID, DS_HISTORICO, DS_SINTOMAS, DS_HEREDITARIEDADE from doenca where CD_CID = ?";
    //falta pegar as doenças relacionadas

    private final String insereDoencaSQL = "insert into doenca (CD_DOENCA, NM_DOENCA, NM_CID, DS_HISTORICO, DS_SINTOMAS, DS_HEREDITARIEDADE) VALUES (?,?,?,?,?,?)";
    private final String insereDoencaDrogaSQL = "insert into Doenca_Droga (CD_DOENCA, CD_DROGA) VALUES (?,?)";
    private final String removeDoencaSQL = "delete from doenca where CD_DOENCA = ?";
    private final String removeDoencaDrogaSQL = "delete from Doenca_Droga where CD_DOENCA = ?";

    private final String alteraDoencaSQL = "update doenca set CD_DOENCA=?, NM_DOENCA=?, NM_CID=?, DS_HISTORICO=?, DS_SINTOMAS=?, DS_HEREDITARIEDADE=? where CD_DOENCA = ?";

    private PoolConexoes poolConexoes;
    private CacheObjetos cache;



    private static DBDoenca instancia = null;
    /**
    * Construtor da Classe
    */
    public DBDoenca() {
        poolConexoes = PoolConexoes.getInstancia();
        cache = CacheObjetos.getInstancia();
    }

   /**
    * Retorna uma Instancia do banco
    *
    * @return BDDoenca <code>BDDoenca</code>
    */
    public static synchronized DBDoenca getInstancia(){
      if (instancia == null) {
          instancia = new DBDoenca();
      }
      return instancia;
    }

   /**
    * Retorna todas as doencas do banco
    *
    * @return List  <code>List</code>
    */
    public List getTodasDoencas() throws PersistenceException{
      Doenca doenca = null;
      PreparedStatement statement= null;
      List listaDoencas = new LinkedList();
      Connection conexao = null;
      Object obj;

      try {
        conexao =  poolConexoes.obterConexao();
        statement =  conexao.prepareStatement(getTodasDoencas);
        ResultSet resultSet = statement.executeQuery();
        while(resultSet.next()) {
          doenca = getDoenca(resultSet, conexao);
          listaDoencas.add(doenca);
        }
      } catch (SQLException excecao) {
        throw new PersistenceException(excecao.getMessage());
      } catch (NullArgumentException ex) {
        throw new PersistenceException(ex);
      }finally {
        if(conexao != null) {
          try {
            poolConexoes.liberarConexao(conexao);
          } catch (SQLException excecao) {
            throw new PersistenceException(excecao.getMessage());
          }
        }
      }
      return listaDoencas;
    }

   /**
    * Retorna uma doenca dado o OID
    *
    * @param oid   OID da doenca  <code>OID</code>
    * @return Doenca  <code>Doenca</code>
    */
   public Doenca procurar(OID oid) throws DoencaNaoCadastradaException, PersistenceException
   {
      Doenca doenca = null;
      Connection conexao = null;
      Object obj;

      try {
        conexao =  poolConexoes.obterConexao();
        obj = cache.pegarObjeto(oid);
        if(obj == null){
          doenca = procurar(oid, conexao);
          cache.inserirObjeto(oid, doenca);
          System.out.println("Coloquei Objeto: ("+((Doenca) cache.pegarObjeto(oid))+") na cache!");
        } else if(obj instanceof Doenca) {
          System.out.println("Busquei o Objeto: ("+((Doenca) obj).getNome()+") da cache!");
          doenca = (Doenca) obj;
        } else {
          throw new PersistenceException("Objeto da cache nao eh uma instacia "+
                                         "de Doenca - OID invalido");
        }
      } 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 doenca;
    }

   /**
    * Retorna uma doenca dado o OID e uma conexao
    *
    * @param oid       OID da doenca  <code>OID</code>
    * @param conexao  Conexao com o Banco <code>Connection</code>
    * @return Doenca  <code>Doenca</code>
    */
    private Doenca procurar(OID oid, Connection conexao) throws PersistenceException,
    DoencaNaoCadastradaException
    {
        PreparedStatement statement= null;
        ResultSet resultSet = null;
        Doenca doenca = null;
        try {
            statement =  conexao.prepareStatement(procurarDoencaPeloCodigoSQL);
            statement.setLong(1, Long.parseLong(oid.toString()));
            resultSet = statement.executeQuery();

            if(resultSet.next()) {
                doenca = getDoenca(resultSet,conexao);
            } else {
                throw new DoencaNaoCadastradaException ("O oid buscado não foi encontrado no sistema, oid = " + oid+"." );
            }
      } catch (SQLException excecao) {
        excecao.printStackTrace();
        throw new PersistenceException(excecao.getMessage());
      } catch (NullArgumentException ex){
        ex.printStackTrace();
        throw new PersistenceException(ex.getMessage());
      } finally {
        try{
          if(statement != null) statement.close();
          if(resultSet != null) resultSet.close();
        }catch(SQLException excecao) {
          excecao.printStackTrace();
          throw new PersistenceException(excecao.getMessage());
        }
      }
      return doenca;
    }

   /**
    * Retorna uma Doenca montada a partir de um ResultSet
    *
    * @return Doenca <code>Doenca</code>
    */
    private Doenca getDoenca(ResultSet resultSet, Connection conexao)
        throws SQLException, NullArgumentException, PersistenceException {
      Doenca doenca;

      OID oid = new OID(resultSet.getLong("CD_DOENCA"));
      String nome = resultSet.getString("NM_DOENCA");
      doenca = new Doenca(oid, nome);
      doenca.setCID(resultSet.getString("NM_CID"));
      doenca.setHistorico(resultSet.getString("DS_HISTORICO"));
      doenca.setSintomas(resultSet.getString("DS_SINTOMAS"));
      doenca.setHereditariedade(resultSet.getString("DS_HEREDITARIEDADE"));
      List drogasRelacionadas = getDrogasRelacionadas(oid,conexao);
      doenca.setDrogasRelacionadas(drogasRelacionadas);

      return doenca;
    }

   /**
    * Retorna uma Lista com todos os OIDS das Drogas Relacioanas a Doenca
    *
    * @return List (de OIDs) <code>List</code>
    */
    private List getDrogasRelacionadas(OID oid,Connection con) throws PersistenceException{
      List drogasRelacionadas = new LinkedList();
      PreparedStatement pst = null;
      ResultSet rs = null;
      RepositorioDroga cadastroDrogas = null;

      try{
        pst = con.prepareStatement(getDrogasRelacionadas);
        pst.setLong(1,oid.getLongValue());
        rs = pst.executeQuery();
        cadastroDrogas = DBDroga.getInstancia();
        while(rs.next()){
          //Guarda uma lista de OIDS de Drogas
          drogasRelacionadas.add(new OID(rs.getLong("CD_DROGA")));
        }
      }catch(SQLException ex){
        ex.printStackTrace();
        throw new PersistenceException(ex);
      }  finally{
        try{
          if(rs!=null) rs.close();
          if(pst!=null) pst.close();
        }catch(SQLException excecao) {
          excecao.printStackTrace();
          throw new PersistenceException(excecao.getMessage());
        }
      }
      return drogasRelacionadas;
    }

   /**
    * Retorna um OID, dado um nome e uma conexao
    *
    * @param nome     nome da Doenca  <code>String</code>
    * @param conexao  Conexao com o Banco <code>Connection</code>
    * @return oid  <code>OID</code>
    */
    private OID getOID(String nome, Connection conexao) throws SQLException {
        PreparedStatement statement = null;
        OID oid = null;
        try {
            statement =  conexao.prepareStatement(getOIDSQL);
            statement.setString(1, nome);
            ResultSet resultSet = statement.executeQuery();

            if(resultSet.next()) {
                oid = new OID(resultSet.getLong("CD_DOENCA"));
            }
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
        return oid;
    }

    private OID getOIDPeloCID(String cid, Connection conexao) throws SQLException {
        PreparedStatement statement = null;
        OID oid = null;
        try {
            statement =  conexao.prepareStatement(getOIDSQLPeloCID);
            statement.setString(1, cid);
            ResultSet resultSet = statement.executeQuery();
            if(resultSet.next()) {
                oid = new OID(resultSet.getLong("CD_DOENCA"));
            }
        } finally {
            if(statement != null) {
              statement.close();
            }
        }
        return oid;
    }

   /**
    * Retorna mais de uma doenca dado o nome
    *
    * @param nome   Nome da doenca <code>String</code>
    * @return Lista de Doencas  <code>List</code>
    */
//   public List procurarPeloNome(String nome)
//   {
//   return null;
//   }

   /**
    * Retorna mais de uma doenca dado o CID
    *
    * @param cid   CID da doenca <code>CID</code>
    * @return Lista de Doencas  <code>List</code>
    */
   public Doenca procurarPeloCID(String cid) throws DoencaNaoCadastradaException,
   PersistenceException
   {
      Connection conexao = null;
      Doenca doenca = null;
      Object obj;

      try {
        conexao =  poolConexoes.obterConexao();

        OID oid = getOIDPeloCID(cid, conexao);
        if(oid == null) {
          throw new DoencaNaoCadastradaException("oid = nulo");
        }
        obj = cache.pegarObjeto(oid);
        if(obj == null){
          doenca = procurar(oid, conexao);
          cache.inserirObjeto(oid, doenca);
          System.out.println("Coloquei Objeto: ("+((Doenca) cache.pegarObjeto(oid)).getNome()+") na cache!");
        } else if(obj instanceof Doenca) {
          doenca = (Doenca) obj;
          System.out.println("Busquei o Objeto: ("+((Doenca) obj).getNome()+") da cache!");
        } else {
          throw new PersistenceException("Objeto da cache nao eh uma instacia de Doenca");
        }
      } 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 doenca;
    }

    public Doenca procurarPeloNome(String nome) throws PersistenceException,
    DoencaNaoCadastradaException
    {
      Connection conexao = null;
      Doenca doenca = null;
      Object obj;

      try {
        conexao =  poolConexoes.obterConexao();

        OID oid = getOID(nome, conexao);
        if(oid == null) {
          throw new DoencaNaoCadastradaException("oid = nulo");
        }
        obj = cache.pegarObjeto(oid);
        if(obj == null){
          doenca = procurar(oid, conexao);
          cache.inserirObjeto(oid, doenca);
          System.out.println("Coloquei Objeto: ("+((Doenca) cache.pegarObjeto(oid)).getNome()+") na cache!");
        } else if(obj instanceof Doenca) {
          doenca = (Doenca) obj;
          System.out.println("Busquei o Objeto: ("+((Doenca) obj).getNome()+") da cache!");
        } else {
          throw new PersistenceException("Objeto da cache nao eh uma instacia de Doenca");
        }
      } 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 doenca;
    }

   /**
    * Retorna uma Lista com todas as Doencas que comecam com um dado Nome
    *
    * @return List (de Doencas) <code>List</code>
    */
    public List procurarPeloNomeParecido(String nome, int start, int size) throws PersistenceException{
      Connection conexao = null;
      PreparedStatement pstmt = null;
      Doenca doencaTemp = null;
      ResultSet rs = null;
      List resultado = new LinkedList();
      try {
        conexao =  poolConexoes.obterConexao();
        pstmt = conexao.prepareStatement(PROCURAR_PELO_NOME_PARECIDO_SQL);
        pstmt.setString(1,"%"+nome+"%");
        rs = pstmt.executeQuery();
        while(rs.next()){
          doencaTemp = getDoenca(rs,conexao);
          resultado.add(doencaTemp);
        }
      } catch (SQLException excecao) {
        throw new PersistenceException(excecao);
      } catch(NullArgumentException ex){
        throw new PersistenceException(ex);
      }finally {
        if(conexao != null) {
          try {
            if(pstmt != null) pstmt.close();
            poolConexoes.liberarConexao(conexao);
          } catch (SQLException excecao) {
            throw new PersistenceException(excecao);
          }
        }
      }
      if(resultado.size() > start){
        int end = java.lang.Math.min(resultado.size(),start+size);
        return resultado.subList(start,end);
      }
      else
        return new LinkedList();
    }

   /**
    * Retorna mais de uma doenca dado a categoria do CID
    *
    * @param categoriaCID   Categoria do CID <code>String</code>
    * @return Lista de Doencas  <code>List</code>
    */
   public List procurarPelaCategoriaCID(String categoriaCID)
   {
   return null;
   }

   /**
    * Retorna mais de uma doenca dado a subcategoria do CID
    *
    * @param subcategoriaCID   Subcategoria do CID <code>String</code>
    * @return Lista de Doencas  <code>List</code>
    */
   public List procurarPelaSubcategoriaCID(String subcategoriaCID)
   {
   return null;
   }

   /**
    * Insere uma Doenca
    *
    * @param doenca   A doenca a ser inserida <code>Doenca</code>
    * @exception DoencaJaCadastradaException
    */
    public OID inserir(Doenca doenca) throws DoencaJaCadastradaException,
          PersistenceException{
      Connection conexao = null;
      OID oid = null;
      Object obj = null;
      Doenca doencaJaCadastrada = null;
      PreparedStatement stmt_Doenca = null;
      PreparedStatement stmt_Doenca_Droga = null;

      if(doenca == null)
        throw new PersistenceException("Tentou cadastrar uma Doenca nula (doenca == null)");
      System.out.println("vou procurar um nome ou cid igual no banco...");
      try {
        doencaJaCadastrada = procurarPeloNome(doenca.getNome());
      }catch (DoencaNaoCadastradaException ex){
        try{
          doencaJaCadastrada = procurarPeloCID(doenca.getCID());
        }catch (DoencaNaoCadastradaException ex2){
          //Sinal de que não existe outra com o mesmo nome ou mesmo CID...
        }
      }
      if(doencaJaCadastrada != null){
        throw new DoencaJaCadastradaException("Doenca: "+doencaJaCadastrada.getNome()+", ja cadastrada");
      }
      try{
        conexao = poolConexoes.obterConexao();
        conexao.setAutoCommit(false);

        //Obter novo OID da fábrica
        OIDFactory fabrica = OIDFactory.getInstancia();
        oid = fabrica.novoOID();
        doenca.setOID(oid);

        //Inserir a doença no banco
        stmt_Doenca =  conexao.prepareStatement(insereDoencaSQL);
        stmt_Doenca.setLong(1, doenca.getId().getLongValue());
        stmt_Doenca.setString(2, doenca.getNome());
        stmt_Doenca.setString(3, doenca.getCID());
        stmt_Doenca.setString(4, doenca.getHistorico());
        stmt_Doenca.setString(5, doenca.getSintomas());
        stmt_Doenca.setString(6, doenca.getHereditariedade());
        stmt_Doenca.executeUpdate();

        //Doencas Relacionadas:
        stmt_Doenca_Droga =  conexao.prepareStatement(insereDoencaDrogaSQL);
        Iterator iter = doenca.getDrogasRelacionadas().iterator();
        while (iter.hasNext()) {
          OID oidDroga = (OID) iter.next();
          if(oidDroga == null) {
            conexao.rollback();
            throw new PersistenceException("Droga com OID não cadastrado cadastrada!");
          }
          stmt_Doenca_Droga.setLong(1, doenca.getId().getLongValue());
          stmt_Doenca_Droga.setLong(2, oidDroga.getLongValue());
          stmt_Doenca_Droga.executeUpdate();
          System.out.println("Inseriu em Droga_Doenca -> DrogaRel = "+oidDroga.getLongValue());
        }
        cache.inserirObjeto(oid, doenca);
      } catch (SQLException excecao) {
        excecao.printStackTrace();
        if(oid != null) cache.invalidarObjeto(oid);
        try {
           conexao.rollback();
        }catch (SQLException sqle1) {
           sqle1.printStackTrace();
        }
        throw new PersistenceException(excecao);
      } catch (NullArgumentException ex){
        ex.printStackTrace();
        throw new PersistenceException(ex);
      } finally {
        if(conexao != null) {
          try {
            if (stmt_Doenca_Droga != null) stmt_Doenca_Droga.close();
            if (stmt_Doenca != null) stmt_Doenca.close();
            conexao.commit();
            conexao.setAutoCommit(true);
            poolConexoes.liberarConexao(conexao);
          } catch (SQLException excecao) {
            throw new PersistenceException(excecao.getMessage());
          }
        }
      }
      return oid;
    }

   /**
    * Remove uma Doenca
    *
    * @param doenca   A doenca a ser Removida <code>Doenca</code>
    * @exception DoencaJaCadastradaException
    */
    public void remover(Doenca doenca) throws
          PersistenceException, DoencaNaoCadastradaException {

      Connection conexao = null;
      PreparedStatement stmt_remove_drogas = null;
      PreparedStatement statement = null;
      OID oid = doenca.getId();

      if(oid == null)
        throw new PersistenceException("doenca com OID == null, no remover!");

      try {
        conexao = poolConexoes.obterConexao();
        conexao.setAutoCommit(false);

        statement =  conexao.prepareStatement(removeDoencaSQL);
        statement.setLong(1, oid.getLongValue());
        statement.executeUpdate();

        //parte de doenças relacionadas...
        stmt_remove_drogas = conexao.prepareStatement(removeDoencaDrogaSQL);
        stmt_remove_drogas.setLong(1,oid.getLongValue());
        stmt_remove_drogas.executeUpdate();

        //remove da Cache
        cache.invalidarObjeto(oid);
        System.out.println("Doenca com OID = "+oid+" removida.");
      } catch (SQLException excecao) {
          throw new PersistenceException(excecao);
      } finally {
        if(conexao != null) {
          try {
            if(statement != null) statement.close();
            if(stmt_remove_drogas != null) stmt_remove_drogas.close();
            conexao.commit();
            conexao.setAutoCommit(true);
            poolConexoes.liberarConexao(conexao);
          } catch (SQLException excecao) {
            throw new PersistenceException(excecao);
          }
        }
      }
    }

   /**
    * Altera uma Doenca ja cadastrada
    *
    * @param doenca   A doenca a ser alterada <code>Doenca</code>
    * @exception DoencaNaoCadastradaException
    */
    public void alterar(Doenca doenca, Doenca newDoenca) throws
    PersistenceException, DoencaNaoCadastradaException
    {
      Connection conexao = null;
      PreparedStatement statement = null;
      PreparedStatement stmt_Doenca_Droga = null;
      PreparedStatement stmt_remove_drogas = null;
      OID oid = doenca.getId();

      if(procurar(oid) == null){
        throw new DoencaNaoCadastradaException();
      }
      try {
        conexao =  poolConexoes.obterConexao();
        conexao.setAutoCommit(false);

        statement =  conexao.prepareStatement(alteraDoencaSQL);
        statement.setLong(1, Long.parseLong(oid.toString()));
        statement.setString(2, newDoenca.getNome());
        statement.setString(3, newDoenca.getCID());
        statement.setString(4, newDoenca.getHistorico());
        statement.setString(5, newDoenca.getSintomas());
        statement.setString(6, newDoenca.getHereditariedade());
        statement.setLong(7, oid.getLongValue());
        statement.executeUpdate();

        //parte de doenças relacionadas...
        //Remove primeiro...
        stmt_remove_drogas = conexao.prepareStatement(removeDoencaDrogaSQL);
        stmt_remove_drogas.setLong(1,oid.getLongValue());
        stmt_remove_drogas.executeUpdate();

        //depois insere
        stmt_Doenca_Droga =  conexao.prepareStatement(insereDoencaDrogaSQL);
        stmt_Doenca_Droga.setLong(1, oid.getLongValue());
        Iterator iter = newDoenca.getDrogasRelacionadas().iterator();
        while (iter.hasNext()) {
          OID oidDroga = (OID) iter.next();
          stmt_Doenca_Droga.setLong(2, oidDroga.getLongValue());
          stmt_Doenca_Droga.executeUpdate();
          System.out.println("Inseriu em Droga_Doenca -> DrogaRel = "+oidDroga.getLongValue());
        }
        //insere na Cache
        cache.inserirObjeto(oid, newDoenca);
        System.out.println("Doenca com OID = "+oid.getLongValue()+" alterada com sucesso!");
      } catch (SQLException excecao) {
          throw new PersistenceException(excecao.getMessage());
      } finally {
        if(conexao != null) {
          try {
            if(statement != null) statement.close();
            if(stmt_Doenca_Droga != null) stmt_Doenca_Droga.close();
            if(stmt_remove_drogas != null) stmt_remove_drogas.close();
            conexao.commit();
            conexao.setAutoCommit(true);
            poolConexoes.liberarConexao(conexao);
          } catch (SQLException excecao) {
            throw new PersistenceException(excecao);
          }
        }
      }
    }
}

/*
    //Acho que era pra retornar uma lista de OIDS dado um sintoma.
    private List getOIDs(String sintomas, Connection conexao) throws SQLException {
      PreparedStatement statement = null;
      List oids = new LinkedList();
      try {
        statement =  conexao.prepareStatement(getOIDSQL);
        statement.setString(1, "DS_SINTOMAS");
        statement.setString(2, sintomas);
        ResultSet resultSet = statement.executeQuery();
        oids = getOIDs(resultSet);
      } finally {
        if(statement != null) {
          statement.close();
        }
      }
      return oids;
    }

    private List getOIDs (ResultSet resultSet) throws SQLException{
      List oids = new LinkedList();
      while(resultSet.next()) {
        OID oid = new OID(resultSet.getLong("CD_DOENCA"));
        oids.add(oid);
      }
      return oids;
    }
*/
