from __future__ import annotations

'''
/** Original work Copyright 2024 damiano IU3QGD
 * https://to-be-defined
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.

 * Addition to the licence: NO company with more than 50employees can use this SW and you are NOT entitled to store this software 
 * on ANY storage that in some way attempts to monetize it (eg: github)
 * corporations and banks are too rich and too powerful, they MUST be split and ALL the money given back to the state

The idea is that configuration storage changes from having vars named
to have a dictionary where subpart of app store and load value
when classes are istantiated, they register with the config so the config can callback to save params before exist
classes must pick up the desired value using any of the following

getDictValue(key, defvalue)

all of the above search at the first level of the tree
So, again, default values are the result of a get ....


'''
 
import json

from glob_fun import getHomeFileName

# =========================================================
# Use this to store the Application configuration parameters
class AppConfig():
    
    
    # -----------------------------------------------------
    # NOTE that default values are given by caller when they do a get
    def __init__(self, config_fname ):
        
        # save for when need save
        self.config_fname = config_fname
        
        # it is a list of modules that can update
        self._modulesList : list[ConfigSavable] = []
        
        # initially, an empty dictionary
        self.config_dict = {}
        
        try:
            # now attempt to load a configuration back
            ffname = getHomeFileName(self.config_fname)
            with open(ffname) as filei:
                self.config_dict = json.load(filei)
        except Exception as exc :
            # Cannot use the GUI logger, it is not started
            print("load Configuration ",exc)
            # since the configuration is mostly broken, let me reset it
            self.config_dict = {}
            
        # Add a default key with the author
        self.config_dict["author"]="Ing. Damiano Bolla"    

    # ----------------------------------------------
    # A savable is a class that implements the appImportConfig and appSaveConfig
    # Unfortunately python has no simple way to define interfaces in a reliable way, there are no types...
    def addMyselfToSavables(self, asavable : ConfigSavable):
        self._modulesList.append(asavable)
        
    # ------------------------------------------------
    # this will be called to pick up values from the configuration
    # Since, normally, there is some GUI to adjust, this will be called after the GUI is rendered
    # NOTE: Every module is in charge of picking up it's own stuff
    def importPartConfig(self):
        for part in self._modulesList:
            try:
                part.appImportConfig(self)
            except Exception as _exc:
                print("importPartConfig: exception",_exc)
    
    # ------------------------------------------------
    # this will be called to have a module store own configuration into the dictionary
    # NOTE: A module must use an own key to store values
    def savePartConfig(self):
        for app in self._modulesList:
            try:
                app.appSaveConfig(self)
            except Exception as _exc:
                print("savePartConfig: exception",_exc)
    
    # ----------------------------------------------------
    # get a value as a dict, this is normally used by modules to get their own stuff
    def getDictValue(self, key , defvalue : dict ) -> dict:
        try:
            return self.config_dict[key]
        except Exception as _exc:
            return defvalue
    
    # -------------------------------------------------------
    # If a function needs to pick up a specific key, inside a module it can use this one
    def getModuleVariable(self, module_key, variable_key, defvalue ):
        try:
            adict = self.config_dict[module_key]
            return adict[variable_key]
        except Exception as _exc:
            return defvalue
    
    # -----------------------------------------------------
    # submodules can use this one to set their own configuration data
    def setDictValue (self, key : str, adict : dict ):
        self.config_dict[key] = adict
    
    # --------------------------------------------------
    # saves configuration data, note that it should be run in a separate thread
    # so, not to block program flow, maybe
    def saveConfigToFile(self):
        try:
            ffname = getHomeFileName(self.config_fname)
            with open(ffname, 'w') as fileo:
                adict = self.config_dict
                json.dump(adict, fileo, indent=3)
        except Exception as exc :
            print("Save Configuration ",exc)

        
# ----------------------------------------------------
# The magic is the __dict__ that holds the items of the object
# by using this one a class will export the content to json        
class ConfigEncoder(json.JSONEncoder):
    def default(self, obj):
        return obj.__dict__

        
# =========================================================
# classes willing to save and load config should implement this interface
class ConfigSavable():
    
    # -------------------------------------------------------------
    # called by the manager to restore some config INTO the app
    # the app will pick up config from the storage
    def appImportConfig(self, storage : AppConfig):
        pass

    # -------------------------------------------------------------
    # called by the manager to save some config INTO the storage
    # the app will set the keys that will be used
    def appSaveConfig(self, storage : AppConfig):
        pass


