/*
* Copyright 2014 - LAit SpA
*				 Realizzato da Icona Management srl su commessa LAit SpA
* Copyright 2015- LAit SpA
*                                                  Aggiornato da LAit SpA
*
* Concesso in licenza a norma dell'EUPL, versione 1.1 o
* successive dell'EUPL (la "Licenza") non appena saranno
* approvate dalla Commissione europea;
* Non è possibile utilizzare l'opera salvo nel rispetto della Licenza.
* E'possibile ottenere una copia della Licenza al seguente indirizzo:
*
* http://ec.europa.eu/idabc/eupl
*
* Salvo diversamente indicato dalla legge applicabile o
* concordato per iscritto, il software distribuito secondo
* i termini della Licenza è distribuito "TAL QUALE",
* SENZA GARANZIE O CONDIZIONI DI ALCUN TIPO,
* esplicite o implicite.
* Si veda la Licenza per la lingua specifica che disciplina
* le autorizzazioni e le limitazioni secondo i termini della
* Licenza.
*/
/**
 * 
 */
package it.laitspa.cpf.spring.sogei;

import it.gov.fatturapa.sdi.fatturapa.v1.*;
import it.gov.fatturapa.sdi.messaggi.v1.MetadatiInvioFileType;
import it.laitspa.cpf.model.QueryResult;
import it.laitspa.cpf.model.sogei.evento.EventoSogei;
import it.laitspa.cpf.model.sogei.fattura.AllegatoFatturaSogei;
import it.laitspa.cpf.model.sogei.fattura.FatturaSogei;
import it.laitspa.cpf.model.sogei.fattura.FatturaSogeiDAO;
import it.laitspa.cpf.model.sogei.fattura.QueryFsByEmittente;
import it.laitspa.cpf.model.sogei.inviofilesdi.AllegatoInvioFileSdiSogei;
import it.laitspa.cpf.model.sogei.inviofilesdi.InvioFileSdiDAO;
import it.laitspa.cpf.model.sogei.inviofilesdi.InvioFileSdiSogei;
import it.laitspa.cpf.model.sogei.inviofilesdi.QueryFluxByEmittente;
import it.laitspa.cpf.model.sogei.inviofilesdi.TipoAllegatoInvioFileSdiSogei;
import it.laitspa.cpf.model.sogei.notifica.NotificaSogei;
import it.laitspa.cpf.model.sogei.notifica.NotificaSogeiDAO;
import it.laitspa.cpf.model.sogei.soggetto.CodiceIpaSogei;
import it.laitspa.cpf.model.sogei.soggetto.QSQuery;
import it.laitspa.cpf.model.sogei.soggetto.SoggettoSogei;
import it.laitspa.cpf.model.sogei.soggetto.SoggettoSogeiDAO;
import it.laitspa.cpf.model.sogei.statoinvio.StatoInvioSogei;
import it.laitspa.cpf.model.sogei.tipofattura.TipoFatturaSogei;
import it.laitspa.cpf.model.sogei.trasmissione.TrasmissioneSogei;
import it.laitspa.cpf.spring.SpringFacade;
import it.laitspa.cpf.util.log.LogUtils;
import it.laitspa.xsd.remover.FieldRemover;
import it.laitspa.xsd.resolver.XmlMarshaller;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.MessageFormat;
import java.util.*;

import javax.xml.bind.JAXBElement;

import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author andrea
 * 
 */
