

import collections
import inspect
import itertools
import os
import sys

from functools import wraps
from inspect import *
from spiralx import Props

__all__ = []

def exported(f):
  __all__.append(getattr(f, "__name__", f.func_name))
  return f

def export_names(*names):
  newnames = set(__all__) - set(names)
  __all__.extend(filter(lambda n: n in newnames, names))


# -------------------------------------

capwords = lambda s: re.sub(, lambda m: m.group(0).upper(), s)


# Regex = Props(
#   capitalise = re.compile(r"\b[a-z]"),
#   regex_escape = re.compile(r"([.?+*()[\]])")
# )

# def make_regex_safe(s):

#   r = Regex.regex_escape
#   return r.sub(r"\\1", s)

export_names("capwords")

def _asstrings(x=None):
  """
  Always returns a list containing zero or more strings.
    >>> _asstrings("a string")
    ['a string']
    >>> _asstrings("comma,separated,values")
    ['comma', 'separated', 'values']
    >>> _asstrings(["moo"])
    ['moo']
    >>> _asstrings(["moo", "list", 6])
    ['moo', 'list', 6]
    >>> _asstrings(None)
    []
    >>> _asstrings()
    []

  """

  if isinstance(x, str):
    return x.split(",")
  elif isinstance(x, collections.Sequence):
    return x
  else:
    return []


def _getTextWrapper():
  from textwrap import TextWrapper
  tw = TextWrapper(width=100, subsequent_indent = "  ")

  def subwrapper(tw):
    base_indent = tw.subsequent_indent
    sw = TextWrapper(width=100,
                     initial_indent=base_indent,
                     subsequent_indent=base_indent+"  ")
    sw.wrap = result_redirector(dest=tw.wrap)

@exported
def clsinfo(obj):
  return classify_class_attrs(obj if isclass(obj) else type(obj))

@exported
def result_redirector(dest=sys.stdin.write, return_dest_result=False, return_default=None):

  def do_data_redirect(result_data):
    if not dest:
      return return_default
    if isinstance(dest, str):
      try:
        tgt = eval(dest)
        if inspect.isroutine(tgt):
          dest = tgt
      except:
        return return_default

    try:
      newresult = dest(result_data)
      print("Passed {0} to {1}, replacement result is {2}".format(result_data, dest, newresult))
      if return_dest_result:
        return newresult
    except:
      pass

    return return_default

  def decorator(func):

    @wraps(func)
    def wrapper(*a, **kw):
      res = func(*a, **kw)
      return do_data_redirect(res)

  return decorator


@exported
def listjoin(l, s="\n", st=unicode):
  return s.join(map(st, l))

@exported
def printitems(iter, name=None):
  itemlist = list(iter)
  name = name or getattr(iter, "__name__", None) or "Items"
  print("{0} (len = {1}):\n{2}\n\n".format(name, len(itemlist), listjoin(itemlist)))

@exported
def syspath():
  import sys
  printitems(sys.path, "sys.path")

@exported
def ospath():
  #print("\n".join(os.environ["path"].split(";")))
  printitems(os.environ["path"], "%PATH%")


"""
def FlagGen(initial=1):
  cur = initial

  flags = []
  while True:
    flags.append(cur)
    names = (yield cur)

    if val is not None:

    cur *= 2


  cur = (

class OptionalArgumentTypes:

  KeyPredicate,
  ValuePredicate,
  TypePredicate,
  Predicate,
  ExternalCondition,

  _Predicates =
"""

@exported
def attach(value, name="attached"):

  def decorator(func):

    @wraps(func)
    def wrapper(*a, **kw):
      kw.set(name, value)
      return func(*a, **kw)

    return wrapper

  return decorator



