# -*- python -*-
#                           Package   : omniidl
# cxx.py                    Created on: 2000/8/10
#			    Author    : David Scott (djs)
#
#    Copyright (C) 2003-2011 Apasphere Ltd
#    Copyright (C) 2000 AT&T Laboratories Cambridge
#
#  This file is part of omniidl.
#
#  omniidl is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see http://www.gnu.org/licenses/
#
# Description:

"""Routines for generating bits of C++ syntax dynamically"""

from omniidl_be.cxx import id, types

# For: Generates a nested C++ for loop ranging over
#          {0, bounds[0]}{0, bounds[1]}...
# If destroyed before properly terminated, will output a warning on stderr
# (exceptions are apparently unwise in destructors)#
#
# Example usage:
#    loop = For(stream, [1,2,3,4])
#    stream.out("// do something with @index@", index = loop.index())
#    loop.end()
#
class For:
    def __init__(self, stream, bounds):
        prefix = "_0i"
        index = 0
        index_string = ""
        for bound in bounds:
            i = prefix + str(index)
            stream.out("""\
for (_CORBA_ULong @i@ = 0; @i@ < @bound@; @i@++){""", i = i, bound = bound)

            stream.inc_indent()
            index_string = index_string + "[" + i + "]"
            index = index + 1

        self.__index = index_string
        self.__bounds = bounds
        self.__stream = stream
        self.__closed = (bounds != [])

    def __del__(self):
        if not self.__closed:
            warning("Possibly unterminated For loop generated")

    def index(self):
        return self.__index

    def end(self):
        for bound in self.__bounds:
            self.__stream.dec_indent()
            self.__stream.out("}")

        self.__closed = 1

# Block: Generates a C++ block with appropriate indenting
# Will generate a warning on premature destruction
#
# Example usage:
#    block = Block(stream)
#    stdout.out("// do something")
#    block.end()
#
class Block:
    def __init__(self, stream):
        stream.out("{")
        stream.inc_indent()
        self.__stream = stream
        self.__closed = 0

    def __del__(self):
        if not self.__closed:
            warning("Possibly unclosed Block generated")

    def end(self):
        self.__stream.dec_indent()
        self.__stream.out("}")
        self.__closed = 1


# Class: Generates a C++ class (both header and implementation code)
# Should be subclassed.
#
# .name():    return the class scoped name (id.Name)
# .forward(): forwrad declare this class
# .hh():      produce the class header
# .cc():      produce the class implementation
#
class Class:
    def __init__(self, name):
        assert isinstance(name, id.Name)

        self._name = name

    def name(self):
        return self._name

    def forward(self, stream):
        stream.out("class @name@;", name = self.name().simple())

    def hh(self, stream):
        raise NotImplementedError("Class header missing")

    def cc(self, stream):
        raise NotImplementedError("Class implementation missing")


# Method: Generates a C++ method (both header and implementation code)
#
# Note C++ method scoping rules:
#   module M{
#      typedef long T;
#      interface I{
#          T op(in T a);
#      };
#   };
# In the class _objref_I there is the prototype:
#    T    op(T& a)
# The implementation looks like this:
#    M::T op(T& a)
# ie in the implementation signature we must fully scope the return value.
class Method:
    def __init__(self, parent_class, name, return_type, arg_types, arg_names):
        self._parent_class = parent_class
        self._name         = name
        self._return_type  = return_type
        self._arg_types    = arg_types
        self._arg_names    = arg_names

    def parent_class(self): return self._parent_class
    def name(self):         return self._name
    def return_type(self):  return self._return_type
    def arg_types(self):    return self._arg_types
    def arg_names(self):    return self._arg_names

    # The types in the argument list never need to be fully qualified
    def __arglist(self, ignore_parameter_names = 0):
        arglist = []
        for x in range(len(self._arg_types)):
            this_arg = self._arg_types[x] + " " + self._arg_names[x]
            if ignore_parameter_names: # optional in method declaration
                this_arg = self._arg_types[x]
            arglist.append(this_arg)
        return ", ".join(arglist)

    def __return(self, fullyScope):
        if fullyScope:
            return self._return_type.op(types.RET)

        environment = self.parent_class().environment()
        return self._return_type.op(types.RET, environment)

    def hh(self, virtual = 0, pure = 0):
        args = "(" + self.__arglist(ignore_parameter_names = 0) + ")"
        proto = self.__return(fullyScope = 0) + " " + self.name()  + args
        if virtual:
            proto = "virtual " + proto
        if pure:
            proto = proto + " = 0"

        proto = proto + ";"
        return proto

    def cc(self, stream, body):
        args = "(" + self.__arglist() + ")"
        name = self.parent_class().name().fullyQualify() + "::" + self.name()
        proto = self.__return(fullyScope = 1) + " " + name + args
        stream.out("""\
@proto@
{
  @body@
}
""", proto = proto, body = body)


# dimsToString: takes a list of int dimensions (eg [1,2,3]) and a prefix string
# returning a strings "[prefix1][prefix2]..."
def dimsToString(dims, prefix = ""):
    new_dims = [ "[%s%r]" % (prefix, x) for x in dims ]
    return "".join(new_dims)


def warning(text):
    print "WARNING: " + text
