##############################################################################
# Part of the Agnos RPC Framework
#    http://agnos.sourceforge.net
#
# Copyright 2011, International Business Machines Corp.
#                 Author: Tomer Filiba (tomerf@il.ibm.com)
#
# 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.
##############################################################################
from contextlib import contextmanager


class Stmt(object):
    def __init__(self, text, *args, **kwargs):
        if text:
            stripped = text.strip()
            default_suffix = "" if stripped.startswith("#") or stripped.endswith(":") else ";"
        else:
            default_suffix = ";" 
        self.suffix = kwargs.pop("suffix", default_suffix)
        if kwargs:
            raise TypeError("invalid keyword arguments %r" % (kwargs.keys(),))
        if args:
            text = text.format(*args)
        self.text = text
    
    def render(self):
        return [self.text + self.suffix]

EmptyStmt = Stmt("", suffix = "")

class Doc(object):
    def __init__(self, text, box = False, spacer = False):
        self.box = box
        self.spacer = spacer
        self.text = text
    
    def render(self):
        lines = ["// " + l for l in self.text.splitlines()]
        if self.box:
            lines.insert(0, "//" * 39)
            lines.append("//" * 39)
        elif self.spacer:
            lines.insert(0, "//")
            lines.append("//")
        return lines 


class Block(object):
    def __init__(self, text, *args, **kwargs):
        self.prefix = kwargs.pop("prefix", "{")
        token = text.split()[0] if text else None
        default_suffix = "};" if token in ["class", "struct", "enum"] else "}" 
        self.suffix = kwargs.pop("suffix", default_suffix)
        if kwargs:
            raise TypeError("invalid keyword arguments %r" % (kwargs.keys(),))
        self.title = Stmt(text, *args, suffix = "")
        self.children = []
        self.stack = []
    
    def _get_head(self):
        if not self.stack:
            return self
        else:
            return self.stack[-1]

    def sep(self, count = 1):
        for i in range(count):
            self._get_head().children.append(EmptyStmt)
    def doc(self, *args, **kwargs):
        self._get_head().children.append(Doc(*args, **kwargs))
    def stmt(self, *args, **kwargs):
        self._get_head().children.append(Stmt(*args, **kwargs))
    
    @contextmanager
    def block(self, *args, **kwargs):
        blk = Block(*args, **kwargs)
        self._get_head().children.append(blk)
        self.stack.append(blk)
        yield blk
        self.stack.pop(-1)
    
    def render(self):
        lines = self.title.render()
        if self.prefix:
            lines.append(self.prefix)
        for child in self.children:
            lines.extend("\t" + l for l in child.render())
        if self.suffix:
            lines.append(self.suffix)
        return lines


class Module(Block):
    def __init__(self):
        Block.__init__(self, None)
    def __enter__(self):
        return self
    def __exit__(self, t, v, tb):
        pass
    def render(self):
        lines = []
        for child in self.children:
            lines.extend(child.render())
        return "\n".join(lines)


if __name__ == "__main__":
    with Module() as m:
        BLOCK = m.block
        STMT = m.stmt
        SEP = m.sep
        
        STMT("using System")
        STMT("using System.Collections")
        STMT("using Thrift")
        SEP(2)
        with BLOCK("namespace t4"):
            with BLOCK("public class moshe"):
                STMT("private int x, y")
                SEP()
                with BLOCK("public moshe()"):
                    STMT("int x = 5")
                    STMT("int y = 6")
                SEP()
                with BLOCK("public ~moshe()"):
                    STMT("Dispose(false)")
    
    print(m.render())




