# 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 write config files to strings as if they were files.
from typing import Dict, List, Optional, Tuple

import UM.VersionUpgrade


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


##  A representation of a profile used as intermediary form for conversion from
#   one format to the other.
class Profile:
    ##  Reads version 1 of the file format, storing it in memory.
    #
    #   \param serialised A string with the contents of a profile.
    #   \param filename The supposed filename of the profile, without extension.
    def __init__(self, serialised: str, filename: str) -> None:
        self._filename = filename

        parser = configparser.ConfigParser(interpolation = None)
        parser.read_string(serialised)

        # Check correctness.
        if not parser.has_section("general"):
            raise UM.VersionUpgrade.FormatException("No \"general\" section.")
        if not parser.has_option("general", "version"):
            raise UM.VersionUpgrade.FormatException("No \"version\" in the \"general\" section.")
        if int(parser.get("general", "version")) != 1: # Hard-coded profile version here. If this number changes the entire function needs to change.
            raise UM.VersionUpgrade.InvalidVersionException("The version of this profile is wrong. It must be 1.")

        # Parse the general section.
        self._name = parser.get("general", "name")
        self._type = parser.get("general", "type")
        self._weight = None
        if "weight" in parser["general"]:
            self._weight = int(parser.get("general", "weight"))
        self._machine_type_id = parser.get("general", "machine_type")
        self._machine_variant_name = parser.get("general", "machine_variant")
        self._machine_instance_name = parser.get("general", "machine_instance")
        self._material_name = None
        if "material" in parser["general"]: #Note: Material name is unused in this upgrade.
            self._material_name = parser.get("general", "material")
        elif self._type == "material":
            self._material_name = parser.get("general", "name")

        # Parse the settings.
        self._settings = {} # type: Dict[str,str]
        if parser.has_section("settings"):
            for key, value in parser["settings"].items():
                self._settings[key] = value

        # Parse the defaults and the disabled defaults.
        self._changed_settings_defaults = {}    # type: Dict[str,str]
        if parser.has_section("defaults"):
            for key, value in parser["defaults"].items():
                self._changed_settings_defaults[key] = value
        self._disabled_settings_defaults = []   # type: List[str]
        if parser.has_section("disabled_defaults"):
            disabled_defaults_string = parser.get("disabled_defaults", "values")
            self._disabled_settings_defaults = [item for item in disabled_defaults_string.split(",") if item != ""] # Split by comma.

    ##  Serialises this profile as file format version 2.
    #
    #   \return A tuple containing the new filename and a serialised form of
    #   this profile, serialised in version 2 of the file format.
    def export(self) -> Optional[Tuple[List[str], List[str]]]:
        import VersionUpgrade21to22 # Import here to prevent circular dependencies.

        if self._name == "Current settings":
            return None #Can't upgrade these, because the new current profile needs to specify the definition ID and the old file only had the machine instance, not the definition.

        config = configparser.ConfigParser(interpolation = None)

        config.add_section("general")
        config.set("general", "version", "2") #Hard-coded profile version 2.
        config.set("general", "name", self._name)
        if self._machine_type_id:
            translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id)
            config.set("general", "definition", translated_machine)
        else:
            config.set("general", "definition", "fdmprinter") #In this case, the machine definition is unknown, and it might now have machine-specific profiles, in which case this will fail.

        config.add_section("metadata")
        config.set("metadata", "quality_type", "normal") #This feature doesn't exist in 2.1 yet, so we don't know the actual quality type. For now, always base it on normal.
        config.set("metadata", "type", "quality")
        if self._weight:
            config.set("metadata", "weight", str(self._weight))
        if self._machine_variant_name:
            if self._machine_type_id:
                config.set("metadata", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id))
            else:
                config.set("metadata", "variant", self._machine_variant_name)

        if self._settings:
            self._settings = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings)
            config.add_section("values")
            for key, value in self._settings.items():
                config.set("values", key, str(value))

        if self._changed_settings_defaults:
            self._changed_settings_defaults = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._changed_settings_defaults)
            config.add_section("defaults")
            for key, value in self._changed_settings_defaults.items():
                config.set("defaults", key, str(value))

        if self._disabled_settings_defaults:
            disabled_settings_defaults = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting)
                                          for setting in self._disabled_settings_defaults]
            config.add_section("disabled_defaults")
            disabled_defaults_string = str(disabled_settings_defaults[0]) #Must be at least 1 item, otherwise we wouldn't enter this if statement.
            for item in disabled_settings_defaults[1:]:
                disabled_defaults_string += "," + str(item)

        output = io.StringIO()
        config.write(output)
        return [self._filename], [output.getvalue()]