/**
 * 
 */
package it.laitspa.cpf.faces.utils;

import it.gov.fatturapa.sdi.fatturapa.v1.FatturaElettronicaType;
import it.laitspa.cpf.faces.utils.versionefattura.VersioneFatturaPaHelper;
import it.laitspa.cpf.util.log.LogUtils;
import it.laitspa.cpf.util.security.SignatureVerifier;
import it.laitspa.xsd.resolver.XmlMarshaller;
import org.apache.commons.io.FilenameUtils;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * @author andrea
 * 
 */
public class SogeiArchiveParser
{

  private String        extension;
  private byte[]        data;
  private StringBuilder sb;
  private ArrayList<InputStream> xsd;

  public SogeiArchiveParser(String extension, byte[] data)
  {
    this.extension = extension;
    this.data = data;
    this.sb = new StringBuilder();
  }

  public StringBuilder getSb()
  {
    return sb;
  }

  public void setSb(StringBuilder sb)
  {
    this.sb = sb;
  }

  public List<FatturaElettronicaType> parse()
  {
    List<FatturaElettronicaType> fatture = new ArrayList<FatturaElettronicaType>();

    if(extension.contains("xml"))
    {
      LogUtils.info(getClass(), "Parsing SDI xml file.");
        FatturaElettronicaType fattura = null;
        try {
            fattura = parseInvoice(data);
        } catch (IOException e) {
            LogUtils.error(getClass(), "Exception caugth while parsing invoice.", e);
            return null;
        } catch (SAXException e) {
            LogUtils.error(getClass(), "Exception caugth while parsing invoice.", e);
            return null;
        } catch (ParserConfigurationException e) {
            LogUtils.error(getClass(), "Exception caugth while parsing invoice.", e);
            return null;
        }
        if (fattura != null)
        fatture.add(fattura);
    }
    else if(extension.contains("p7m"))
    {
      LogUtils.info(getClass(), "Parsing SDI p7m file.");
      byte[] content = retrieveUnsignedContent(data);
      if(content == null)
      {
        LogUtils.error(getClass(),
            "Exception caught while extracting XML content from SDI file, uncrypted data is null.");
        return null;
      }
        FatturaElettronicaType fattura = null;
        try {
            fattura = parseInvoice(content);
        } catch (IOException e) {
            LogUtils.error(getClass(), "Exception caugth while parsing invoice.", e);
            return null;
        } catch (SAXException e) {
            LogUtils.error(getClass(), "Exception caugth while parsing invoice.", e);
            return null;
        } catch (ParserConfigurationException e) {
            LogUtils.error(getClass(), "Exception caugth while parsing invoice.", e);
            return null;
        }
        if (fattura != null)
        fatture.add(fattura);
    }
    else if(extension.contains("zip"))
    {
      LogUtils.info(getClass(), "Parsing SDI Zip archive.");
      ByteArrayInputStream bis = new ByteArrayInputStream(data);
      ZipInputStream zin = new ZipInputStream(new BufferedInputStream(bis));
      try
      {
        ZipEntry entry;
        while((entry = zin.getNextEntry()) != null)
        {
          LogUtils.info(getClass(), "Parsing entry " + entry.getName());

          ByteArrayOutputStream bos = new ByteArrayOutputStream();
          byte[] buffer = new byte[512];
          int count = 0;
          while((count = zin.read(buffer, 0, buffer.length)) != -1)
          {
            bos.write(buffer, 0, count);
          }
          bos.flush();
          bos.close();

          // Set unzipped byte
          //
          byte[] unzipped = bos.toByteArray();
          String zip_ext = FilenameUtils.getExtension(entry.getName());
          if(zip_ext == null || zip_ext.trim().length() < 1 || zip_ext.toUpperCase().equals("XML"))
          {
            FatturaElettronicaType fattura = parseInvoice(unzipped);
            if (fattura != null)
              fatture.add(fattura);
          }
          else if(zip_ext.contains("p7m"))
          {
            byte[] content = retrieveUnsignedContent(unzipped);
            if(content == null)
            {
              LogUtils
                  .error(getClass(),
                      "Exception caught while extracting XML content from p7m in Zip SDI file, uncrypted data is null.");
              return null;
            }
            FatturaElettronicaType fattura = parseInvoice(content);
            if (fattura != null)
              fatture.add(fattura);
          }
        }

        zin.close();

      }
      catch(Exception e)
      {
        LogUtils.error(getClass(), "Exception caugth while iterating on zip entries.", e);
        return null;
      }
    }

    return fatture;
  }

  private byte[] retrieveUnsignedContent(byte[] signed)
  {
    byte[] content = null;
    SignatureVerifier v = new SignatureVerifier(signed);
    content = v.getContentByEncrypted();
    return content;
  }

  private FatturaElettronicaType parseInvoice(byte[] data_i) throws IOException, SAXException, ParserConfigurationException {
    XmlMarshaller<FatturaElettronicaType> mr = new XmlMarshaller<FatturaElettronicaType>();
    ByteArrayInputStream bis = new ByteArrayInputStream(data_i);
    InputStream xsd = VersioneFatturaPaHelper.getFileXSD(data_i);

    FatturaElettronicaType fattura = null;
    try
    {
      byte[] c_data = BOMUtils.removeBOM(bis);
      bis = new ByteArrayInputStream(c_data);
      mr.setBuffer(sb);
      fattura = mr.unmarshal(FatturaElettronicaType.class, bis, xsd);
    }
    catch(Exception e)
    {
      LogUtils.error(getClass(),
          "Cannot create FatturaElettronicaType from buffer inside archive.", e);
    }
    finally
    {
      try
      {
        bis.close();
      }
      catch(IOException e)
      {
        LogUtils.error(getClass(), e);
      }
    }

    return fattura;
  }

}
