
import traceback
import wx

_KNOWN_HIERARCHY = []
_SIZERS = []
_ALL = []

stuff = []

## class SizeManagerMetaclass(type):
    ## def __new__(cls, name, bases, dict):
        ## print name, bases
        ## return super(cls, object).__new__(cls, name, bases, dict)


class SizeManager(object):
    pass


default_size = (0, wx.EXPAND|wx.ALL, 2)

class SizerSizeManager(SizeManager):
    ## __metaclass__ = SizeManagerMetaclass
    def __init__(self, *args, **kwargs):
        sflags = kwargs.pop('sflags', default_size)
        wx.BoxSizer.__init__(self, self.cl, *args, **kwargs)

        if _SIZERS:
            print "adding", type(self), type(_SIZERS[-1]), len(_SIZERS)
            _SIZERS[-1].Add(self, *sflags)

    def __enter__(self):
        print "adding sizer to hierarchy", type(self), len(_SIZERS)
        _SIZERS.append(self)
        _ALL.append(self)
        return self
    
    def __exit__(self, type, value, traceback):
        if not _SIZERS or _SIZERS[-1] is not self or not _ALL or _ALL[-1] is not self:
            raise RuntimeError("improper stack push/pop order")
        _SIZERS.pop()
        _ALL.pop()
        if _ALL[-1] is _KNOWN_HIERARCHY[-1]:
            print "setting sizer", self, _KNOWN_HIERARCHY[-1]
            print traceback.format_stack()
            _KNOWN_HIERARCHY[-1].SetSizer(self)

class ControlSizeManager(SizeManager):
    ## __metaclass__ = SizeManagerMetaclass
    def __init__(self, *args, **kwargs):
        # todo: handle frame with no parent
        if ((not args) or not isinstance(args[0], (wx.Control, wx.TopLevelWindow))) and \
            'parent' not in kwargs and not isinstance(self, wx.Frame):
            for obj in reversed(_KNOWN_HIERARCHY):
                if isinstance(obj, (wx.Control, wx.TopLevelWindow)):
                    args = (obj,) + args
                    break
            else:
                print "couldn't find parent", self

        sflags = kwargs.pop('sflags', default_size)
        super(ControlSizeManager, self).__init__(*args, **kwargs)
        print "adding", type(self), type(_SIZERS[-1]), len(_SIZERS)
        _SIZERS[-1].Add(self, *sflags)
        stuff.append(self)

    def __enter__(self):
        print "adding control to hierarchy", self
        _KNOWN_HIERARCHY.append(self)
        return self

    def __exit__(self, type, value, traceback):
        if not _KNOWN_HIERARCHY or _KNOWN_HIERARCHY[-1] is not self or not _ALL or _ALL[-1] is not self:
            raise RuntimeError("improper stack push/pop order")
        print "removing control from hierarchy", self
        _KNOWN_HIERARCHY.pop()

def _wrap_control(ctrl):
    class SizeManagedControl(ctrl):
        def __init__(self, *args, **kwargs):
            # todo: handle frame with no parent
            if ((not args) or not isinstance(args[0], (wx.Control, wx.TopLevelWindow))) and \
                'parent' not in kwargs and not isinstance(self, wx.Frame):
                for obj in reversed(_KNOWN_HIERARCHY):
                    if isinstance(obj, (wx.Control, wx.TopLevelWindow)):
                        args = (obj,) + args
                        break
                else:
                    print "couldn't find parent", self
        
            sflags = kwargs.pop('sflags', default_size)
            super(SizeManagedControl, self).__init__(*args, **kwargs)
            print "adding", type(self), type(_SIZERS[-1]), len(_SIZERS)
            _SIZERS[-1].Add(self, *sflags)
            stuff.append(self)
        
        def __enter__(self):
            print "adding control to hierarchy", self
            _KNOWN_HIERARCHY.append(self)
            _ALL.append(self)
            return self
        
        def __exit__(self, type, value, traceback):
            if not _KNOWN_HIERARCHY or _KNOWN_HIERARCHY[-1] is not self or not _ALL or _ALL[-1] is not self:
                raise RuntimeError("improper stack push/pop order")
            print "removing control from hierarchy", self
            _KNOWN_HIERARCHY.pop()
            _ALL.pop()

    SizeManagedControl.__name__ = 'Sized' + ctrl.__name__
    return SizeManagedControl

