/*
 * Auto-CreamAPI
 * Copyright (C) 2020 Jeddunk
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

package xyz.jeddunk.autocreamapi.util;

import org.apache.commons.configuration2.*;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Configurations;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;

import static java.text.MessageFormat.format;

public class CreamApiConfig {

    final Logger logger = LoggerFactory.getLogger(CreamApiConfig.class);

    private static CreamApiConfig configInstance;
    private static final Configurations CONFIGS = new Configurations();

    private INIConfiguration config;

    public String getPath() {
        return path;
    }

    private String path = "cream_api.ini";
    private Integer appId;
    private String language;
    private Boolean unlockAll;
    private Boolean extraProtection;
    private Boolean forceOffline;
    private final Map<Integer, String> dlc = new HashMap<>();
    private final List<String> languages = new ArrayList<>();

    private CreamApiConfig() {
        try {
            config = CONFIGS.ini(path);
            config.setCommentLeadingCharsUsedInInput(";");
        } catch (ConfigurationException e) {
            logger.warn("No config file found!");
            //e.printStackTrace();
        }

        // https://partner.steamgames.com/doc/store/localization#supported_languages
        languages.addAll(Arrays.asList(
                "english",
                "latam",
                "brazilian",
                "german",
                "french",
                "italian",
                "portuguese",
                "spanish",
                "russian",
                "schinese",
                "tchinese",
                "japanese",
                "koreana",
                "arabic",
                "bulgarian",
                "czech",
                "danish",
                "dutch",
                "finnish",
                "greek",
                "hungarian",
                "norwegian",
                "polish",
                "romanian",
                "swedish",
                "thai",
                "turkish",
                "ukrainian",
                "vietnamese"
        ));

        try {
            read();
        } catch (NoSuchElementException e) {
            logger.warn("Error reading cream_api.ini! " +
                    "This can be ignored if setting up CreamAPI for the first time.");
        } catch (NullPointerException e) {
            logger.warn("Can't fill out fields, no configuration file set!");
        }
    }

    public static synchronized CreamApiConfig getInstance() {
        if (configInstance == null) {
            configInstance = new CreamApiConfig();
        }
        return configInstance;
    }

    public void read() throws NullPointerException, NoSuchElementException {
        logger.info("Reading config file...");
        appId = config.getInt("steam.appid");
        language = config.getString("steam.language");
        if (language == null) {
            language = "english";
        }
        unlockAll = config.getBoolean("steam.unlockall");
        extraProtection = config.getBoolean("steam.extraprotection");
        forceOffline = config.getBoolean("steam.forceoffline");
        final SubnodeConfiguration dlc_section = config.getSection("dlc");
        dlc_section.getKeys().forEachRemaining(k -> dlc.put(Integer.parseInt(k), dlc_section.getString(k)));
        logger.info("Successfully read config file!");
    }

    public void sync() throws ConfigurationException {
        logger.info("Saving config file...");
        FileBasedConfigurationBuilder<INIConfiguration> builder = CONFIGS.iniBuilder(path);
        config = builder.getConfiguration();
        config.setCommentLeadingCharsUsedInInput(";");

        config.setProperty("steam.appid", appId);
        config.setProperty("steam.language", language);
        config.setProperty("steam.unlockall", unlockAll);
        config.setProperty("steam.extraprotection", extraProtection);
        config.setProperty("steam.forceoffline", forceOffline);
        final SubnodeConfiguration dlc_section = config.getSection("dlc");
        dlc_section.clear();
        this.dlc.forEach((key, value) -> config.setProperty("dlc." + key.toString(), value));

        // default settings
        config.setProperty("steam.orgapi", "steam_api_o.dll");
        config.setProperty("steam.orgapi64", "steam_api64_o.dll");
        config.setProperty("steam.lowviolence", false);
        config.setProperty("steam_misc.disableuserinterface", false);

        builder.save();
        logger.info("Saved successfully!");
    }

    // DLC list parsing

    public String getDlcListAsString() {
        StringBuilder sb = new StringBuilder();
        dlc.forEach((key, value) -> sb.append(key).append("=").append(value).append("\r\n"));
        return sb.toString();
    }

    public void setDlcListFromString(String str) {
        dlc.clear();
        Arrays.stream(str.split("\\R+")).forEach(line -> {
            final String[] split = line.split("\\s*=\\s*", 2);
            if (split.length == 2) dlc.put(Integer.parseInt(split[0]), split[1]);
            else logger.error(format("Error while splitting line: \"{0}\"", line));
        });
    }

    // GETTERS AND SETTERS

    public Integer getAppId() {
        return appId;
    }

    public void setAppId(Integer appId) {
        this.appId = appId;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public Boolean getUnlockAll() {
        return unlockAll;
    }

    public void setUnlockAll(Boolean unlockAll) {
        this.unlockAll = unlockAll;
    }

    public Boolean getExtraProtection() {
        return extraProtection;
    }

    public void setExtraProtection(Boolean extraProtection) {
        this.extraProtection = extraProtection;
    }

    public Boolean getForceOffline() {
        return forceOffline;
    }

    public void setForceOffline(Boolean forceOffline) {
        this.forceOffline = forceOffline;
    }

    public List<String> getLanguages() {
        return languages;
    }

    public void setConfig(String path) throws ConfigurationException, IOException {
        File file = new File(path);
        if (file.createNewFile()) {
            logger.info("New config file created!");
        } else {
            logger.info("Using existing config file!");
        }
        this.config = CONFIGS.ini(path);
        this.config.setCommentLeadingCharsUsedInInput(";");
        this.path = path;
    }
}
