# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

import configparser #To read config files.
import io #To output config files to string.
from typing import List, Optional, Tuple

import UM.VersionUpgrade #To indicate that a file is of the wrong format.

##  Creates a new preferences instance by parsing a serialised preferences file
#   in version 1 of the file format.
#
#   \param serialised The serialised form of a preferences file in version 1.
#   \param filename The supposed filename of the preferences file, without
#   extension.
#   \return A representation of those preferences, or None if the file format is
#   incorrect.
def importFrom(serialised: str, filename: str) -> Optional["Preferences"]:
    try:
        return Preferences(serialised, filename)
    except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
        return None

##  A representation of preferences files as intermediary form for conversion
#   from one format to the other.
class Preferences:
    ##  Reads version 2 of the preferences file format, storing it in memory.
    #
    #   \param serialised A serialised version 2 preferences file.
    #   \param filename The supposed filename of the preferences file, without
    #   extension.
    def __init__(self, serialised: str, filename: str) -> None:
        self._filename = filename

        self._config = configparser.ConfigParser(interpolation = None)
        self._config.read_string(serialised)

        #Checking file correctness.
        if not self._config.has_section("general"):
            raise UM.VersionUpgrade.FormatException("No \"general\" section.")
        if not self._config.has_option("general", "version"):
            raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.")
        if int(self._config.get("general", "version")) != 2: # Explicitly hard-code version 2, since if this number changes the programmer MUST change this entire function.
            raise UM.VersionUpgrade.InvalidVersionException("The version of this preferences file is wrong. It must be 2.")
        if self._config.has_option("general", "name"): #This is probably a machine instance.
            raise UM.VersionUpgrade.FormatException("There is a \"name\" field in this configuration file. I suspect it is not a preferences file.")

    ##  Serialises these preferences as a preferences file of version 3.
    #
    #   This is where the actual translation happens.
    #
    #   \return A tuple containing the new filename and a serialised version of
    #   a preferences file in version 3.
    def export(self) -> Tuple[List[str], List[str]]:
        #Reset the cura/categories_expanded property since it works differently now.
        if self._config.has_section("cura") and self._config.has_option("cura", "categories_expanded"):
            self._config.remove_option("cura", "categories_expanded")

        #Translate the setting names in the visible settings.
        if self._config.has_section("machines") and self._config.has_option("machines", "setting_visibility"):
            visible_settings = self._config.get("machines", "setting_visibility")
            visible_settings_list = visible_settings.split(",")
            import VersionUpgrade21to22 #Import here to prevent a circular dependency.
            visible_settings_list = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting_name)
                                for setting_name in visible_settings_list]
            visible_settings = ",".join(visible_settings_list)
            self._config.set("machines", "setting_visibility", value = visible_settings)

        #Translate the active_instance key.
        if self._config.has_section("machines") and self._config.has_option("machines", "active_instance"):
            active_machine = self._config.get("machines", "active_instance")
            self._config.remove_option("machines", "active_instance")
            self._config.set("cura", "active_machine", active_machine)

        #Update the version number itself.
        self._config.set("general", "version", value = "3")

        #Output the result as a string.
        output = io.StringIO()
        self._config.write(output)
        return [self._filename], [output.getvalue()]