/* 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.tools.storage;

import fr.exemole.bdfserver.api.storage.BdfStorageException;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import net.fichotheque.EditOrigin;
import net.fichotheque.history.HistoryUnit;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.io.FileUtils;
import net.mapeadores.util.io.IOUtils;
import org.w3c.dom.Document;


/**
 * Désigne un fichier destiné à stocker des informations de la base.
 *
 * @author Vincent Calame
 */
public class StorageFile {

    public final static String ENCODING = "UTF-8";
    private final String relativePath;
    private final File file;
    private final File backupFile;

    public StorageFile(String relativePath, File file, File backupFile) {
        if (file == null) {
            throw new IllegalArgumentException("file is null");
        }
        this.relativePath = relativePath;
        this.file = file;
        this.backupFile = backupFile;
    }

    public String getRelativePath() {
        return relativePath;
    }

    public boolean exists() {
        return file.exists();
    }

    public boolean isDirectory() {
        return file.isDirectory();
    }

    public boolean backupExist() {
        return (backupFile != null) && (backupFile.exists());
    }

    public String getName() {
        return file.getName();
    }

    public File getFile() {
        return file;
    }

    public File getBackupFile() {
        return backupFile;
    }

    public String toURI() {
        return file.getPath();
    }

    public void delete() {
        backup();
        try {
            if (file.exists()) {
                FileUtils.forceDelete(file);
            }
        } catch (IOException ioe) {
            throw new BdfStorageException(file, ioe);
        }
    }

    public List<StorageFile> listFiles() {
        List<StorageFile> result = new ArrayList<StorageFile>();
        if (file.isDirectory()) {
            for (File f : file.listFiles()) {
                result.add(getSubStorageFile(f.getName()));
            }
        }
        return result;
    }

    public StorageFile getSubStorageFile(String path) {
        String subRelativePath = relativePath + File.separator + path;
        File subFile = new File(file, path);
        File subBackupFile = null;
        if (backupFile != null) {
            subBackupFile = new File(backupFile, path);
        }
        return new StorageFile(subRelativePath, subFile, subBackupFile);
    }

    public HistoryUnit getHistoryUnit() {
        return StorageUtils.getHistoryUnit(file, backupFile);
    }

    public List<String> readLines() {
        if (!file.exists()) {
            return null;
        }
        List<String> result;
        try (InputStream is = new FileInputStream(file)) {
            result = IOUtils.readLines(is, "UTF-8");
        } catch (IOException ioe) {
            if (!backupExist()) {
                throw new BdfStorageException(file, ioe);
            }
            try (InputStream is2 = new FileInputStream(backupFile)) {
                result = IOUtils.readLines(is2, "UTF-8");
            } catch (IOException ioe2) {
                throw new BdfStorageException(backupFile, ioe2);
            }
            restore();
        }
        return result;
    }

    public Document readDocument() {
        if (!file.exists()) {
            return null;
        }
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docbuilder = null;
        Document document = null;
        try {
            docbuilder = documentBuilderFactory.newDocumentBuilder();
        } catch (ParserConfigurationException pce) {
            throw new BdfStorageException(pce);
        }
        try (InputStream is = new FileInputStream(file)) {
            document = docbuilder.parse(is);
        } catch (Exception e) {
            if (!backupExist()) {
                throw new BdfStorageException(file, e);
            }
            try (InputStream is2 = new FileInputStream(backupFile)) {
                document = docbuilder.parse(is2);
            } catch (Exception e2) {
                throw new BdfStorageException(backupFile, e);
            }
            restore();
        }
        return document;
    }

    public Document readDocument(String versionName) {
        return StorageUtils.readDocument(file, backupFile, versionName);
    }

    public boolean restore() {
        if (backupExist()) {
            try {
                FileUtils.copyFile(backupFile, file);
            } catch (IOException ioe) {
                throw new BdfStorageException(file, ioe);
            }
            return true;
        }
        return false;
    }

    public BufferedWriter getWriter() throws IOException {
        return getWriter(true);
    }

    public BufferedWriter getWriter(boolean withBackup) throws IOException {
        testPath();
        if (withBackup) {
            backup();
        }
        OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
        BufferedWriter bufWriter = new BufferedWriter(writer);
        return bufWriter;
    }

    public BufferedWriter archiveAndGetWriter(@Nullable EditOrigin editOrigin) throws IOException {
        testPath();
        if (editOrigin != null) {
            StorageUtils.archive(file, backupFile, editOrigin);
        } else {
            backup();
        }
        OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
        BufferedWriter bufWriter = new BufferedWriter(writer);
        return bufWriter;
    }

    public BufferedOutputStream getOutputStream() throws IOException {
        return getOutputStream(true);
    }

    public BufferedOutputStream getOutputStream(boolean withBackup) throws IOException {
        testPath();
        if (withBackup) {
            backup();
        }
        OutputStream outputStream = new FileOutputStream(file);
        BufferedOutputStream bufOutputStream = new BufferedOutputStream(outputStream);
        return bufOutputStream;
    }

    public BufferedOutputStream archiveAndGetOutputStream(EditOrigin editOrigin) throws IOException {
        testPath();
        if (editOrigin != null) {
            StorageUtils.archive(file, backupFile, editOrigin);
        }
        OutputStream outputStream = new FileOutputStream(file);
        BufferedOutputStream bufOutputStream = new BufferedOutputStream(outputStream);
        return bufOutputStream;
    }

    public void archiveAndDelete(EditOrigin editOrigin) {
        try {
            if (editOrigin != null) {
                StorageUtils.archiveBeforeDelete(file, backupFile, editOrigin);
            }
            if (file.exists()) {
                FileUtils.forceDelete(file);
            }
        } catch (IOException ioe) {
            throw new BdfStorageException(file, ioe);
        }
    }


    private void testPath() {
        file.getParentFile().mkdirs();
        if (file.isDirectory()) {
            file.delete();
        }
    }

    private void backup() {
        if ((file.exists()) && (backupFile != null) && (!file.isDirectory())) {
            try {
                FileUtils.copyFile(file, backupFile);
            } catch (IOException ioe) {
                throw new BdfStorageException(backupFile, ioe);
            }
        }
    }

    public static StorageFile build(File rootDir, File rootBackupDir, String path) {
        File file = new File(rootDir, path);
        File backupFile = null;
        if (rootBackupDir != null) {
            backupFile = new File(rootBackupDir, path);
        }
        return new StorageFile(path, file, backupFile);
    }

}
