/*
 * Copyright (C) 2014 Robert Simonovsky
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package cz.cas.lib.proarc.common.export.desa.structure;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.yourmediashelf.fedora.client.FedoraClient;
import com.yourmediashelf.fedora.client.FedoraClientException;
import com.yourmediashelf.fedora.client.request.GetDatastreamDissemination;
import com.yourmediashelf.fedora.generated.foxml.DatastreamType;

import cz.cas.lib.proarc.common.export.desa.Const;
import cz.cas.lib.proarc.common.export.mets.FileMD5Info;
import cz.cas.lib.proarc.common.export.mets.MetsExportException;
import cz.cas.lib.proarc.common.export.mets.MetsUtils;
import cz.cas.lib.proarc.common.fedora.FoxmlUtils;
import cz.cas.lib.proarc.desa.SIP2DESATransporter;
import cz.cas.lib.proarc.desa.pspsip.PSPSIP;
import cz.cas.lib.proarc.desa.pspsip.PSPSIP.SIP;
import cz.cas.lib.proarc.mets.DivType;
import cz.cas.lib.proarc.mets.DivType.Fptr;
import cz.cas.lib.proarc.mets.DivType.Mptr;
import cz.cas.lib.proarc.mets.FileType;
import cz.cas.lib.proarc.mets.FileType.FLocat;
import cz.cas.lib.proarc.mets.MdSecType;
import cz.cas.lib.proarc.mets.MdSecType.MdWrap;
import cz.cas.lib.proarc.mets.MdSecType.MdWrap.XmlData;
import cz.cas.lib.proarc.mets.Mets;
import cz.cas.lib.proarc.mets.MetsType.FileSec;
import cz.cas.lib.proarc.mets.MetsType.FileSec.FileGrp;
import cz.cas.lib.proarc.mets.StructMapType;
import cz.cas.lib.proarc.mods.ModsDefinition;
import cz.cas.lib.proarc.nsesss2.Spis;
import cz.cas.lib.proarc.oaidublincore.OaiDcType;

/**
 * Visitor class for creating mets document out of Desa objects
 *
 * @author Robert Simonovsky
 *
 */
public class DesaElementVisitor implements IDesaElementVisitor {
    private final Logger LOG = Logger.getLogger(DesaElementVisitor.class.getName());
    private int tmpFolderCount = 0;

    /**
     * Archives the list of files to a zip archive
     *
     * @param zipFileName
     * @param fileList
     * @param desaElement
     * @throws MetsExportException
     */
    private void zip(String zipFileName, ArrayList<File> fileList, IDesaElement desaElement) throws MetsExportException {
        try {
            File file = new File(zipFileName);
            if (file.exists()) {
                file.delete();
                file = null;
                LOG.log(Level.FINE, "File:" + zipFileName + " exists, so it was deleted");
            }
            ZipFile zipFile = new ZipFile(zipFileName);
            ZipParameters zip4jZipParameters = new ZipParameters();
            zip4jZipParameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
            zip4jZipParameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
            zipFile.addFiles(fileList, zip4jZipParameters);
            LOG.log(Level.FINE, "Zip archive created:" + zipFileName + " for " + desaElement.getElementType());
        } catch (ZipException e) {
            throw new MetsExportException(desaElement.getOriginalPid(), "Unable to create a zip file:" + zipFileName, false, e);
        }

    }

