/* BdfServer_DirectoryStorage - Copyright (c) 2006-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.BalayageStorage;
import fr.exemole.bdfserver.api.storage.BdfStorageException;
import fr.exemole.bdfserver.api.storage.StorageContent;
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.BalayageStorageUnitBuilder;
import fr.exemole.bdfserver.tools.storage.StorageFile;
import fr.exemole.bdfserver.tools.storage.StorageUtils;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.EditOrigin;
import net.fichotheque.Fichotheque;
import net.fichotheque.exportation.balayage.BalayageDef;
import net.fichotheque.tools.exportation.balayage.BalayageDefBuilder;
import net.fichotheque.tools.exportation.balayage.dom.BalayageDefDOMReader;
import net.fichotheque.tools.exportation.balayage.dom.OldBalayageDOMReader;
import net.fichotheque.xml.defs.BalayageDefXMLPart;
import net.mapeadores.util.io.IOUtils;
import net.mapeadores.util.logging.LogUtils;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.xml.AppendableXMLWriter;
import net.mapeadores.util.xml.XMLUtils;
import org.w3c.dom.Document;


/**
 *
 * @author Vincent Calame
 */
public class BalayageStorageImpl implements BalayageStorage {

    private final Fichotheque fichotheque;
    private final StorageDirectory balayageDirectory;

    public BalayageStorageImpl(Fichotheque fichotheque, BdfServerDirs dirs) {
        this.fichotheque = fichotheque;
        this.balayageDirectory = StorageDirectoryUtils.toStorageDirectory(dirs, "balayagedata");
    }

    @Override
    public BalayageStorage.Unit[] checkStorage() {
        SortedMap<String, BalayageStorage.Unit> map = new TreeMap<String, BalayageStorage.Unit>();
        File dataDir = balayageDirectory.getDataDir();
        if ((dataDir.exists()) && (dataDir.isDirectory())) {
            for (File f : dataDir.listFiles()) {
                if (f.isDirectory()) {
                    String name = f.getName();
                    if (testName(name)) {
                        map.put(name, buildUnit(name, f));
                    }
                }
            }
        }
        return map.values().toArray(new Unit[map.size()]);
    }

    @Override
    public Unit getBalayageStorageUnit(String balayageName) {
        if (!testName(balayageName)) {
            return null;
        }
        File varDir = balayageDirectory.getDataFile(balayageName);
        if (varDir.exists()) {
            if (!varDir.isDirectory()) {
                return null;
            }
            return buildUnit(balayageName, varDir);
        }
        return null;
    }

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

    @Override
    public void saveStorageContent(String balayageName, String contentPath, InputStream inputStream, EditOrigin editOrigin) throws IOException {
        if (!testName(balayageName)) {
            throw new IllegalArgumentException(balayageName + " is not a technical name");
        }
        contentPath = checkPath(contentPath);
        if (contentPath == null) {
            throw new IllegalArgumentException(contentPath + " is not a relative path");
        }
        StorageFile storageFile = getStorageFile(balayageName, contentPath);
        try (OutputStream os = storageFile.archiveAndGetOutputStream(editOrigin)) {
            IOUtils.copy(inputStream, os);
        }
    }

    @Override
    public boolean removeStorageContent(String balayageName, String contentPath, EditOrigin editOrigin) {
        if (!testName(balayageName)) {
            return false;
        }
        contentPath = checkPath(contentPath);
        if (contentPath == null) {
            return false;
        }
        StorageFile storageFile = getStorageFile(balayageName, contentPath);
        if (!storageFile.exists()) {
            return false;
        }
        storageFile.archiveAndDelete(editOrigin);
        return true;
    }

    @Override
    public boolean removeBalayage(String balayageName, EditOrigin editOrigin) {
        if (!testName(balayageName)) {
            return false;
        }
        StorageFile balayageDir = balayageDirectory.getStorageFile(balayageName);
        if ((!balayageDir.exists()) || (!balayageDir.isDirectory())) {
            return false;
        }
        StorageDirectoryUtils.archiveAndDeleteDirectory(balayageDir, editOrigin);
        return true;
    }

    @Override
    public void saveBalayageDef(BalayageDef balayageDef, EditOrigin editOrigin) {
        StorageFile storageFile = getStorageFile(balayageDef.getName(), "_def.xml");
        try (BufferedWriter writer = storageFile.archiveAndGetWriter(editOrigin)) {
            AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(writer);
            xmlWriter.appendXMLDeclaration();
            BalayageDefXMLPart.init(xmlWriter)
                    .addBalayageDef(balayageDef);
        } catch (IOException ioe) {
            throw new BdfStorageException(ioe);
        }
    }

    private BalayageStorage.Unit buildUnit(String balayageName, File balayageDir) {
        BalayageStorageUnitBuilder balayageStorageUnitBuilder = new BalayageStorageUnitBuilder();
        BalayageDefBuilder balayageDefBuilder = new BalayageDefBuilder(balayageName);
        boolean withDef = false;
        boolean withOldBalayage = false;
        for (File file : balayageDir.listFiles()) {
            String fileName = file.getName();
            if (fileName.equals("_def.xml")) {
                withDef = true;
                StorageFile defFile = getStorageFile(balayageName, fileName);
                Document doc = defFile.readDocument();
                BalayageDefDOMReader.init(fichotheque, balayageDefBuilder, LogUtils.NULL_MESSAGEHANDLER)
                        .read(doc.getDocumentElement());
            } else if (file.isDirectory()) {
                balayageStorageUnitBuilder.populate(file, "");
            } else if (fileName.equals("balayage.xml")) {
                withOldBalayage = true;
                balayageStorageUnitBuilder.addStorageContent(file.getName(), file);
            } else {
                balayageStorageUnitBuilder.addStorageContent(file.getName(), file);
            }
        }
        if ((!withDef) && (withOldBalayage)) {
            StorageFile oldBalayageFile = getStorageFile(balayageName, "balayage.xml");
            try {
                Document doc = oldBalayageFile.readDocument();
                OldBalayageDOMReader.init(fichotheque, balayageDefBuilder, LogUtils.NULL_MESSAGEHANDLER)
                        .read(doc.getDocumentElement());
            } catch (BdfStorageException bse) {

            }
        }
        balayageStorageUnitBuilder.setBalayageDef(balayageDefBuilder.toBalayageDef());
        return balayageStorageUnitBuilder.toBalayageStorageUnit();
    }

    private StorageFile getStorageFile(String balayageName, String contentPath) {
        String path = balayageName + File.separator + contentPath;
        return balayageDirectory.getStorageFile(path);
    }

    private static boolean testName(String s) {
        return StringUtils.isTechnicalName(s, true);
    }

    private static String checkPath(String path) {
        path = StringUtils.normalizeRelativePath(path);
        if (path == null) {
            return null;
        }
        return path;
    }

}
