/* BdfServer_DirectoryStorage - Copyright (c) 2007-2025 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package fr.exemole.bdfserver.storage.directory.implementations;

import fr.exemole.bdfserver.api.storage.BdfStorageException;
import fr.exemole.bdfserver.api.storage.StorageContent;
import fr.exemole.bdfserver.api.storage.TemplateStorage;
import fr.exemole.bdfserver.conf.BdfServerDirs;
import fr.exemole.bdfserver.storage.directory.StorageDirectory;
import fr.exemole.bdfserver.storage.directory.StorageDirectoryUtils;
import fr.exemole.bdfserver.tools.storage.StorageFile;
import fr.exemole.bdfserver.tools.storage.StorageUtils;
import fr.exemole.bdfserver.tools.storage.TemplateStorageUnitBuilder;
import fr.exemole.bdfserver.xml.transformation.TemplateDefXMLPart;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.text.ParseException;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.EditOrigin;
import net.fichotheque.exportation.transformation.TemplateDef;
import net.fichotheque.exportation.transformation.TemplateKey;
import net.fichotheque.exportation.transformation.TransformationKey;
import net.fichotheque.tools.exportation.transformation.TemplateDefBuilder;
import net.fichotheque.tools.exportation.transformation.dom.TemplateDefDOMReader;
import net.mapeadores.util.io.IOUtils;
import net.mapeadores.util.logging.LogUtils;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.text.ValidExtension;
import net.mapeadores.util.xml.AppendableXMLWriter;
import net.mapeadores.util.xml.XMLUtils;
import org.w3c.dom.Document;


/**
 *
 * @author Vincent Calame
 */
public class TemplateStorageImpl implements TemplateStorage {

    private final StorageDirectory transformationDirectory;

    public TemplateStorageImpl(BdfServerDirs dirs) {
        this.transformationDirectory = StorageDirectoryUtils.toStorageDirectory(dirs, "transformationdata");
    }

    @Override
    public Unit[] checkStorage() {
        SortedMap<TemplateKey, TemplateStorage.Unit> map = new TreeMap<TemplateKey, TemplateStorage.Unit>();
        File dataDir = transformationDirectory.getDataDir();
        if (dataDir.exists()) {
            for (File transformationDir : dataDir.listFiles()) {
                if (transformationDir.isDirectory()) {
                    String transformationName = transformationDir.getName();
                    try {
                        TransformationKey transformationKey = TransformationKey.parse(transformationName);
                        for (File templateDir : transformationDir.listFiles()) {
                            if (templateDir.isDirectory()) {
                                String templateName = templateDir.getName();
                                TemplateKey templateKey;
                                int idx = templateName.indexOf('.');
                                if (idx != -1) {
                                    try {
                                        ValidExtension validExtension = ValidExtension.parse(templateName.substring(idx + 1));
                                        templateKey = TemplateKey.parse(transformationKey, validExtension, templateName.substring(0, idx));
                                    } catch (ParseException pe) {
                                        continue;
                                    }
                                } else {
                                    try {
                                        templateKey = TemplateKey.parse(transformationKey, templateName);
                                    } catch (ParseException pe) {
                                        continue;
                                    }
                                }
                                map.put(templateKey, buildUnit(templateDir, templateKey));
                            }
                        }
                    } catch (ParseException pe) {
                    }
                }
            }
        }
        return map.values().toArray(new Unit[map.size()]);
    }

    @Override
    public Unit getTemplateStorageUnit(TemplateKey templateKey) {
        String completePath = getCompletePath(templateKey, null);
        File varDir = transformationDirectory.getDataFile(completePath);
        return buildUnit(varDir, templateKey);
    }

    @Override
    public void saveTemplateDef(TemplateDef templateDef, EditOrigin editOrigin) {
        StorageFile storageFile = getStorageFile(templateDef.getTemplateKey(), "_def.xml");
        try (BufferedWriter writer = storageFile.archiveAndGetWriter(editOrigin)) {
            AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(writer);
            xmlWriter.appendXMLDeclaration();
            TemplateDefXMLPart xmlPart = new TemplateDefXMLPart(xmlWriter);
            xmlPart.addTemplateDef(templateDef);
        } catch (IOException ioe) {
            throw new BdfStorageException(ioe);
        }
    }

    @Override
    public StorageContent getStorageContent(TemplateKey templateKey, String contentPath) {
        if (templateKey.isDist()) {
            return null;
        }
        contentPath = checkPath(contentPath);
        if (contentPath == null) {
            return null;
        }
        StorageFile storageFile = getStorageFile(templateKey, contentPath);
        if (!storageFile.exists()) {
            return null;
        }
        return StorageUtils.toStorageContent(contentPath, storageFile.getFile());
    }