    /**
     * Returns a identifier from BIBLIO-MODS stream
     *
     * @param desaElement
     * @return
     * @throws MetsExportException
     */
    private String getIdentifier(IDesaElement desaElement) throws MetsExportException {
        LOG.log(Level.FINE, "Element model:" + desaElement.getModel());
        if (Const.DC_URI.equals(desaElement.getDescriptorType())) {
            LOG.log(Level.FINE, "DC variant descriptor in BIBLIO MODS for " + desaElement.getOriginalPid());
            Document dcDoc = MetsUtils.getDocumentFromList(desaElement.getDescriptor());
            List<String> validationErrors;
            try {
                validationErrors = MetsUtils.validateAgainstXSD(dcDoc, OaiDcType.class.getResourceAsStream("dc_oai.xsd"));
            } catch (Exception ex) {
                throw new MetsExportException(desaElement.getOriginalPid(), "Error while validating DC document in BIBLIO_MODS for:" + desaElement.getOriginalPid() + "(" + desaElement.getElementType() + ")", false, ex);
            }

            if (validationErrors.size() > 0) {
                MetsExportException metsException = new MetsExportException(desaElement.getOriginalPid(), "Invalid DC in BIBLIO_MODS for:" + desaElement.getOriginalPid() + "(" + desaElement.getElementType() + ")", false, null);
                metsException.getExceptions().get(0).setValidationErrors(validationErrors);
                throw metsException;
            }
            List<Element> descriptor = desaElement.getDescriptor();
            return MetsUtils.xPathEvaluateString(descriptor, "*[local-name()='dc']/*[local-name()='identifier']");
        }

        if (Const.NSESSS_URI.equals(desaElement.getDescriptorType())) {
            LOG.log(Level.FINE, "NSESS variant descriptor in BIBLIO MODS for " + desaElement.getOriginalPid());
            Document nsessDoc = MetsUtils.getDocumentFromList(desaElement.getDescriptor());
            List<String> validationErrors;
            try {
                validationErrors = MetsUtils.validateAgainstXSD(nsessDoc, Spis.class.getResourceAsStream("nsesss2.xsd"));
            } catch (Exception ex) {
                throw new MetsExportException(desaElement.getOriginalPid(), "Error while validating NSESSS document in BIBLIO_MODS for:" + desaElement.getOriginalPid() + "(" + desaElement.getElementType() + ")", false, ex);
            }
            if (validationErrors.size() > 0) {
                MetsExportException metsException = new MetsExportException(desaElement.getOriginalPid(), "Invalid NSESSS in BIBLIO_MODS for:" + desaElement.getOriginalPid() + "(" + desaElement.getElementType() + ")", false, null);
                metsException.getExceptions().get(0).setValidationErrors(validationErrors);
                throw metsException;
            }

            List<Element> descriptor = desaElement.getDescriptor();
            if (Const.FOLDER.equalsIgnoreCase(desaElement.getElementType())) {
                return MetsUtils.xPathEvaluateString(descriptor, "*[local-name()='Spis']/*[local-name()='EvidencniUdaje']/*[local-name()='Identifikace']/*[local-name()='Identifikator']");
            }
            if (Const.DOCUMENT.equalsIgnoreCase(desaElement.getElementType())) {
                return MetsUtils.xPathEvaluateString(descriptor, "*[local-name()='Dokument']/*[local-name()='EvidencniUdaje']/*[local-name()='Identifikace']/*[local-name()='Identifikator']");
            }
            throw new MetsExportException(desaElement.getOriginalPid(), "Element not DOCUMENT or FOLDER:" + desaElement.getElementType(), false, null);
        }
        throw new MetsExportException(desaElement.getOriginalPid(), "Unable to get Identifier - DER/DES descriptor missing - " + desaElement.getModel(), false, null);
    }

    /**
     * Returns a label for LOGICAL div from DC stream
     *
     * @param desaElement
     * @return
     * @throws MetsExportException
     */
    private String getLabel(IDesaElement desaElement) throws MetsExportException {
        LOG.log(Level.FINE, "Element model:" + desaElement.getModel());
        if (MetsUtils.xPathEvaluateNode(desaElement.getDescriptor(), "*[namespace-uri()='http://www.openarchives.org/OAI/2.0/oai_dc/']") != null) {
            LOG.log(Level.FINE, "DC variant descriptor in BIBLIO MODS for " + desaElement.getOriginalPid());
            List<Element> descriptor = desaElement.getDescriptor();
            return MetsUtils.xPathEvaluateString(descriptor, "*[local-name()='dc']/*[local-name()='title']");
        }
        if (MetsUtils.xPathEvaluateNode(desaElement.getDescriptor(), "*[namespace-uri()='http://www.mvcr.cz/nsesss/v2']") != null) {
            LOG.log(Level.FINE, "NSESS variant descriptor in BIBLIO MODS for " + desaElement.getOriginalPid());
            List<Element> descriptor = desaElement.getDescriptor();
            if (Const.FOLDER.equalsIgnoreCase(desaElement.getElementType())) {
                return MetsUtils.xPathEvaluateString(descriptor, "*[local-name()='Spis']/*[local-name()='EvidencniUdaje']/*[local-name()='Identifikace']/*[local-name()='Identifikator']");
            }
            if (Const.DOCUMENT.equalsIgnoreCase(desaElement.getElementType())) {
                return MetsUtils.xPathEvaluateString(descriptor, "*[local-name()='Dokument']/*[local-name()='EvidencniUdaje']/*[local-name()='Identifikace']/*[local-name()='Identifikator']");
            }
            throw new MetsExportException(desaElement.getOriginalPid(), "Element not DOCUMENT or FOLDER:" + desaElement.getElementType(), false, null);
        }
        throw new MetsExportException(desaElement.getOriginalPid(), "Unable to get Label - DER/DES descriptor missing - " + desaElement.getModel(), false, null);
    }