@Repository("SogeiFacade")
@Transactional
public class SogeiFacadeImpl
  extends SpringFacade
  implements SogeiFacade
{

  /**
   * 
   */
  @Override
  public boolean serializeLoadFatturaSogei(BigInteger identificativo, String nomeFile,
      String nomeFileMetaDati, MetadatiInvioFileType metadati, byte[] meta, byte[] xml_fattura)
  {
    throw new UnsupportedOperationException("Operazione non implementata.");
  }

 
  @Override
  public boolean serializeFatturaSogei(Integer codiceInvioSogei)
  {
    Session session = this.sessionFactory.getCurrentSession();
    InvioFileSdiDAO idao = new InvioFileSdiDAO(session);

    Date now = new Date();

    InvioFileSdiSogei flussoSogei =
        (InvioFileSdiSogei)session.load(InvioFileSdiSogei.class, codiceInvioSogei);
    flussoSogei.setDataElaborazione(now);
    session.update(flussoSogei);
    session.flush();

    // Retrieve Fattura
    //
    AllegatoInvioFileSdiSogei allegato =
        idao.retrieveAllegato(codiceInvioSogei, TipoAllegatoInvioFileSdiSogei.LOTTO_FIRMATO);
    byte[] allegato_data = allegato.getDatafile();

    // Check type of file:
    // 1 - xml
    // 2 - xml.p7m
    // 3 - zip with xml and p7m
    String extension = allegato.getEstensione();
    if(extension == null || extension.trim().length() < 1)
      extension = "xml";

    SogeiArchiveParser archive_pr = new SogeiArchiveParser(extension, allegato_data);
    List<FatturaElettronicaType> l_fatture = archive_pr.parse();
    
    StringBuilder e_buf = archive_pr.getSb();
    String errors = e_buf.toString();
    if (errors != null && errors.length() > 0)
    {
      // Send errors mail.
      //
      sendSegnalazioneForFlusso(session, flussoSogei, errors);
      
      // Return to back state
      //
      StatoInvioSogei stato = flussoSogei.getStatoInvio();
      if (stato.getCodice().equals(StatoInvioSogei.IMMESSO))
      {
        StatoInvioSogei statoInAttesa = (StatoInvioSogei) session.load(StatoInvioSogei.class, StatoInvioSogei.IN_ATTESA_FIRMA);
        flussoSogei.setStatoInvio(statoInAttesa);
        session.update(flussoSogei);
      }
      return false;
    }
    
    if(l_fatture == null)
      return false;

    SogeiLoader loader = SogeiLoaderFactory.getLoader(flussoSogei, session, l_fatture);
    if (loader.loadSogei())
    {
      // Send ok mail.
      //
      sendSegnalazioneForFlusso(session, flussoSogei, null);
    }

    // Update flux flag.
    //
    flussoSogei.setFlagEsito(Boolean.TRUE);
    session.update(flussoSogei);
    session.flush();
    return true;
  }
  
  @Override
  public boolean serializeNotificaSogei(Integer codiceNotifica)
  {
    throw new UnsupportedOperationException("Operazione non implementata.");
  }  

  @Override
  public boolean
      notificaEsitoCommittente(Integer codiceFattura, Integer codiceInvio, Boolean esito)
  {
    Session session = this.sessionFactory.getCurrentSession();
    SogeiServiceWrapper sw = new SogeiServiceWrapper(session);
    return sw.notificaEsitoCommittente(codiceFattura, codiceInvio, esito);
  }
  
  @Override
  public void receiveNotificaEsito(BigInteger identificativoSdi, String nomeFile,
      byte[] data_response)
  {
    throw new UnsupportedOperationException("Operazione non implementata.");
  }   
  
  @Override
  public void receiveAttestazioneTrasmissione(BigInteger identificativoSdi, String nomeFile,
      byte[] data_response)
  {
    throw new UnsupportedOperationException("Operazione non implementata.");
  }
  
  
  @Override
  public void receiveMancataConsegna(BigInteger identificativoSdi, String nomeFile,
      byte[] data_response)
  {
    throw new UnsupportedOperationException("Operazione non implementata.");
  }  
  
  @Override
  public void receiveNotificaScarto(BigInteger identificativoSdi, String nomeFile,
      byte[] data_response)
  {
    throw new UnsupportedOperationException("Operazione non implementata.");
    
  }  

  @Override
  public void receiveRicevutaConsegna(BigInteger identificativoSdi, String nomeFile,
      byte[] data_response)
  {
    throw new UnsupportedOperationException("Operazione non implementata.");
  }
  
  @Override
  public void receiveNotificaDecorrenza(BigInteger identificativoSdi, String nomeFile,
      byte[] data_response)
  {
    throw new UnsupportedOperationException("Operazione non implementata.");
  }

  @Override
  public QueryResult listQueryResult(Integer codiceEmittente, Date dataCaricamentoDa,
      Date dataCaricamentoA, String partitaIvaCedente, String partitaIvaCommittente,
      String codiceIpa, Integer codiceStato, 
      String numeroFattura, Date dataFatturaDa, Date dataFatturaA, 
      int s, int ps)
  {
    Session session = this.sessionFactory.getCurrentSession();
    QueryFsByEmittente qr = new QueryFsByEmittente(session);

    qr.setCodiceEmittente(codiceEmittente);
    qr.setDataCaricamentoA(dataCaricamentoA);
    qr.setDataCaricamentoDa(dataCaricamentoDa);
    qr.setPartitaIvaCedente(partitaIvaCedente);
    qr.setPartitaIvaCommittente(partitaIvaCommittente);
    qr.setCodiceIpa(codiceIpa);
    qr.setCodiceStato(codiceStato);
    qr.setNumeroFattura(numeroFattura);
    qr.setDataFatturaA(dataFatturaA);
    qr.setDataFatturaDa(dataFatturaDa);

    qr.setFirstResult(s);
    qr.setMaxResults(ps);

    return qr.query();
  }

  @Override
  public List<NotificaSogei> listN(Integer codice)
  {
    throw new UnsupportedOperationException("Operazione non implementata.");
  }
  
  @Override
  public List<NotificaSogei> listNF(Integer codice)
  {
    Session session = this.sessionFactory.getCurrentSession();
    String h =
        "from NotificaSogei n "
            + "left join fetch n.statoNotifica ns "
            + "left join fetch n.tipoNotifica tn " 
            + "where n.invio.codice = :codice "
            + "order by n.dataCreazione desc ";
    Query q = session.createQuery(h);
    q.setParameter("codice", codice);

    @SuppressWarnings("unchecked")
    List<NotificaSogei> list = q.list();
    LogUtils.info(getClass(), "Returned {0,number,#} items for notification.",
        new Object[] { list.size() });
    return list;
  }  
  
  @Override
  public List<EventoSogei> listEv(Integer codice)
  {
    Session session = this.sessionFactory.getCurrentSession();
    String h =
        "from EventoSogei n "
            + "left join fetch n.statoLotto st "
            + "where n.invio.codice = :codice "
            + "order by n.codice desc ";
    Query q = session.createQuery(h);
    q.setParameter("codice", codice);

    @SuppressWarnings("unchecked")
    List<EventoSogei> list = q.list();
    LogUtils.info(getClass(), "Returned {0,number,#} events for flux_id {1,number,#} .",
        new Object[] { list.size(), codice});
    return list;
  }   

  @Override
  public byte[] createX(Integer codice)
  {
    Session session = this.sessionFactory.getCurrentSession();
    FatturaSogei f = (FatturaSogei)session.load(FatturaSogei.class, codice);
    AllegatoFatturaSogei a = f.getAllegato();
    return a.getDatafile();
  }

  protected Object resolveXmlLabel(String lut, String column, String descrizione)
  {
    Session session = this.sessionFactory.getCurrentSession();

    String hql = "from " + lut + " where " + column + " = :descrizione";

    Query q = session.createQuery(hql);
    q.setParameter("descrizione", descrizione);

    Object result = (Object)q.uniqueResult();
    return result;
  }

  protected boolean checkDataFattura(Date dataFattura, Date dataImmissione)
  {
    GregorianCalendar gc = new GregorianCalendar();
    gc.setTime(dataFattura);
    gc.set(Calendar.HOUR_OF_DAY, 0);
    gc.set(Calendar.MINUTE, 0);
    gc.set(Calendar.SECOND, 0);
    gc.set(Calendar.MILLISECOND, 0);
    Date date = gc.getTime();

    gc = new GregorianCalendar(2009, 0, 1);
    if(date.before(gc.getTime()))
      return false;

    gc.setTime(dataImmissione);
    gc.set(Calendar.HOUR_OF_DAY, 0);
    gc.set(Calendar.MINUTE, 0);
    gc.set(Calendar.SECOND, 0);
    gc.set(Calendar.MILLISECOND, 0);
    if(date.after(gc.getTime()))
      return false;

    return true;
  }

  @Override
  public InvioFileSdiSogei getNextToBeLoaded()
  {
    Session session = this.sessionFactory.getCurrentSession();
    InvioFileSdiDAO idao = new InvioFileSdiDAO(session);
    return idao.getNextToBeLoaded();
  }
  
  @Override
  public NotificaSogei getNextNotificaToBeLoaded()
  {
    Session session = this.sessionFactory.getCurrentSession();
    NotificaSogeiDAO idao = new NotificaSogeiDAO(session);
    return idao.getNextToBeLoaded();
  }

  @Override
  public SoggettoSogei retrieveSoggettoForUtente(Integer codiceUtente)
  {
    Session session = this.sessionFactory.getCurrentSession();
    SoggettoSogeiDAO sdao = new SoggettoSogeiDAO(session);
    return sdao.retrieveSoggettoForUtente(codiceUtente);
  }

  @Override
  public void serializeCaricamentoFlusso(Integer codiceFlusso, Integer codiceSogettoEmittente,
      FatturaElettronicaType fattura)
  {
    serializeCaricamentoFlusso(codiceFlusso, codiceSogettoEmittente, fattura, StatoInvioSogei.IN_ATTESA_FIRMA);
  }
  
  @Override
  public Integer serializeCaricamentoFlusso(Integer codiceFlusso, Integer codiceSogettoEmittente,
      FatturaElettronicaType fattura, Integer codiceStato)
  {

    Session session = this.sessionFactory.getCurrentSession();

    // Retrieve stato.
    //
    StatoInvioSogei statoInvio =
        (StatoInvioSogei)session.load(StatoInvioSogei.class, codiceStato);

    FatturaElettronicaHeaderType header = fattura.getFatturaElettronicaHeader();

    // Clean object.
    //
    FieldRemover<FatturaElettronicaType, String> fr =
        new FieldRemover<FatturaElettronicaType, String>(FatturaElettronicaType.class, String.class);


      List<FatturaElettronicaBodyType> bodyArray = fattura.getFatturaElettronicaBody();
      Iterator<FatturaElettronicaBodyType> bodyIterator = bodyArray.iterator();

      while(bodyIterator.hasNext()){
          FatturaElettronicaBodyType currentBody = bodyIterator.next();
          List<ScontoMaggiorazioneType> smtList = currentBody.getDatiGenerali().getDatiGeneraliDocumento().getScontoMaggiorazione();
          Iterator<ScontoMaggiorazioneType> iSconti = smtList.iterator();
          while(iSconti.hasNext()){
              ScontoMaggiorazioneType smt = iSconti.next();
              if(smt.getImporto() == null || smt.getImporto().equals(BigDecimal.ZERO)){
                  smt.setImporto(null);
              }
              if(smt.getPercentuale() == null || smt.getPercentuale().equals(BigDecimal.ZERO)){
                  smt.setPercentuale(null);
              }
          }

          ArrayList<DettaglioLineeType> lineeList = (ArrayList<DettaglioLineeType>)currentBody.getDatiBeniServizi().getDettaglioLinee();
          Iterator<DettaglioLineeType> iLinee = lineeList.iterator();
          while ((iLinee.hasNext())) {
              DettaglioLineeType currentLinea = iLinee.next();
              BigDecimal quantita = currentLinea.getQuantita();

              if(quantita == null || quantita.equals(BigDecimal.ZERO)) {
                  currentLinea.setQuantita(null);
              }

              if (currentLinea.getAltriDatiGestionali() == null){

                  AltriDatiGestionaliType adgtQ = new AltriDatiGestionaliType();
                  adgtQ.setTipoDato("QUANTITA");
                  adgtQ.setRiferimentoNumero(quantita);

                  AltriDatiGestionaliType adgtUM = new AltriDatiGestionaliType();
                  adgtUM.setTipoDato("UM");
                  adgtUM.setRiferimentoTesto(currentLinea.getUnitaMisura());

                  AltriDatiGestionaliType adgtPU = new AltriDatiGestionaliType();
                  adgtPU.setTipoDato("PREZUNIT");
                  adgtPU.setRiferimentoNumero(currentLinea.getPrezzoUnitario());

                  List<AltriDatiGestionaliType> listAdgtScontiMaggiorazioni = new ArrayList<AltriDatiGestionaliType>();
                  List<ScontoMaggiorazioneType> listScontiMaggiorazioni = currentLinea.getScontoMaggiorazione();
                  Iterator<ScontoMaggiorazioneType> iScontiMaggiorazioni = listScontiMaggiorazioni.iterator();
                  while (iScontiMaggiorazioni.hasNext()) {
                      ScontoMaggiorazioneType scontoMaggiorazione = iScontiMaggiorazioni.next();
                      AltriDatiGestionaliType adgtScontoMaggiorazione = new AltriDatiGestionaliType();

                      String tipo = "";
                      if (scontoMaggiorazione.getTipo().equals(TipoScontoMaggiorazioneType.SC))
                          tipo = "SC";
                      else if (scontoMaggiorazione.getTipo().equals(TipoScontoMaggiorazioneType.MG))
                          tipo = "MG";

                      if(scontoMaggiorazione.getImporto() == null || scontoMaggiorazione.getImporto().equals(BigDecimal.ZERO)){
                          scontoMaggiorazione.setImporto(null);
                      }

                      if(scontoMaggiorazione.getPercentuale() == null || scontoMaggiorazione.getPercentuale().equals(BigDecimal.ZERO)){
                          scontoMaggiorazione.setPercentuale(null);
                      }

                      adgtScontoMaggiorazione.setTipoDato(tipo);
                      if (quantita != null && !quantita.equals(BigDecimal.ZERO) && scontoMaggiorazione.getImporto() != null)
                          adgtScontoMaggiorazione.setRiferimentoNumero(scontoMaggiorazione.getImporto().divide(quantita));
                      else
                          adgtScontoMaggiorazione.setRiferimentoNumero(scontoMaggiorazione.getImporto());
                      listAdgtScontiMaggiorazioni.add(adgtScontoMaggiorazione);
                  }

                  currentLinea.setQuantita(null);
                  currentLinea.setUnitaMisura(null);
                  currentLinea.setPrezzoUnitario(currentLinea.getPrezzoTotale());

                  currentLinea.setScontoMaggiorazione(null);

                  currentLinea.getAltriDatiGestionali().add(adgtQ);
                  currentLinea.getAltriDatiGestionali().add(adgtUM);
                  currentLinea.getAltriDatiGestionali().add(adgtPU);
                  currentLinea.getAltriDatiGestionali().addAll(listAdgtScontiMaggiorazioni);
              }
              else {
                  List<ScontoMaggiorazioneType> listScontiMaggiorazioni = currentLinea.getScontoMaggiorazione();
                  Iterator<ScontoMaggiorazioneType> iScontiMaggiorazioni = listScontiMaggiorazioni.iterator();
                  while (iScontiMaggiorazioni.hasNext()) {
                      ScontoMaggiorazioneType scontoMaggiorazione = iScontiMaggiorazioni.next();
                      if (scontoMaggiorazione.getImporto() == null || scontoMaggiorazione.getImporto().equals(BigDecimal.ZERO)) {
                          scontoMaggiorazione.setImporto(null);
                      }

                      if (scontoMaggiorazione.getPercentuale() == null || scontoMaggiorazione.getPercentuale().equals(BigDecimal.ZERO)) {
                          scontoMaggiorazione.setPercentuale(null);
                      }
                  }
              }
          }
      }

    fr.removeEmptyFields(fattura);

    CedentePrestatoreType prestatore = header.getCedentePrestatore();
    IdFiscaleType id_fiscale = prestatore.getDatiAnagrafici().getIdFiscaleIVA();

    CessionarioCommittenteType committente = header.getCessionarioCommittente();
    IdFiscaleType id_committente = committente.getDatiAnagrafici().getIdFiscaleIVA();
    String cessionario = null;
    if(id_committente != null)
      cessionario = id_committente.getIdCodice();
    else
      cessionario = committente.getDatiAnagrafici().getCodiceFiscale();

    DatiTrasmissioneType dt = header.getDatiTrasmissione();
    IdFiscaleType id_trasmittente = dt.getIdTrasmittente();

    // Find allegato.
    //
    InvioFileSdiDAO sdao = new InvioFileSdiDAO(session);
    InvioFileSdiSogei invio = null;
    AllegatoInvioFileSdiSogei allegato = null;
    TrasmissioneSogei tr = null;

    // Retrieve emittente.
    //
    SoggettoSogei emittente =
        (SoggettoSogei)session.load(SoggettoSogei.class, codiceSogettoEmittente);

    if(codiceFlusso != null)
    {
      invio = (InvioFileSdiSogei)session.load(InvioFileSdiSogei.class, codiceFlusso);
      allegato =
          sdao.retrieveAllegato(codiceFlusso, TipoAllegatoInvioFileSdiSogei.LOTTO_NON_FIRMATO);
      tr = sdao.retrieveTrasmissioneForFlux(codiceFlusso);
    }
    else
    {
      invio = new InvioFileSdiSogei();

      // Building new
      //
      String hql =
          "select max(coalesce(i.progressivo, 0)) "
              + "from TrasmissioneSogei tr "
              + "inner join tr.invio i "
              + "where tr.soggettoCedente.codice = :codiceSoggetto ";

      Query q = session.createQuery(hql);
      q.setParameter("codiceSoggetto", codiceSogettoEmittente);

      Integer count = (Integer)q.uniqueResult();
      Integer progressivo = null;
      if(count != null)
        progressivo = count.intValue() + 1;
      else
        progressivo = 1;

      invio.setProgressivo(progressivo);

      String s_progressivo = formatToAlphaNumeric(invio.getProgressivo(), 5);

      // Rebuild filename.
      //
      String fileName =
          MessageFormat.format("{0}{1}_{2}.xml", new Object[] {
              id_trasmittente.getIdPaese(), id_trasmittente.getIdCodice(), s_progressivo });

      invio.setPrestatoreCedente(id_fiscale.getIdCodice());
      invio.setCessionarioCommittente(cessionario);
      invio.setNomeFile(fileName);
      invio.setCodiceDestinatario(dt.getCodiceDestinatario());
      invio.setStatoInvio(statoInvio);

      session.save(invio);

      // Generate new trasmissione.
      //
      tr = new TrasmissioneSogei();
      tr.setCodiceIdentificativoFiscale(id_trasmittente.getIdCodice());
      tr.setNazione(id_trasmittente.getIdPaese());
      tr.setFormatoTrasmissione(FormatoTrasmissioneType.FPA_12.value());
      tr.setProgressivoInvio(s_progressivo);
      tr.setCodiceDestinatario(dt.getCodiceDestinatario());
      tr.setInvio(invio);
      tr.setSoggettoCedente(emittente);

      session.save(tr);

      // Create allegato
      //
      TipoAllegatoInvioFileSdiSogei tipo =
          (TipoAllegatoInvioFileSdiSogei)session.load(TipoAllegatoInvioFileSdiSogei.class,
              TipoAllegatoInvioFileSdiSogei.LOTTO_NON_FIRMATO);

      allegato = new AllegatoInvioFileSdiSogei();
      allegato.setTipoAllegato(tipo);
      allegato.setNomeFile(fileName);
      allegato.setEstensione("xml");
      allegato.setFile(invio);
    }

    // Rebuild info's
    //
    invio.setPrestatoreCedente(id_fiscale.getIdCodice());
    invio.setCessionarioCommittente(cessionario);
    String s_progressivo = formatToAlphaNumeric(invio.getProgressivo(), 5);

    // Insert progressivo.
    //
    fattura.getFatturaElettronicaHeader().getDatiTrasmissione().setProgressivoInvio(s_progressivo);

    // Unmarshall
    //
    XmlMarshaller<FatturaElettronicaType> mr = new XmlMarshaller<FatturaElettronicaType>();
    ByteArrayOutputStream bos = null;
    byte[] data = null;
    try
    {
      bos = new ByteArrayOutputStream();
      ObjectFactory obj = new ObjectFactory();
      JAXBElement<FatturaElettronicaType> jaxb_element = obj.createFatturaElettronica(fattura);
      mr.writeDocument(jaxb_element, bos);
      data = bos.toByteArray();
      LogUtils.info(getClass(), "Jaxb element successfully unmarshalled.");
    }
    catch(Exception e)
    {
      LogUtils.error(getClass(), "Exception caught while unmarshalling object.", e);
    }
    finally
    {
      if(bos != null)
        try
        {
          bos.close();
        }
        catch(IOException e)
        {
          LogUtils.error(getClass(), "Exception caught while closing stream.", e);
        }
    }

    // Rebuild filename.
    //
    String fileName =
        MessageFormat.format("{0}{1}_{2}.xml", new Object[] {
            id_trasmittente.getIdPaese(), id_trasmittente.getIdCodice(), s_progressivo });

    invio.setNomeFile(fileName);
    invio.setCodiceDestinatario(dt.getCodiceDestinatario());
    invio.setStatoInvio(statoInvio);

    // Rebuild tr info's
    //
    tr.setCodiceIdentificativoFiscale(id_trasmittente.getIdCodice());
    tr.setNazione(id_trasmittente.getIdPaese());
    tr.setFormatoTrasmissione(fattura.getVersione().value());
    tr.setProgressivoInvio(s_progressivo);
    tr.setCodiceDestinatario(dt.getCodiceDestinatario());

    // Update allegato.
    //
    allegato.setDatafile(data);

    // Save invio.
    //
    session.saveOrUpdate(invio);

    // Save tr
    //
    session.saveOrUpdate(tr);

    // Save allegato
    //
    session.saveOrUpdate(allegato);

    if (codiceFlusso == null)
    {
      String descrizione = "Flusso correttamente inserito sul sistema";
      if (codiceStato.equals(StatoInvioSogei.IMMESSO))
        descrizione += " tramite web services";
      descrizione += ".";
      sdao.saveEventoFlusso(invio, descrizione);
    }
    else
      sdao.saveEventoFlusso(codiceFlusso, "Aggiornamento flusso eseguito con successo.");

    session.flush();

    LogUtils.info(getClass(), "serializeCaricamentoFlusso successfully performed.");

    return invio.getCodice();
  }

  @Override
  public QueryResult listFluxByCodiceEmittente(Integer codiceEmittente, Date dataCaricamentoDa,
      Date dataCaricamentoA, String partitaIvaCedente, String partitaIvaCommittente,
      String codiceIpa, int startRow, int pageSize)
  {
    Session session = this.sessionFactory.getCurrentSession();
    QueryFluxByEmittente qr = new QueryFluxByEmittente(session);

    qr.setCodiceEmittente(codiceEmittente);
    qr.setDataCaricamentoA(dataCaricamentoA);
    qr.setDataCaricamentoDa(dataCaricamentoDa);
    qr.setPartitaIvaCedente(partitaIvaCedente);
    qr.setPartitaIvaCommittente(partitaIvaCommittente);
    qr.setCodiceIpa(codiceIpa);

    qr.setFirstResult(startRow);
    qr.setMaxResults(pageSize);

    return qr.query();
  }

  private String formatToAlphaNumeric(int decimalNumber, int length)
  {
    LogUtils.info(getClass(), "Current number: " + decimalNumber);

    // To convert number to alphanumeric code.
    // We need to convert base10(decimal) to base36
    //
    String strBaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    String strTempVal = "";
    int mod = 0;
    while(decimalNumber != 0)
    {
      mod = (int)(decimalNumber % 36);
      strTempVal = strBaseDigits.substring(mod, mod + 1) + strTempVal;
      decimalNumber = decimalNumber / 36;
    }
    LogUtils.info(getClass(), "alphanumeric code generated from number : " + strTempVal);

    return it.laitspa.cpf.util.misc.StringUtils.padLeft(strTempVal, '0', length);
  }

  @Override
  public AllegatoInvioFileSdiSogei
      retrieveAllegato(Integer codiceInvio, Integer codiceTipoAllegato)
  {
    Session session = this.sessionFactory.getCurrentSession();
    InvioFileSdiDAO sdao = new InvioFileSdiDAO(session);
    return sdao.retrieveAllegato(codiceInvio, codiceTipoAllegato);
  }

  @Override
  public boolean serializeSignedAllegatoForFlux(Integer codiceFlusso, String file_name,
      String estensione, byte[] data)
  {

    Session session = this.sessionFactory.getCurrentSession();
    InvioFileSdiSogei invio =
        (InvioFileSdiSogei)session.load(InvioFileSdiSogei.class, codiceFlusso);
    StatoInvioSogei stato = invio.getStatoInvio();

    if(stato.getCodice() < StatoInvioSogei.SCARTATO)
    {
      LogUtils.warn(getClass(), "Illegal attempt to signed already sent flux for id "
          + codiceFlusso);
      return false;
    }

    StatoInvioSogei statoImmesso =
        (StatoInvioSogei)session.load(StatoInvioSogei.class, StatoInvioSogei.IMMESSO);
    TipoAllegatoInvioFileSdiSogei tipoFirmato =
        (TipoAllegatoInvioFileSdiSogei)session.load(TipoAllegatoInvioFileSdiSogei.class,
            TipoAllegatoInvioFileSdiSogei.LOTTO_FIRMATO);
    
    InvioFileSdiDAO idao = new InvioFileSdiDAO(session);
    AllegatoInvioFileSdiSogei allegato = idao.retrieveAllegato(codiceFlusso, TipoAllegatoInvioFileSdiSogei.LOTTO_NON_FIRMATO);

    // Save allegato
    //
    allegato.setTipoAllegato(tipoFirmato);
    session.update(allegato);

    invio.setStatoInvio(statoImmesso);
    invio.setDataElaborazione(null);
    invio.setFlagEsito(null);
    session.update(invio);

    LogUtils.info(getClass(), "Signed attachment successfully sent for id " + codiceFlusso);
    return true;
  }
  
  
  private void sendSegnalazioneForFlusso(Session session, InvioFileSdiSogei invio, String errors)
  {
    InvioFileSdiDAO sdao = new InvioFileSdiDAO(session);
    sdao.sendSegnalazioneForFlusso(invio, errors);
  }
  
  @Override
  public QueryResult listSoggettiByQuery(String denominazione, String cognome, String nome,
      String identificativoFiscale, String codiceFiscale, String codiceIpa, int startRow,
      int pageSize)
  {
    Session session = this.sessionFactory.getCurrentSession();
    QSQuery qr = new QSQuery(session);
    
    qr.setCodiceFiscale(codiceFiscale);
    qr.setCodiceIpa(codiceIpa);
    qr.setCognome(cognome);
    qr.setDenominazione(denominazione);
    qr.setIdentificativoFiscale(identificativoFiscale);
    qr.setNome(nome);
    
    qr.setFirstResult(startRow);
    qr.setMaxResults(pageSize);
    
    return qr.query();
  }
  
  @Override
  public boolean isCodiceIpaAlreadyPresent(String codiceIpa)
  {
    Session session = this.sessionFactory.getCurrentSession();
    String hql = "select count(*) "
        + "from CodiceIpaSogei c "
        + "where upper(c.codiceIpa) = :codiceIpa ";
    Query q = session.createQuery(hql);
    q.setParameter("codiceIpa", codiceIpa);
    
    Long count = (Long)q.uniqueResult();
    boolean res = count.intValue() > 0;
    LogUtils.info(getClass(), "isCodiceIpaAlreadyPresent returned " + res + " for code " + codiceIpa);
    return res;
  }
  
  @Override
  public void saveCodiceIpaForSoggetto(Integer codiceSoggetto, String codiceIpa)
  {
    Session session = this.sessionFactory.getCurrentSession();
    
    SoggettoSogei soggetto = (SoggettoSogei) session.load(SoggettoSogei.class, codiceSoggetto);
    
    CodiceIpaSogei codice = new CodiceIpaSogei();
    codice.setSoggetto(soggetto);
    codice.setCodiceIpa(codiceIpa);
    codice.setDataCreazione(new Date());
    
    session.save(codice);
  }  
  
  @Override
  public boolean deleteCodiceIpa(Integer codiceIpa)
  {
    Session session = this.sessionFactory.getCurrentSession();
    
    CodiceIpaSogei codice = (CodiceIpaSogei)session.load(CodiceIpaSogei.class, codiceIpa);
    
    
    String hql = "select count(*) "
        + "from InvioFileSdiSogei c "
        + "where upper(c.codiceDestinatario) = :codiceIpa ";
    Query q = session.createQuery(hql);
    q.setParameter("codiceIpa", codice.getCodiceIpa().toUpperCase());
    
    Long count = (Long)q.uniqueResult();
    boolean res = count.intValue() == 0;
    LogUtils.info(getClass(), "deleteCodiceIpa returned " + res + " for code " + codiceIpa);
    
    if (res)
    {
      session.delete(codice);
      LogUtils.info(getClass(), "CodiceIpa successfully deleted for code " + codiceIpa);
    }
    return res;
  }

  @Override
  @Transactional(readOnly=false)
  public boolean saveAnagraficaSoggetto(Integer codiceSoggetto, String nazione,
      String codiceIdentificativoFiscale, String codiceFiscale, String denominazione, String nome,
      String cognome, String descrizioneTitolare, String descrizioneCodiceEori, String mail)
  {
    Session session = this.sessionFactory.getCurrentSession();
    SoggettoSogei soggetto = (SoggettoSogei) session.get(SoggettoSogei.class, codiceSoggetto);
    boolean save = false;
    if (soggetto == null)
    {
      soggetto = new SoggettoSogei();
      soggetto.setDataCreazione(new Date());
      soggetto.setCodice(codiceSoggetto);
      save = true;
    }
    
    soggetto.setCodiceFiscale(codiceFiscale);
    soggetto.setCodiceIdentificativoFiscale(codiceIdentificativoFiscale);
    soggetto.setCognome(cognome);
    soggetto.setDenominazione(denominazione);
    soggetto.setDescrizioneCodiceEori(descrizioneCodiceEori);
    soggetto.setDescrizioneTitolare(descrizioneTitolare);
    soggetto.setMail(mail);
    soggetto.setNazione(nazione);
    soggetto.setNome(nome);
    if (save)
      session.save(soggetto);
    else
      session.update(soggetto);
    
    LogUtils.info(getClass(), "SoggettoSogei successfully saved for id " + codiceSoggetto);
    return true;
  }
  
  @Override
  public List<Integer> listLottiByStateAndDate(Date dataCreazione, Integer codiceStato)
  {
    Session session = this.sessionFactory.getCurrentSession();
    
    String hql = "select i.codice "
        + "from InvioFileSdiSogei i "
        + "where i.statoInvio.codice = :codiceStato ";
    if (dataCreazione != null)
      hql += "and i.dataCreazione >= :dataCreazione "; 
        
    Query q = session.createQuery(hql);
    q.setParameter("codiceStato", codiceStato);
    if (dataCreazione != null)
      q.setParameter("dataCreazione", dataCreazione);
      
    @SuppressWarnings("unchecked")
    List<Integer> list = q.list();
    LogUtils.info(getClass(), "listLottiByStateAndDate returned " + list.size() + " items for state " + codiceStato);
    return list;
  }
  
  @Override
  public boolean confirmLotto(Integer identificativo)
  {
    Session session = this.sessionFactory.getCurrentSession();
    InvioFileSdiSogei idao = (InvioFileSdiSogei) session.get(InvioFileSdiSogei.class, identificativo);
    if (idao == null)
      return false;
    
    StatoInvioSogei stato = idao.getStatoInvio();
    if (!stato.getCodice().equals(StatoInvioSogei.INVIATO))
      return false;
    
    StatoInvioSogei statoConfermato = (StatoInvioSogei) session.load(StatoInvioSogei.class, StatoInvioSogei.CONFERMATO);
    idao.setStatoInvio(statoConfermato);
    session.update(idao);
    
    InvioFileSdiDAO sdao = new InvioFileSdiDAO(session);
    sdao.saveEventoFlusso(idao, "Flusso prelevato tramite web services.");
    
    return true;
  }
    @Override
    public FatturaSogei getFatturaByNumero(String numeroFattura) {
              Session session = sessionFactory.getCurrentSession();
              FatturaSogeiDAO fDao = new FatturaSogeiDAO(session);
              FatturaSogei fatturaSogei = fDao.retrieveFatturaByNumero(numeroFattura, TipoFatturaSogei.FATTURA);
              return fatturaSogei;
          }


}