    @Override
    public void saveStorageContent(TemplateKey templateKey, String contentPath, InputStream inputStream, EditOrigin editOrigin) throws IOException {
        if (templateKey.isDist()) {
            throw new IllegalArgumentException("templateKey is dist key");
        }
        contentPath = checkPath(contentPath);
        if (contentPath == null) {
            return;
        }
        StorageFile storageFile = getStorageFile(templateKey, contentPath);
        try (OutputStream os = storageFile.archiveAndGetOutputStream(editOrigin)) {
            IOUtils.copy(inputStream, os);
        }
    }

    @Override
    public boolean removeStorageContent(TemplateKey templateKey, String contentPath, EditOrigin editOrigin) {
        if (templateKey.isDist()) {
            throw new IllegalArgumentException("templateKey is default key");
        }
        contentPath = checkPath(contentPath);
        if (contentPath == null) {
            return false;
        }
        StorageFile storageFile = getStorageFile(templateKey, contentPath);
        if (!storageFile.exists()) {
            return false;
        }
        storageFile.archiveAndDelete(editOrigin);
        return true;
    }

    @Override
    public void createTemplate(Unit templateStorageDescription, EditOrigin editOrigin) throws IOException {
        String completePath = getCompletePath(templateStorageDescription.getTemplateKey(), null);
        StorageFile transformationDir = transformationDirectory.getStorageFile(completePath);
        if (transformationDir.exists()) {
            transformationDir.delete();
        }
        for (StorageContent content : templateStorageDescription.getStorageContentList()) {
            String path = content.getPath();
            path = StringUtils.normalizeRelativePath(path);
            if (path == null) {
                continue;
            }
            StorageFile storageFile = transformationDir.getSubStorageFile(path);
            try (InputStream is = content.getInputStream(); OutputStream os = storageFile.archiveAndGetOutputStream(editOrigin)) {
                IOUtils.copy(is, os);
            }
        }
        saveTemplateDef(templateStorageDescription.getTemplateDef(), editOrigin);
        StorageFile typeFile = transformationDir.getSubStorageFile("_type");
        try (Writer writer = typeFile.archiveAndGetWriter(editOrigin)) {
            writer.write(templateStorageDescription.getType());
        } catch (IOException ioe) {
            throw new BdfStorageException(ioe);
        }
    }

    @Override
    public boolean removeTemplate(TemplateKey templateKey, EditOrigin editOrigin) {
        String completePath = getCompletePath(templateKey, null);
        StorageFile transformationDir = transformationDirectory.getStorageFile(completePath);
        if ((!transformationDir.exists()) || (!transformationDir.isDirectory())) {
            return false;
        }
        StorageDirectoryUtils.archiveAndDeleteDirectory(transformationDir, editOrigin);
        return true;
    }

    private TemplateStorage.Unit buildUnit(File templateDir, TemplateKey templateKey) {
        String type = "";
        TemplateStorageUnitBuilder templateStorageUnitBuilder = new TemplateStorageUnitBuilder();
        TemplateDefBuilder templateDefBuilder = new TemplateDefBuilder(templateKey);
        for (File file : templateDir.listFiles()) {
            String fileName = file.getName();
            if (fileName.equals("_type")) {
                try (FileInputStream is = new FileInputStream(file)) {
                    type = IOUtils.toString(is, "UTF-8").trim();
                } catch (IOException ioe) {
                    throw new BdfStorageException(ioe);
                }
            } else if (fileName.equals("_def.xml")) {
                StorageFile defFile = getStorageFile(templateKey, fileName);
                Document doc = defFile.readDocument();
                TemplateDefDOMReader.init(templateDefBuilder, LogUtils.NULL_MESSAGEHANDLER)
                        .read(doc.getDocumentElement());
            } else if (file.isDirectory()) {
                templateStorageUnitBuilder.populate(file, "");
            } else {
                templateStorageUnitBuilder.addStorageContent(file.getName(), file);
            }
        }
        templateStorageUnitBuilder
                .setType(type)
                .setTemplateDef(templateDefBuilder.toTemplateDef());
        return templateStorageUnitBuilder.toTemplateStorageUnit();
    }

    private String getCompletePath(TemplateKey templateKey, String path) {
        StringBuilder buf = new StringBuilder();
        buf.append(templateKey.getTransformationKey());
        buf.append(File.separatorChar);
        buf.append(templateKey.getCompleteName());
        if (path != null) {
            buf.append(File.separatorChar);
            buf.append(path);
        }
        return buf.toString();
    }

    private StorageFile getStorageFile(TemplateKey templateKey, String path) {
        String completePath = getCompletePath(templateKey, path);
        return transformationDirectory.getStorageFile(completePath);
    }

    private static String checkPath(String path) {
        path = StringUtils.normalizeRelativePath(path);
        if (path == null) {
            return null;
        }
        switch (path) {
            case "_def.xml":
            case "_type":
                return null;
            default:
                return path;
        }
    }

}
