/* 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.TableExportStorage;
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.TableExportStorageUnitBuilder;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.EditOrigin;
import net.fichotheque.exportation.table.TableExportDef;
import net.fichotheque.tools.exportation.table.TableExportDefBuilder;
import net.fichotheque.tools.exportation.table.dom.TableExportDefDOMReader;
import net.fichotheque.xml.defs.TableExportDefXMLPart;
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 TableExportStorageImpl implements TableExportStorage {

    private final StorageDirectory tableExportDirectory;

    public TableExportStorageImpl(BdfServerDirs dirs) {
        this.tableExportDirectory = StorageDirectoryUtils.toStorageDirectory(dirs, "tableexportdata");
    }

    @Override
    public Unit[] checkStorage() {
        SortedMap<String, TableExportStorage.Unit> map = new TreeMap<String, TableExportStorage.Unit>();
        scanTableExportDirectories(map);
        return map.values().toArray(new Unit[map.size()]);
    }

    @Override
    public Unit getTableExportStorageUnit(String tableExportName) {
        if (!testName(tableExportName)) {
            return null;
        }
        File varDir = tableExportDirectory.getDataFile(tableExportName);
        if ((varDir.exists()) && (varDir.isDirectory())) {
            return buildUnit(tableExportName);
        } else {
            return null;
        }
    }

    @Override
    public boolean saveTableExportDef(TableExportDef tableExportDef, EditOrigin editOrigin) {
        String tableExportName = tableExportDef.getName();
        if (!testName(tableExportName)) {
            return false;
        }
        StorageFile storageFile = getStorageFile(tableExportName, "_def.xml");
        try (BufferedWriter writer = storageFile.archiveAndGetWriter(editOrigin)) {
            AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(writer);
            xmlWriter.appendXMLDeclaration();
            TableExportDefXMLPart.init(xmlWriter)
                    .addTableExportDef(tableExportDef);
        } catch (IOException ioe) {
            throw new BdfStorageException(ioe);
        }
        return true;
    }

    @Override
    public boolean removeTableExport(String tableExportName, EditOrigin editOrigin) {
        if (!testName(tableExportName)) {
            return false;
        }
        StorageFile tableExportDir = tableExportDirectory.getStorageFile(tableExportName);
        if ((!tableExportDir.exists()) || (!tableExportDir.isDirectory())) {
            return false;
        }
        StorageDirectoryUtils.archiveAndDeleteDirectory(tableExportDir, editOrigin);
        return true;
    }

    @Override
    public boolean copyTableExport(String tableExportName, String newTableExportName, EditOrigin editOrigin) {
        if (!testName(tableExportName)) {
            return false;
        }
        if (!testName(newTableExportName)) {
            throw new IllegalArgumentException(newTableExportName + " is not a technical name");
        }
        File sourceDir = tableExportDirectory.getDataDir(tableExportName);
        StorageFile destinationDir = tableExportDirectory.getStorageFile(newTableExportName);
        if (destinationDir.exists()) {
            return false;
        }
        StorageDirectoryUtils.copyAndArchiveDirectory(sourceDir, destinationDir, editOrigin);
        return true;
    }

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

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

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


    private void scanTableExportDirectories(Map<String, TableExportStorage.Unit> map) {
        File dataDir = tableExportDirectory.getDataDir();
        if (!dataDir.exists()) {
            return;
        }
        if (!dataDir.isDirectory()) {
            return;
        }
        for (File file : dataDir.listFiles()) {
            if (file.isDirectory()) {
                String name = file.getName();
                if (testName(name)) {
                    if (map.containsKey(name)) {
                        continue;
                    }
                    TableExportStorage.Unit unit = buildUnit(name);
                    map.put(name, unit);
                }
            }
        }
    }

    private TableExportStorage.Unit buildUnit(String name) {
        StorageFile dir = tableExportDirectory.getStorageFile(name);
        TableExportStorageUnitBuilder builder = new TableExportStorageUnitBuilder();
        TableExportDefBuilder tableExportDefBuilder = new TableExportDefBuilder(name);
        for (StorageFile storageFile : dir.listFiles()) {
            if (!storageFile.isDirectory()) {
                String fileName = storageFile.getName();
                if ((fileName.endsWith(".txt")) && (fileName.length() > 4)) {
                    if (testName(fileName.substring(0, fileName.length() - 4))) {
                        builder.addStorageContent(StorageUtils.toStorageContent(fileName, storageFile.getFile()));
                    }
                } else if (fileName.equals("_def.xml")) {
                    Document doc = storageFile.readDocument();
                    TableExportDefDOMReader.init(tableExportDefBuilder, LogUtils.NULL_MESSAGEHANDLER)
                            .read(doc.getDocumentElement());
                }
            }
        }
        builder.setTableExportDef(tableExportDefBuilder.toTableExportDef());
        return builder.toTableExportStorageUnit();
    }

    private StorageFile getStorageFile(String tableExportName, String fileName) {
        String path = tableExportName + File.separator + fileName;
        return tableExportDirectory.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;
        }
        switch (path) {
            case "_def.xml":
                return null;
            default:
                return path;
        }
    }


}