class AllowsOptionalArguments(object):
  """
  Decorator wrapper for functions which accept all kinds of
  random arguments we might use - filter predicates, sort or
  grouping functions for output etc. etc. This sorts them
  out, combines and stores them and just passes on the basics
  to the wrapped func.
  """

  def __init__(self, passthru=None, argtypes="All"):
    self.argtypes = argtypes
    self.passthru = _asstrings(passthru)
    self.passkeyset = frozenset(self.passthru)

  def __call__(self, func):
    # Wrapper around func basically discards any params that
    # don't match self.passthru, attaches this decorator
    # to func as "argument_deco"

    print("func is %s" % (repr(func)))
    argspec = getargspec(func)
    print(argspec)

    argument_deco = self

    @wraps(func)
    #@attach(self, "argument_deco")
    def wrapper(*args, **kwargs):
      argument_deco.collect_args(args, kwargs)

      da = dict((k, v) for k, v in zip(argspec.args, args))
      print("*args = %s" % da)
      kwargs.update((k, v) for k, v in zip(argspec.args, args))

      okkeys =  self.passkeyset.intersection(set(kwargs.keys()))

      fargs = dict((k,kwargs[k]) for k in okkkeys)
      #ok = filter(lambda k, v: k in self.passthru, kwargs.items())
      print(fargs)

      return func(**fargs)

    return wrapper

  def collect_args(self, args, kwargs):
    print(args)
    print(kwargs)



def predfix(**kwargs):
  accepters = {
    "kpred": [lambda k: True],
    "vpred":  [lambda v: True],
    "vtpred": [lambda vt: True],
    "extpred": [lambda: True],
  }
  for kw, param in kwargs.items():
    accepters[kw].push(param)
    if kw == "pred":
      accepters["kpred"].push(param)
      accepters["vpred"].push(param)
  return Predicates(accepters["kpred"], accepters["vpred"], accepters["vtpred"], accepters["extpred"])



_accepter = lambda x: True




@exported
def dir2(obj, pred=_accepter, kpred=_accepter, tpred=_accepter):
  """Return all members of an object as (name, value) pairs sorted by name.
  Optionally, only return members that satisfy a given predicate."""
  #preds = predfix(pred=pred, kpred=kpred, vpred=vpred)
  results = []
  for key in filter(kpred, dir(object)):
    value = getattr(object, key)
    if pred(value) and tpred(type(value)):
      results.append((key, value))
  results.sort()
  return results



@exported
@AllowsOptionalArguments(passthru="obj")
def localdir(obj):
  if isclass(obj):
    fcattrs = filter(lambda att: att.defining_class is myt,
      inspect.classify_class_attrs(o))
  items = map(lambda i: (i.name, i.value), fcattrs)
  items.sort()
  return items

  #localnames = map(operator.attrgetter("name"), fcattrs)
  #return filter(lambda k,v: k in localnames, dir2(o))


@exported
def clsinfo(o, sortkey=None):
  if type(o) is not type:
    o = type(o)
  cattrs = inspect.classify_class_attrs(o)
  if sortkey:
    return sorted(cattrs, key=lambda att: getattr(att, sortkey, None))
  return sorted(cattrs)




#@exported
def sortitems(items, keyfunc):
  itemtokey = _assortkeyfunc(keyfunc)
  return sorted(items, key=itemtokey)


# @exported
# def groupitems(items, keyfunc):
#   class SortedList(list):
#     def add(self, val):
#       list.append(self, val)
#       list.sort(self)
#     append = add
#  class SubGroup(list):
#    def __init__(self):
#      self.items = []
#
#    count = __len__
#    def additem(self, i):
#      list.append(self, i)
#
#      self.items.append(i)
#
#    def sortitems(self, keyfunc=None):
#      kf = _assortkeyfunc(keyfunc)
#      self.items.sort(key=kf)
#      return self.items
#
#   groupsbykey = collections.defaultdict(SortedList)
#   itemtokey = _assortkeyfunc(keyfunc)
#   keymapper = lambda obj: (itemtokey(obj), obj)
#
#   for key, item in map(keymapper, items):
#     groupsbykey.get(key).append(item)
#
#   return groupsbykey


@exported
def groupitems(items, keyfunc=lambda x: x):
  print(items)
  groupsbykey = collections.defaultdict(list)
  keymapper = lambda obj: (keyfunc(obj), obj)

  for key, item in map(keymapper, items):
    groupsbykey[key].append(item)

  return groupsbykey


getattrs = exported(lambda obj, *keys: [getattr(obj, k) for k in keys])


def run_tests():
  import doctest
  doctest.testmod()


# ------------------------------------------

print("End of module quick!")
printitems(__all__, name="__all__")

if __name__ == "__main__":
  import doctest
  doctest.testmod()
