/* BdfServer_Multi - Copyright (c) 2022-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.multi.impl;

import fr.exemole.bdfserver.api.storage.BdfStorageException;
import fr.exemole.bdfserver.multi.MultiConf;
import fr.exemole.bdfserver.multi.api.MultiMetadata;
import fr.exemole.bdfserver.multi.api.MultiMetadataEditor;
import fr.exemole.bdfserver.multi.tools.dom.MultiMetadataDOMReader;
import fr.exemole.bdfserver.multi.xml.MultiMetadataXMLPart;
import fr.exemole.bdfserver.storage.directory.StorageDirectory;
import fr.exemole.bdfserver.tools.storage.StorageFile;
import java.io.BufferedWriter;
import java.io.IOException;
import java.text.ParseException;
import net.fichotheque.EditOrigin;
import net.fichotheque.utils.DefaultMetadata;
import net.fichotheque.utils.DefaultMetadataEditor;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.Langs;
import net.mapeadores.util.localisation.LangsUtils;
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 MultiMetadataImpl extends DefaultMetadata implements MultiMetadata {

    private final StorageDirectory storageDirectory;
    private Langs workingLangs;
    private String authority;

    public MultiMetadataImpl(StorageDirectory storageDirectory, Langs workingLangs, String authority) {
        this.storageDirectory = storageDirectory;
        this.workingLangs = workingLangs;
        this.authority = authority;
    }

    @Override
    public Langs getWorkingLangs() {
        return workingLangs;
    }

    @Override
    public String getAuthority() {
        return authority;
    }

    public MultiMetadataEditor getMultiMetadataEditor(EditOrigin editOrigin) {
        return new InternalMultiMetadataEditor(this, editOrigin);
    }

    public boolean saveChanges(MultiMetadataEditor editor) {
        InternalMultiMetadataEditor internalEditor = (InternalMultiMetadataEditor) editor;
        return internalEditor.saveChanges(storageDirectory);
    }

    public static MultiMetadataImpl build(StorageDirectory storageDirectory, MultiConf multiConf) {
        MultiMetadataImpl multiMetadata = new MultiMetadataImpl(storageDirectory, LangsUtils.wrap(getDefaultLang(multiConf)), getDefaultAuthority(multiConf));
        StorageFile storageFile = MultiStorageFileCatalog.multiMetadata(storageDirectory);
        if (storageFile.exists()) {
            Document document = storageFile.readDocument();
            MultiMetadataDOMReader reader = new MultiMetadataDOMReader(multiMetadata.getMultiMetadataEditor(null), LogUtils.NULL_MESSAGEHANDLER);
            reader.read(document.getDocumentElement());
        }
        return multiMetadata;
    }

    private static String getDefaultAuthority(MultiConf multiConf) {
        String authority = multiConf.getParam("authority");
        if (authority != null) {
            if (!StringUtils.isAuthority(authority)) {
                authority = null;
            }
        }
        if (authority == null) {
            authority = "localhost";
        }
        return authority;
    }

    private static Lang getDefaultLang(MultiConf multiConf) {
        Lang defaultLang = null;
        String langString = multiConf.getParam("lang");
        if (langString != null) {
            try {
                defaultLang = Lang.parse(langString);
            } catch (ParseException pe) {
            }
        }
        if (defaultLang == null) {
            defaultLang = Lang.getDefault().getRootLang();
        }
        return defaultLang;
    }


    private static class InternalMultiMetadataEditor extends DefaultMetadataEditor implements MultiMetadataEditor {

        private final MultiMetadataImpl multiMetadataImpl;
        private final EditOrigin editOrigin;


        private InternalMultiMetadataEditor(MultiMetadataImpl multiMetadataImpl, EditOrigin editOrigin) {
            super(multiMetadataImpl);
            this.multiMetadataImpl = multiMetadataImpl;
            this.editOrigin = editOrigin;
        }

        @Override
        public boolean setWorkingLangs(Langs workingLangs) {
            if (workingLangs.isEmpty()) {
                return false;
            } else if (LangsUtils.areEquals(multiMetadataImpl.workingLangs, workingLangs)) {
                return false;
            } else {
                multiMetadataImpl.workingLangs = workingLangs;
                fireChange();
                return true;
            }
        }

        @Override
        public boolean setAuthority(String authority) {
            if (StringUtils.isAuthority(authority)) {
                String currentAuthority = multiMetadataImpl.authority;
                if (!currentAuthority.equals(authority)) {
                    multiMetadataImpl.authority = authority;
                    fireChange();
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        private boolean saveChanges(StorageDirectory storageDirectory) {
            if (isWithChanges()) {
                StorageFile storageFile = MultiStorageFileCatalog.multiMetadata(storageDirectory);
                try (BufferedWriter buf = storageFile.archiveAndGetWriter(editOrigin)) {
                    AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(buf);
                    xmlWriter.appendXMLDeclaration();
                    MultiMetadataXMLPart multiMetadataXMLPart = new MultiMetadataXMLPart(xmlWriter);
                    multiMetadataXMLPart.appendMultiMetadata(multiMetadataImpl);
                    return true;
                } catch (IOException ioe) {
                    throw new BdfStorageException(ioe);
                }
            } else {
                return false;
            }
        }

    }

}
