"""usage: import metawrap as metakit
use just like metakit with the following changes:
print view ->         outputs the metakit dump version of a view, useful 
for debugging
view.columns() -> list the attributes (columns) of the view
view.asDict() -> return the row dictionaries
print row ->          prints the contents of a row
row.asDict() ->     returns a dictionary of the row's values.
                           The downside is that you can't have an 
attribute "asDict" in the row.
"""
import metakit, sys, StringIO, types

# Implementation notes: The hardest thing to understand here is the
# _view class below.  The trick is that sometimes a metakit function
# returns a seperate view which we would also like to wrap up in our
# handy view class.
#
# This is done by "intercepting" these functions (see VIEW_RETURNS below)
# with a special class named _view
# This class keeps track of the view associated with the function and
# the name of the function called.
# When the function is actually called, _view.__call__ is called
# with the appropriate parameters.  The result of applying the
# function with these parameters is then wrapped up in a view class
# and returned.  *whew*!
#
# you can always add methods to replace these though, for
# example you can add a view.append() method to replace the default
# append.

class MetaError(Exception): pass
def dump(view):
    stdout = sys.stdout
    sys.stdout = StringIO.StringIO()
    metakit.dump(view)
    text = sys.stdout.getvalue()
    sys.stdout = stdout
    return text
   
class storage:
    def __init__(self, filename=None, mode=None):
        if filename and mode:
            self.__db = metakit.storage(filename, mode)
        elif filename and not mode:
            raise MetaError("Need to supply both a filename and a mode")
        else:
            self.__db = metakit.storage()
    def __getattr__(self, attribute): return getattr(self.__db, attribute)
    def getas(self, table): return view(self.__db.getas(table))
    def view(self, viewname): return view(self.__db.view(viewname))

# these functions return views as well
VIEW_RETURNS = ["indices", "rview", "select",
                "project", "flatten", "join", "unique", "intersect",
                "different", "minus", "remapwith", "pair", "rename",
                "product", "groupby", "counts", "blocked", "hashed",
                "sort", "sortrev"]

class _view:
    """This class wraps views returned by view method functions."""
    def __init__(self, vw, st, funcname):
        self.vw = vw
        self.st = st
        self.func = func
    def __call__(self, *a, **kw):
        return view(vw=apply(getattr(self.vw, self.funcname), a, kw),
                    st=self.st)
   
class view:
    def __init__(self, vw=None, st=None):
        if vw is None: vw = metakit.view()
        self.__view = vw
        self.__storage = storage
    def __getattr__(self, attribute):
        if attribute in VIEW_RETURNS: return _view(self.__view, self.__storage, attribute)
        else: return getattr(self.__view, attribute)
    def __len__(self): return len(self.__view)
    def __getslice__(self, lo, hi): return view(self.__view[lo:hi])
    def __delslice__(self, lo, hi):  del self.__view[lo:hi]
    def __getitem__(self, index): 
	if type(index) == types.TupleType:
	    _row, col = index
	    return getattr(self[_row], col)
	return row(self.__view[index], self)
    def __str__(self): return dump(self)
    def columns(self): return [x.name for x in self.__view.structure()]
    def asDict(self): return [row.asDict() for row in self]

class row:
    def __init__(self, rw, vw):
        self.__rw = rw
        self.__vw = vw
    def __repr__(self):
        res = ["Row with attributes:"]
        for prop in self.__vw.structure():
            res.append("\t%s:\t%s"%(prop.name, getattr(self.__rw, prop.name)))
        return "\n".join(res)
    def __getattr__(self, column):
        if column[0:6] == "_row__": return self.__dict__[column]
        else: return getattr(self.__rw, column)
    def __setattr__(self, column, value):
        if column[0:6] == "_row__": self.__dict__[column] = value
        else: setattr(self.__rw, column, value)
    def asDict(self):
        res = {}
        for prop in self.__vw.structure():
            res[prop.name] = getattr(self.__rw, prop.name)
        return res