    /**
     * Creates a temporary folder
     *
     * @param desaElement
     * @return
     * @throws MetsExportException
     */
    private File createTempFolder(IDesaElement desaElement) throws MetsExportException {
        File tmpFileFolder = null;
        try {
            tmpFileFolder = File.createTempFile("tmp" + MetsUtils.removeNonAlpabetChars(desaElement.getElementID() + "_" + tmpFolderCount), ".tmp");
            tmpFolderCount++;
            tmpFileFolder.delete();
            tmpFileFolder = new File(tmpFileFolder.getAbsolutePath());
        } catch (IOException e) {
            throw new MetsExportException(desaElement.getOriginalPid(), "Unable to create a temp folder", false, e);
        }
        tmpFileFolder.mkdir();
        LOG.log(Level.FINE, "TMP folder:" + tmpFileFolder.getAbsolutePath() + " created for:" + desaElement.getOriginalPid() + "(" + desaElement.getElementType() + ")");
        return tmpFileFolder;
    }

    /**
     * Prepares the generic mets information
     *
     * @param desaElement
     * @return
     * @throws MetsExportException
     */
    private Mets prepareMets(IDesaElement desaElement) throws MetsExportException {
        Mets mets = new Mets();
        MdSecType mdSecType = new MdSecType();
        mdSecType.setID("DM_0001");
        MdWrap mdWrap = new MdWrap();
        mdWrap.setMDTYPE("OTHER");
        mdWrap.setMIMETYPE("text/xml");
        XmlData xmlData = new XmlData();
        xmlData.getAny().addAll(desaElement.getDescriptor());
        mdWrap.setXmlData(xmlData);
        mdSecType.setMdWrap(mdWrap);
        mets.getDmdSec().add(mdSecType);
        StructMapType structMapType = new StructMapType();
        structMapType.setTYPE("LOGICAL");
        mets.getStructMap().add(structMapType);
        return mets;
    }

    /**
     * Returns a file name of original file in RAW datastream
     *
     * @param desaElement
     * @return
     * @throws MetsExportException
     */
    private String getFileName(IDesaElement desaElement) throws MetsExportException {
        String node = MetsUtils.xPathEvaluateString(desaElement.getRelsExt(), "*[local-name()='RDF']/*[local-name()='Description']/*[local-name()='importFile']");
        return node;
    }

