/* BdfServer - Copyright (c) 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.jslib;

import fr.exemole.bdfserver.api.storage.BdfStorageException;
import java.io.BufferedReader;
import java.io.IOException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.function.BiConsumer;
import net.mapeadores.util.ini.IniParser;
import net.mapeadores.util.io.DocStream;
import net.mapeadores.util.io.ResourceStorages;
import net.mapeadores.util.jslib.TemplateFamily;
import net.mapeadores.util.jslib.TemplateFamilyBuilder;
import net.mapeadores.util.text.RelativePath;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
public final class JsLibConfFactory {

    private final short PARAMS_SECTION = 1;
    private final short USE_SECTION = 2;
    private final short CSS_SECTION = 3;
    private final short JAVASCRIPT_SECTION = 4;
    private final String name;
    private final ResourceStorages resourceStorages;
    private final RelativePath libDir;
    private short currentSection = PARAMS_SECTION;
    private final Map<String, String> iniMap = new HashMap<String, String>();
    private final BdfJsLibBuilder jsLibBuilder = new BdfJsLibBuilder();
    private final Set<String> pageSet = new LinkedHashSet<String>();
    private BiConsumer<String, String> consumer;

    private JsLibConfFactory(ResourceStorages resourceStorages, String name, RelativePath libDir) {
        this.resourceStorages = resourceStorages;
        this.name = name;
        this.libDir = libDir;
        this.consumer = (key, value) -> {
            iniMap.put(key, value);
        };
    }

    public static JsLibConf newInstance(ResourceStorages resourceStorages, String name, RelativePath libDir) {
        JsLibConfFactory factory = new JsLibConfFactory(resourceStorages, name, libDir);
        factory.parseConfFile();
        factory.checkSubfolders();
        return factory.toJsLibConf();
    }

    private void parseConfFile() {
        DocStream docStream = resourceStorages.getResourceDocStream(libDir.buildChild("jslib.conf"));
        if (docStream != null) {
            try (BufferedReader reader = new BufferedReader(docStream.getReader())) {
                String line;
                while ((line = reader.readLine()) != null) {
                    parseLine(line);
                }
            } catch (IOException ioe) {
                throw new BdfStorageException(ioe);
            }
        }
    }

    private void parseLine(String line) {
        line = line.trim();
        if (line.length() == 0) {
            return;
        }
        char carac = line.charAt(0);
        switch (carac) {
            case ';':
            case '#':
            case '!':
                return;
            case '[':
                currentSection = checkSection(line.substring(1));
                return;
        }
        switch (currentSection) {
            case USE_SECTION:
                parseUse(line);
                break;
            case CSS_SECTION:
                parseCss(line);
                break;
            case JAVASCRIPT_SECTION:
                parseJavascript(line);
                break;
            default:
                parseParam(line);
        }

    }

    private short checkSection(String sectionName) {
        int idx = sectionName.indexOf(']');
        if (idx > -1) {
            sectionName = sectionName.substring(0, idx);
        }
        sectionName = sectionName.trim();
        sectionName = sectionName.toLowerCase();
        switch (sectionName) {
            case "use":
                return USE_SECTION;
            case "css":
                return CSS_SECTION;
            case "js":
            case "javascript":
                return JAVASCRIPT_SECTION;
            case "params":
            default:
                return PARAMS_SECTION;
        }
    }

    private void parseParam(String line) {
        IniParser.parseLine(line, consumer);
    }

    private void parseUse(String line) {
        pageSet.add(line);
    }

    private void parseJavascript(String line) {
        try {
            jsLibBuilder.addJsScript(convertToRelativePath(line));
        } catch (ParseException pe) {

        }
    }

    private void parseCss(String line) {
        try {
            jsLibBuilder.addCssPath(convertToRelativePath(line));
        } catch (ParseException pe) {

        }
    }

    private void checkSubfolders() {
        SortedMap<String, RelativePath> subFolders = resourceStorages.listSubFolders(libDir);
        for (Map.Entry<String, RelativePath> entry : subFolders.entrySet()) {
            RelativePath subfolderPath = entry.getValue();
            switch (entry.getKey()) {
                case "templates":
                    TemplateFamily jslibTemplateFamily = TemplateFamilyBuilder.init(name)
                            .setPath(subfolderPath)
                            .toTemplateFamily();
                    jsLibBuilder.addTemplateFamily(jslibTemplateFamily);
                    break;
                case "js":
                    jsLibBuilder.scanJavascriptFolder(resourceStorages, subfolderPath);
                    break;
                case "css":
                    jsLibBuilder.scanCssFolder(resourceStorages, subfolderPath);
                    break;

            }
        }
    }

    private void addJavascriptFiles() {

    }

    private JsLibConf toJsLibConf() {
        return new JsLibConf(iniMap, jsLibBuilder.toJsLib(), StringUtils.toList(pageSet));
    }

    private RelativePath convertToRelativePath(String path) throws ParseException {
        if (path.startsWith("/")) {
            return RelativePath.parse(path.substring(0));
        } else {
            return libDir.parseChild(path);
        }
    }

}
