import sys
from static_item import gds_static_item
import internal_call

__all__ = ["gds_static_item", "gds_dynamic_item"]

# this is an item which can be calculated
# when a parameter is changed from the outside, the object needs to be recalculated
class gds_dynamic_item(gds_static_item):
    """ gds_dynamic_item: base class for objects that do internal calculations 
        this class takes care of checking whether calculations need to be executed
        based on changes in object properties """
    __static_item_class__ = gds_static_item
    def __init__(self, calculated = False, **kwargs):
        self.__reset_calc__(calculated)
        object.__setattr__(self, "__internal_calc__", False)
        object.__setattr__(self, "__info_calculated__", False)
        super(gds_dynamic_item, self).__init__(**kwargs)

        
    def __make_static__(self, static_class = None):
        """ makes the dynamic object static """
        if static_class == None: static_class = self.__static_item_class__
        self.calc() # calculate coordinates
        # upcast: static_class must be parent class of dynamic class
        object.__setattr__(self, "__class__", static_class)

    def __reset_calc__ (self, value = False):
        """ resets the calculation flag """
        object.__setattr__(self, "__calculated__",  value)
        object.__setattr__(self, "__info_calculated__",  value)
        

    def __set_internal_calc__ (self, value = False):
        """ resets the calculation flag """
        object.__setattr__(self, "__internal_calc__",  value)

    def is_calc_internal(self, level= 1):
        """ checks is a call to a function is done from within the object or from outside """
        return self.__internal_calc__

    def is_calculated(self):
        """ returns True is the object is already calculated """
        return self.__calculated__
    
    # override set_attribute
    def __setattr__(self, name, value):
        if not self.is_call_internal():
            if hasattr(self, name):
                if value == object.__getattribute__(self,name): return
            self.__reset_calc__()
        object.__setattr__(self, name, value)

    def calc_info(self, Force = False):
        if Force or not self.__info_calculated__: 
            self.__calc_info__()
        object.__setattr__(self, "__info_calculated__", True)
        
    def calc(self, Force = False):
        """ calculates the object, when necessary (or when Force is True) """
        if Force or not self.is_calculated(): 
            self.__set_internal_calc__(True)
            self.clear()
            self.calc_info(Force)
            self.__calc__() 
        self.__reset_calc__(True)
        self.__set_internal_calc__(False)

    def __calc_info__(self):
        pass
        
    is_call_internal = internal_call.is_call_internal