    /**
     * Saves the mets document into a file
     *
     * @param mets
     * @param outputFile
     * @throws MetsExportException
     */
    private void saveMets(Mets mets, File outputFile, IDesaElement desaElement) throws MetsExportException {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(Mets.class, OaiDcType.class, ModsDefinition.class);
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
            marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.w3.org/2001/XMLSchema-instance http://www.w3.org/2001/XMLSchema.xsd http://www.loc.gov/METS/ http://www.loc.gov/standards/mets/mets.xsd http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/mods.xsd http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd");
            marshaller.marshal(mets, outputFile);
        } catch (Exception ex) {
            throw new MetsExportException(desaElement.getOriginalPid(), "Unable to save mets file:" + outputFile.getAbsolutePath(), false, ex);
        }
        List<String> validationErrors;
        try {
            validationErrors = MetsUtils.validateAgainstXSD(outputFile, Mets.class.getResourceAsStream("mets.xsd"));
        } catch (Exception ex) {
            throw new MetsExportException("Error while validating Mets file: " + outputFile, false, ex);
        }
        if (validationErrors.size() > 0) {
            MetsExportException metsException = new MetsExportException("Error while validating Mets file:" + outputFile, false, null);
            metsException.getExceptions().get(0).setValidationErrors(validationErrors);
            throw metsException;
        }
        LOG.log(Level.FINE, "Element validated:" + desaElement.getOriginalPid() + "(" + desaElement.getElementType() + ")");
    }

    /**
     * Adds a file to the apropriate fileGroup
     *
     * @param fileGrpMap
     * @param desaElement
     * @param fileType
     * @throws MetsExportException
     */
    private void addFiletoFileGrp(Map<String, FileGrp> fileGrpMap, IDesaElement desaElement, FileType fileType) throws MetsExportException {
        String type = MetsUtils.xPathEvaluateString(desaElement.getDescriptor(), "*[local-name()='dc']/*[local-name()='type']");
        if (type == null) {
            throw new MetsExportException(desaElement.getOriginalPid(), "Element type is missing", false, null);
        }

        if (type.length() == 0) {
            throw new MetsExportException(desaElement.getOriginalPid(), "Type of component is empty", false, null);
        }

        String fileGrpType = Const.fileGrpMap.get(type);
        if (fileGrpType == null) {
            throw new MetsExportException(desaElement.getOriginalPid(), "Unable to find mapping for file type:" + type, false, null);
        }
        fileGrpMap.get(fileGrpType).getFile().add(fileType);
    }

    /**
     * Adds all non-empty filegroups to the mets
     *
     * @param fileGrpMap
     * @param fileSec
     */
    private void addFileGrpToMets(Map<String, FileGrp> fileGrpMap, FileSec fileSec) {
        int order = 0;
        for (String key : fileGrpMap.keySet()) {
            FileGrp fileGrp = fileGrpMap.get(key);
            if (fileGrp.getFile().size() > 0) {
                order++;
                fileGrp.setID("GRP_000" + order);
                fileSec.getFileGrp().add(fileGrp);
            }
        }
    }

    /**
     *
     * Prepares a map of all FileGroups
     *
     * @return
     */
    private HashMap<String, FileGrp> prepareFileGrpMap() {
        HashMap<String, FileGrp> grpMap = new HashMap<String, FileGrp>();
        String[] uses = { Const.ORIGINAL, Const.PREVIEW, Const.DIGITIZED, Const.MIGRATED, Const.INPUT };
        for (String use : uses) {
            FileGrp fileGrp = new FileGrp();
            fileGrp.setUSE(use);
            grpMap.put(use, fileGrp);
        }
        return grpMap;
    }

    /**
     * Inserts a document element into a mets
     *
     * @param desaElement
     * @param suffix
     * @throws MetsExportException
     */
    private void insertDocument(IDesaElement desaElement, String suffix) throws MetsExportException {
        Map<String, FileGrp> fileGrpMap = prepareFileGrpMap();
        if (suffix == null) {
            suffix = "0001";
        }
        File tmpFolder = createTempFolder(desaElement);
        try {
            File outputMets = new File(tmpFolder.getAbsolutePath() + File.separator + "mets.xml");
            Mets mets = prepareMets(desaElement);
            DivType divType = new DivType();
            divType.setLabel(getLabel(desaElement));
            divType.setTYPE("record");
            divType.getDMDID().add(mets.getDmdSec().get(0));
            ArrayList<File> fileList = new ArrayList<File>();
            StructMapType structMapType = mets.getStructMap().get(0);
            structMapType.setDiv(divType);

            // if no files are present, then skip
            if (desaElement.getChildren().size() > 0) {
                FileSec fileSec = new FileSec();
                mets.setFileSec(fileSec);
                int fileOrder = 0;
                for (DesaElement fileElement : desaElement.getChildren()) {
                    fileOrder++;
                    DatastreamType rawDS = FoxmlUtils.findDatastream(fileElement.getSourceObject(), "RAW");
                    byte[] fileContent = new byte[0];
                    String mimeType = "empty";
                    if (rawDS != null) {
                        if (rawDS.getDatastreamVersion().get(0).getContentLocation() != null) {
                            if ("INTERNAL_ID".equals(rawDS.getDatastreamVersion().get(0).getContentLocation().getTYPE())) {
                                if (desaElement.getDesaContext().getFedoraClient() == null) {
                                    throw new MetsExportException(fileElement.getOriginalPid(), "Datastream dissemination allowed only for Fedora storage", false, null);
                                }
                                desaElement.getDesaContext().getFedoraClient();
                                GetDatastreamDissemination dsRaw = FedoraClient.getDatastreamDissemination(fileElement.getOriginalPid(), "RAW");
                                try {
                                    InputStream is = dsRaw.execute(desaElement.getDesaContext().getFedoraClient()).getEntityInputStream();
                                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                                    try {
                                        MetsUtils.copyStream(is, bos);
                                        bos.close();
                                        fileContent = bos.toByteArray();
                                    } catch (IOException e) {
                                        throw new MetsExportException(desaElement.getOriginalPid(), "Unable to copy raw datastream content", false, e);
                                    }
                                } catch (FedoraClientException e) {
                                    throw new MetsExportException(desaElement.getOriginalPid(), "Unable to read raw datastream content", false, e);
                                }
                            } else {
                                throw new MetsExportException(fileElement.getOriginalPid(), "Expecting INTERNAL_ID type in ContentLocation - found:" + rawDS.getDatastreamVersion().get(0).getContentLocation().getTYPE(), false, null);
                            }
                        } else {
                            fileContent = rawDS.getDatastreamVersion().get(0).getBinaryContent();
                        }
                        mimeType = rawDS.getDatastreamVersion().get(0).getMIMETYPE();
                    } else {
                        desaElement.getDesaContext().getMetsExportException().addException(fileElement.getOriginalPid(), "RAW datastream is missing", false, null);
                        continue;
                    }
                    FileOutputStream fos;
                    String outputFileName = MetsUtils.removeNonAlpabetChars(getFileName(fileElement));
                    /*
                     * Generates a filename if it's not provided from the
                     * original document
                     */
                    if ((outputFileName == null) || (outputFileName.trim().length() == 0)) {
                        outputFileName = "file_" + String.format("%04d", fileOrder) + "." + MetsUtils.getMimeToExtension().getProperty(mimeType);
                        LOG.log(Level.INFO, "importFile name was not specified for:" + fileElement.getOriginalPid() + " new name was generated:" + outputFileName);
                    }
                    String fullOutputFileName = tmpFolder.getAbsolutePath() + File.separator + outputFileName;
                    try {
                        File outputFile = new File(fullOutputFileName);
                        fos = new FileOutputStream(outputFile);
                        fileList.add(outputFile);
                    } catch (FileNotFoundException e) {
                        throw new MetsExportException(fileElement.getOriginalPid(), "Unable to create a temp file:" + fullOutputFileName, false, e);
                    }
                    FileMD5Info fileMd5Info;
                    try {
                        fileMd5Info = MetsUtils.getDigestAndCopy(new ByteArrayInputStream(fileContent), fos);
                    } catch (NoSuchAlgorithmException e) {
                        throw new MetsExportException(fileElement.getOriginalPid(), "Unable to generate MD5 digest", false, e);
                    } catch (IOException e) {
                        throw new MetsExportException(fileElement.getOriginalPid(), "Unable to save file", false, e);
                    }
                    FileType fileType = new FileType();
                    String validIdentifier = MetsUtils.validateIdentifier(getIdentifier(desaElement) + "_" + String.format("%04d", fileOrder));
                    fileType.setID(validIdentifier);
                    fileType.setCHECKSUMTYPE("MD5");
                    fileType.setCHECKSUM(fileMd5Info.getMd5());
                    FLocat flocat = new FLocat();
                    flocat.setLOCTYPE("URL");
                    flocat.setHref(outputFileName);
                    fileType.getFLocat().add(flocat);
                    addFiletoFileGrp(fileGrpMap, fileElement, fileType);
                    Fptr fptr = new Fptr();
                    fptr.setFILEID(fileType);
                    divType.getFptr().add(fptr);
                }
                addFileGrpToMets(fileGrpMap, fileSec);
            }
            saveMets(mets, outputMets, desaElement);
            fileList.add(outputMets);
            desaElement.setZipName(MetsUtils.removeNonAlpabetChars(getIdentifier(desaElement) + "_" + suffix));
            String zipFileName = desaElement.getDesaContext().getOutputPath() + File.separator + desaElement.getZipName() + ".zip";
            zip(zipFileName, fileList, desaElement);
        } finally {
            MetsUtils.deleteFolder(tmpFolder);
        }
        LOG.fine("Document successfuly exported");
    }

    /**
     * Insert a folder element into a mets
     *
     * @param desaElement
     * @throws MetsExportException
     */
    private void insertFolder(IDesaElement desaElement) throws MetsExportException {
        Mets mets = prepareMets(desaElement);
        DivType divType = new DivType();
        divType.setTYPE("file");
        divType.setLabel(getLabel(desaElement));
        divType.getDMDID().add(mets.getDmdSec().get(0));
        StructMapType structMapType = mets.getStructMap().get(0);
        structMapType.setDiv(divType);
        int documentOrder = 0;
        for (DesaElement documentElement : desaElement.getChildren()) {
            documentOrder++;
            insertDocument(documentElement, String.format("%04d", documentOrder));
            DivType documentDiv = new DivType();
            documentDiv.setTYPE("record");
            documentDiv.setLabel(getLabel(documentElement));
            documentDiv.setORDER(BigInteger.valueOf(documentOrder));
            Mptr mptr = new Mptr();
            mptr.setLOCTYPE("OTHER");
            if (Const.NSESSS_URI.equals(documentElement.getDescriptorType())) {
                mptr.setOTHERLOCTYPE("ERMS_ID");
            } else {
                mptr.setOTHERLOCTYPE("internal_reference");
            }

            mptr.setHref(getIdentifier(documentElement));
            documentDiv.getMptr().add(mptr);
            divType.getDiv().add(documentDiv);
        }
        File tmpFolder = createTempFolder(desaElement);
        try {
            File outputMets = new File(tmpFolder.getAbsolutePath() + File.separator + "mets.xml");
            saveMets(mets, outputMets, desaElement);
            desaElement.setZipName(MetsUtils.removeNonAlpabetChars(getIdentifier(desaElement)) + "_FILE");
            String zipFileName = desaElement.getDesaContext().getOutputPath() + File.separator + desaElement.getZipName() + ".zip";
            ArrayList<File> fileList = new ArrayList<File>();
            fileList.add(outputMets);
            zip(zipFileName, fileList, desaElement);
        } finally {
            MetsUtils.deleteFolder(tmpFolder);
        }

        LOG.fine("Folder successfuly exported");
    }

    /**
     * Updates desaElements with the IdSIPVersion from DESA transport
     *
     * @param desaElement
     * @param transportResult
     */
    private void updateSIPversion(IDesaElement desaElement, PSPSIP transportResult) {
        for (DesaElement desaElementChild : desaElement.getChildren()) {
            updateSIPversion(desaElementChild, transportResult);
        }

        if (desaElement.getZipName() != null) {
            for (SIP sip : transportResult.getSIP()) {
                if (sip.getIdentifier().equals(desaElement.getZipName())) {
                    desaElement.setIdSIPVersion(sip.getIdSIPVersion());
                    break;
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see cz.cas.lib.proarc.common.export.desa.structure.IDesaElementVisitor#
     * insertIntoMets
     * (cz.cas.lib.proarc.common.export.desa.structure.IDesaElement)
     */
    @Override
    public void insertIntoMets(IDesaElement desaElement, HashMap<String, String> desaProps) throws MetsExportException {
        LOG.log(Level.FINE, "Inserting into Mets:" + desaElement.getOriginalPid() + "(" + desaElement.getElementType() + ")");
        // get root element first
        IDesaElement rootElement = desaElement.getDesaContext().getRootElement();
        if (rootElement == null) {
            throw new MetsExportException("Element does not have a root set:" + desaElement.getModel() + " - " + desaElement.getOriginalPid(), false);
        }
        if (Const.DOCUMENT.equalsIgnoreCase(rootElement.getElementType())) {
            insertDocument(rootElement, null);
        } else if (Const.FOLDER.equalsIgnoreCase(rootElement.getElementType())) {
            insertFolder(rootElement);
        } else
            throw new MetsExportException(rootElement.getOriginalPid(), "Unknown type:" + rootElement.getElementType() + " model:" + rootElement.getModel(), false, null);

        SIP2DESATransporter sipTransporter = desaElement.getDesaContext().getTransporter();
        if (sipTransporter != null) {
            LOG.log(Level.INFO, "Exporting to desa");
            try {
                String desaResultPath = rootElement.getDesaContext().getDesaResultPath();
                if (desaResultPath != null) {
                    sipTransporter.transport(rootElement.getDesaContext().getOutputPath(), desaResultPath, desaResultPath);
                    updateSIPversion(rootElement, sipTransporter.getResults());
                } else {
                    throw new MetsExportException(desaElement.getOriginalPid(), "Result dir (desa.resultDir) is not set", false, null);
                }
            } catch (Exception ex) {
                throw new MetsExportException(desaElement.getOriginalPid(), "Unable to transport mets to desa", false, ex);
            }
        }
    }
}