/* BdfServer_DirectoryStorage - Copyright (c) 2011-2024 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.BdfUserStorage;
import fr.exemole.bdfserver.api.users.BdfUserPrefs;
import fr.exemole.bdfserver.storage.directory.bdfdata.BdfUserDirectory;
import fr.exemole.bdfserver.storage.directory.bdfdata.Save;
import fr.exemole.bdfserver.tools.storage.StorageFile;
import fr.exemole.bdfserver.tools.users.BdfUserPrefsBuilder;
import fr.exemole.bdfserver.tools.users.BdfUserUtils;
import fr.exemole.bdfserver.tools.users.dom.BdfUserPrefsDOMReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import net.fichotheque.SubsetKey;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.sphere.Sphere;
import net.mapeadores.util.ini.IniParser;
import org.w3c.dom.Document;
import fr.exemole.bdfserver.storage.directory.StorageDirectory;


/**
 *
 * @author Vincent Calame
 */
public class BdfUserStorageImpl implements BdfUserStorage {

    private final static Map<String, String> EMPTY_MAP = Collections.emptyMap();
    private final StorageDirectory bdfdataDirectory;

    public BdfUserStorageImpl(StorageDirectory bdfdataDirectory) {
        this.bdfdataDirectory = bdfdataDirectory;
    }

    @Override
    public void saveBdfUserPrefs(Redacteur redacteur, BdfUserPrefs bdfUserPrefs) {
        Save.saveBdfUserPrefs(bdfdataDirectory, redacteur, bdfUserPrefs);
    }

    @Override
    public void removeBdfUser(Sphere sphere, int id) {
        BdfUserDirectory bdfUserDirectory = BdfUserDirectory.getBdfUserDirectory(bdfdataDirectory, sphere.getSubsetKey(), id);
        bdfUserDirectory.delete();
    }

    @Override
    public BdfUserPrefs getBdfUserPrefs(Redacteur redacteur) {
        StorageFile storageFile = BdfUserDirectory.getPrefsStorageFile(bdfdataDirectory, redacteur.getSubsetKey(), redacteur.getId());
        if (!storageFile.exists()) {
            return null;
        }
        Document doc = storageFile.readDocument();
        BdfUserPrefsBuilder bdfUserPrefsBuilder = new BdfUserPrefsBuilder();
        BdfUserPrefsDOMReader domReader = new BdfUserPrefsDOMReader(redacteur.getFichotheque(), bdfUserPrefsBuilder);
        domReader.readBdfUserPrefs(doc.getDocumentElement());
        return bdfUserPrefsBuilder.toBdfUserPrefs();
    }

    @Override
    public void removeSphere(SubsetKey sphereKey) {
        BdfUserDirectory.deleteSphereDir(bdfdataDirectory, sphereKey);
    }

    @Override
    public Map<String, String> getStoreMap(Redacteur redacteur, String name) {
        if (!BdfUserUtils.isValidStoreName(name)) {
            throw new IllegalArgumentException("Invalid store name: " + name);
        }
        Map<String, String> resultMap = new LinkedHashMap<String, String>();
        StorageFile storageFile = BdfUserDirectory.getIniStorageFile(bdfdataDirectory, redacteur.getSubsetKey(), redacteur.getId(), name);
        if (storageFile.exists()) {
            List<String> lines = storageFile.readLines();
            if (!lines.isEmpty()) {
                MapConsumer consumer = new MapConsumer(resultMap);
                for (String line : lines) {
                    IniParser.parseLine(line, consumer);
                }
            }
        }
        return resultMap;
    }

    @Override
    public void putStoreMap(Redacteur redacteur, String name, Map<String, String> map) {
        if (!BdfUserUtils.isValidStoreName(name)) {
            throw new IllegalArgumentException("Invalid store name: " + name);
        }
        StorageFile storageFile = BdfUserDirectory.getIniStorageFile(bdfdataDirectory, redacteur.getSubsetKey(), redacteur.getId(), name);
        synchronized (redacteur) {
            try (BufferedWriter buf = storageFile.getWriter()) {
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    String key = entry.getKey();
                    if (BdfUserUtils.isValidStoreKey(key)) {
                        buf.append(key);
                        buf.append('=');
                        escape(buf, entry.getValue());
                        buf.append('\n');
                    }
                }
            } catch (IOException ioe) {
                throw new BdfStorageException(ioe);
            }
        }
    }

    private static void escape(BufferedWriter buf, String value) throws IOException {
        int length = value.length();
        for (int i = 0; i < length; i++) {
            char carac = value.charAt(i);
            switch (carac) {
                case '\\':
                    buf.append("\\\\");
                    break;
                case '\n':
                    buf.append("\\n");
                    break;
                default:
                    buf.append(carac);
            }
        }
    }


    private static class MapConsumer implements BiConsumer<String, String> {

        private final Map<String, String> resultMap;

        private MapConsumer(Map<String, String> resultMap) {
            this.resultMap = resultMap;
        }

        @Override
        public void accept(String key, String value) {
            if (BdfUserUtils.isValidStoreKey(key)) {
                resultMap.put(key, value);
            }
        }

    }

}
