#-------------------------------------------------------------------------------------------------------------------------------
# An "htag.Tag" implem for brython (see https://github.com/manatlan/htag/tree/main/brython)
#-------------------------------------------------------------------------------------------------------------------------------
from browser import html,document,window

def importStatics():
    import __main__
    if not document.hasOwnProperty("htagStaticsImported"):
        all_statics={}
        for k,v in __main__.__dict__.items():
            if v!=TAG and issubclass(v,TAG):
                liste = v.statics
                if not (isinstance( v.statics, list) or isinstance( v.statics, tuple)):
                    liste=[v.statics]
                    
                for i in liste:
                    if isinstance(i,str):
                        i=Tag.style(i)                    
                    elif isinstance(i,bytes):
                        i=Tag.script(i.decode())                    
                    else:
                        assert isinstance(i,TAG)
                        
                    #computed_hash = hash(f"{i.tag} {i.attrs} {i.getInnerHTML()}")
                    computed_hash = hash(f"{i.tag} {i.attrs} {i.innerHTML}")
                    
                    if i.attrs.get("src") or i.attrs.get("href"):
                        i.setAttribute("htagNeedToLoadBeforeStart", True)
                        i.onload = lambda ev: ev.target.removeAttribute("htagNeedToLoadBeforeStart")
                    all_statics[ computed_hash ] = i
            
        document.head <= all_statics.values()
        del all_statics
        document.htagStaticsImported=True


class TAG:
    statics=[]

    def __init__(self,*args,**kargs):
        is_component=hasattr(self,"init")
        attrs,events,props=self.__exkargs__(kargs)
        if is_component:
            importStatics()
            
            # component creation
            constructor=getattr(self,"init")
            
            # print(f"TAG COMPONENT '{self.__class__.__name__}({self.tag})' with: {attrs=} {events=} {props=}")
            if attrs or events:
                # it's "open tag" (accept redef) (it should got a 'init( ..., **a)' )
                # (because it tries call with "_var")
                for k,v in attrs.items():
                    if isinstance(v,bool):
                        if v: self.attrs[k]=""
                    else:
                        self.attrs[k]=v
                for k,v in events.items():
                    self.bind(k,v)
                
                kargs={k:v for k,v in kargs.items() if k not in props}
                try:
                    constructor(*args,**props,**kargs) # to produce a typeerror if not "opened"
                except Exception as e:
                    print(f"***ERROR*** {self.__class__.__name__} constructor:",e)
                    raise e
            else:
                # it's "closed tag" (refuse redef)
                constructor(*args,**kargs)

            if hasattr(self,"connected"):

                def waitConnected( timeout ) -> "promise":
                    # https://codepen.io/eanbowman/pen/jxqKjJ
                    start = window.Date.now()
                
                    def waitParenting(resolve, reject):
                        if self.parent != None:
                            resolve()
                        elif timeout and (window.Date.now() - start) >= timeout:
                            reject(window.Error.new(f"Error waitConnected: Timeout waiting for '{obj_str}' ;-("))
                        else:
                            window.setTimeout( lambda: waitParenting(resolve, reject) , 30 )
                    
                    return window.Promise.new(waitParenting)
    
                waitConnected(2000).then( lambda *a,**k: window.setTimeout( self.connected,0) ) # 2sec for parent'ing, and defer the connected callback
        else:
            # simple creation
            #print(f"TAG SIMPLE '{self.tag}' with: {args=} {kargs=}")
            if len(args)>0:
                content,*args = args
                self <= content
            
            for k,v in attrs.items():
                if isinstance(v,bool):
                    if v: self.attrs[k]=""
                else:
                    self.attrs[k]=v
            for k,v in events.items():
                self.bind(k,v)
            for k,v in props.items():
                setattr(self,k,v)
                
    def __exkargs__(self,kargs:dict) -> (dict,dict,dict):
        events={}   # events
        attrs={}    # html attibuts
        props={}    # instance properties
        for k,v in kargs.items():
            if k.startswith("_"):
                k=TagCreator.cname(k[1:])
                if k.startswith("on"):
                    events[ k[2:] ] = v
                else:
                    attrs[ k ] = v
            else:
                props[k]=v
        return (attrs,events,props)

    def __iadd__(self,  elt):   # "+=" (like "<=")
        self <= elt
        return self
        
    def __enter__(self): # so can use construction with 'with as:' statement
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass     

class TagCreator(type):
    types={}
    def __getattr__(self, tagName:str):
        nodeName = TagCreator.cname(tagName)
        if nodeName not in TagCreator.types:
            TagCreator.types[nodeName]=html.maketag(nodeName)
        typ = TagCreator.types[nodeName]
        return type("".join([i.capitalize() for i in ("tag_"+tagName).split("_")]), (TAG,typ), {**TAG.__dict__,"tag":nodeName})

    @staticmethod
    def cname(x:str) -> str:
        return x.replace("_","-")

class Tag(metaclass=TagCreator):

    @staticmethod
    def onload( cb ):
        importStatics()
        def startWhenReady( cb ):
            if document.querySelector("*[htagNeedToLoadBeforeStart]"):
                window.setTimeout( lambda: startWhenReady(cb), 100 )
            else:
                cb()
        startWhenReady(cb)

        