@apply
def onimport():
    # We put all of the imports, etc., in here in order to not pollute the
    # global namespace with a bunch of temporary variables.
    gl = globals()

    for name, obj in vars(wx).iteritems():
        if isinstance(obj, type) and issubclass(obj, (wx.Control, wx.TopLevelWindow)):
            new_obj = _wrap_control(obj)
            gl[new_obj.__name__] = new_obj

    class HorizontalSizer(wx.BoxSizer):
        cl = wx.HORIZONTAL
        def __init__(self, *args, **kwargs):
            sflags = kwargs.pop('sflags', default_size)
            wx.BoxSizer.__init__(self, self.cl, *args, **kwargs)
        
            if _SIZERS:
                print "adding", type(self), type(_SIZERS[-1]), len(_SIZERS)
                _SIZERS[-1].Add(self, *sflags)
        
        def __enter__(self):
            print "adding sizer to hierarchy", type(self), len(_SIZERS)
            _SIZERS.append(self)
            _ALL.append(self)
            return self
        
        def __exit__(self, type, value, tb):
            if not _SIZERS or _SIZERS[-1] is not self or not _ALL or _ALL[-1] is not self:
                raise RuntimeError("improper stack push/pop order")
            _SIZERS.pop()
            _ALL.pop()
            if _ALL[-1] is _KNOWN_HIERARCHY[-1]:
                print "setting sizer", self, _KNOWN_HIERARCHY[-1]
                print traceback.format_stack()
                _KNOWN_HIERARCHY[-1].SetSizer(self)
                
    gl['HorizontalSizer'] = HorizontalSizer

    class VerticalSizer(wx.BoxSizer):
        cl = wx.VERTICAL
        def __init__(self, *args, **kwargs):
            sflags = kwargs.pop('sflags', default_size)
            wx.BoxSizer.__init__(self, self.cl, *args, **kwargs)
        
            if _SIZERS:
                print "adding", type(self), type(_SIZERS[-1]), len(_SIZERS)
                _SIZERS[-1].Add(self, *sflags)
        
        def __enter__(self):
            print "adding sizer to hierarchy", type(self), len(_SIZERS)
            _SIZERS.append(self)
            _ALL.append(self)
            return self
        
        def __exit__(self, type, value, tb):
            if not _SIZERS or _SIZERS[-1] is not self or not _ALL or _ALL[-1] is not self:
                raise RuntimeError("improper stack push/pop order")
            _SIZERS.pop()
            _ALL.pop()
            if _ALL[-1] is _KNOWN_HIERARCHY[-1]:
                print "setting sizer", self, _KNOWN_HIERARCHY[-1]
                print traceback.format_stack()
                _KNOWN_HIERARCHY[-1].SetSizer(self)
                
    gl['VerticalSizer'] = VerticalSizer



if __name__ == '__main__':
    print 'SizedFrame' in globals()
    print sorted(globals())
    class TestFrame(wx.Frame):
        def __init__(self, *args, **kwargs):
            wx.Frame.__init__(self, *args, **kwargs)
            _KNOWN_HIERARCHY.append(self)
            _ALL.append(self)
            with VerticalSizer():
                with HorizontalSizer() as hs:
                    SizedStaticText(label="label1")
                    hs.AddStretchSpacer()
                    self.input1 = SizedTextCtrl(size=(250, -1))
                with HorizontalSizer() as hs:
                    SizedStaticText(label="label2")
                    hs.AddStretchSpacer()
                    self.input2 = SizedTextCtrl(size=(250, -1))
                with HorizontalSizer() as hs:
                    SizedStaticText(label="label3")
                    hs.AddStretchSpacer()
                    self.input3 = SizedTextCtrl(size=(250, -1))
                with HorizontalSizer() as hs:
                    SizedStaticText(label="label4")
                    hs.AddStretchSpacer()
                    self.input4 = SizedTextCtrl(size=(250, -1))

    class TestFrame3(wx.Frame):
        def __init__(self, *args, **kwargs):
            wx.Frame.__init__(self, *args, **kwargs)
            _KNOWN_HIERARCHY.append(self)
            _ALL.append(self)
            with VerticalSizer():
                with HorizontalSizer() as hs:
                    hs.Add(wx.StaticText(self, label="label1"), *default_size)
                    hs.AddStretchSpacer()
                    self.input1 = wx.TextCtrl(self, size=(250, -1))
                    hs.Add(self.input1, *default_size)
                with HorizontalSizer() as hs:
                    hs.Add(wx.StaticText(self, label="label2"), *default_size)
                    hs.AddStretchSpacer()
                    self.input2 = wx.TextCtrl(self, size=(250, -1))
                    hs.Add(self.input2, *default_size)
                with HorizontalSizer() as hs:
                    hs.Add(wx.StaticText(self, label="label3"), *default_size)
                    hs.AddStretchSpacer()
                    self.input3 = wx.TextCtrl(self, size=(250, -1))
                    hs.Add(self.input3, *default_size)
                with HorizontalSizer() as hs:
                    hs.Add(wx.StaticText(self, label="label4"), *default_size)
                    hs.AddStretchSpacer()
                    self.input4 = wx.TextCtrl(self, size=(250, -1))
                    hs.Add(self.input4, *default_size)

    class TestFrame2(wx.Frame):
        def __init__(self, *args, **kwargs):
            wx.Frame.__init__(self, *args, **kwargs)
            vs = wx.BoxSizer(wx.VERTICAL)

            hs = wx.BoxSizer(wx.HORIZONTAL)
            hs.Add(wx.StaticText(self, label="label1"), *default_size)
            hs.AddStretchSpacer()
            self.input1 = wx.TextCtrl(self, size=(250, -1))
            hs.Add(self.input1, *default_size)
            vs.Add(hs, *default_size)

            hs = wx.BoxSizer(wx.HORIZONTAL)
            hs.Add(wx.StaticText(self, label="label2"), *default_size)
            hs.AddStretchSpacer()
            self.input2 = wx.TextCtrl(self, size=(250, -1))
            hs.Add(self.input2, *default_size)
            vs.Add(hs, *default_size)
            
            hs = wx.BoxSizer(wx.HORIZONTAL)
            hs.Add(wx.StaticText(self, label="label3"), *default_size)
            hs.AddStretchSpacer()
            self.input3 = wx.TextCtrl(self, size=(250, -1))
            hs.Add(self.input3, *default_size)
            vs.Add(hs, *default_size)
            
            hs = wx.BoxSizer(wx.HORIZONTAL)
            hs.Add(wx.StaticText(self, label="label4"), *default_size)
            hs.AddStretchSpacer()
            self.input4 = wx.TextCtrl(self, size=(250, -1))
            hs.Add(self.input4, *default_size)
            vs.Add(hs, *default_size)

            self.SetSizer(vs)

    a = wx.App(0)
    f = TestFrame(None, -1, size=(600, 600))
    f.Show()
    f.Hide()
    f.Show()
    a.MainLoop()
