#vim: fileencoding=utf8
""" tasktools

``tasktools`` is a collection of enhancement to Python ``distutils(setuptools)``
that allow you to more easily create extra commands.

"""
from __future__ import with_statement
import re, sys, imp, os
from os.path import join, abspath, dirname
from contextlib import contextmanager
try:
  from setuptools import setup
  def _clear_commands():
    from pkg_resources import working_set as ws
    setattr(ws.by_key["setuptools"], "_ep_map", {})
except ImportError,e:
  from distutils.core import setup
  def _clear_commands(): pass
from distutils.core import Command
import distutils.dist
from distutils.dist import Distribution
import distutils.command as scommands

__version__ = "1.0.0"
__author__  = "Yusuke Inuzuka"

class _Odict(dict): keys = lambda self: sorted(dict.keys(self))
_va = dict(global_description=None, cmd_args={}, nssep="_", load_path=[])
_cmds = _Odict()
_namespace = []
_old_scommands = scommands.__all__

_get_ns = lambda:_namespace and _va["nssep"].join(_namespace)+_va["nssep"] or ""
def global_description(desc): _va["global_description"] = desc
def cmd_args(**v): _va["cmd_args"] = v
def load_path(*path): _va["load_path"] = _va["load_path"] + list(path)
def use_without_standard():
  if not use_without_standard.initialized:
    _va["nssep"] = ":"
    _clear_commands()
    scommands.__all__ = []
    distutils.dist.command_re = re.compile("[a-zA-Z0-9_:]+")
    Distribution.common_usage = ""
    def new_print_command_list (self, commands, header, max_length):
      if header == "Standard commands" and not scommands.__all__ : return
      if "extra" in header.lower() : header = "Commands"
      return self.old_print_command_list(commands, header, max_length)
    setattr(Distribution, "old_print_command_list", getattr(Distribution, "print_command_list"))
    setattr(Distribution, "print_command_list", new_print_command_list)
    use_without_standard.initialized = True
use_without_standard.initialized = False


def run():
  if _va["load_path"]:
    def tasks(path):
      result = []
      for root, dirs, files in os.walk(path):
        map(result.append, (join(root, i) for i in files if i == "tasks.py"))
      return result

    idf = "tasktools_dynamic_task%d"
    for pa in _va["load_path"]:
      for i, path in enumerate((abspath(i) for i in tasks(pa))):
        imp.load_source(idf%i, path)

  if use_without_standard.initialized and _va["global_description"]:
    _arg = sys.argv[1] if len(sys.argv) > 1 else ""
    if _arg in ["--help-commands", "--help", "-h"] and _va["global_description"]:
      print "\n".join([u"#"*60, _va["global_description"], u"#"*60])
  _va["cmd_args"].update({"cmdclass":_cmds})
  setup(**_va["cmd_args"])

class _CommandType(type):
  def __new__(cls, class_name, class_bases, classdict):
    d = dict(user_options=[], finalize_options=lambda s:None)
    d.update(classdict)
    def _(self):
      [setattr(self,i[0].rstrip("="),None) for i in d["user_options"]]
    d["initialize_options"] = _
    d["boolean_options"] = [i for i,j,k in d["user_options"] if not i.endswith("=")]
    def _(self):
      map(self.run_command, self.get_sub_commands())
      return classdict["run"](self)
    d["run"] = _
    name = _get_ns()+class_name.lower()
    cls = type.__new__(cls, name, class_bases + (object,), d)
    cls.description = cls.__doc__ and cls.__doc__.strip() or ""
    if "sub_commands" in d:
      cls.description += "\n\tsub commands:\n"+"\n".join("\t\t"+i for i,j in d["sub_commands"])
    if name in _old_scommands and name not in scommands.__all__: scommands.__all__.append(name)
    if class_name != "Task" : _cmds[name] = cls
    return cls
class Task(Command): __metaclass__ = _CommandType

@contextmanager
def namespace(name):
  _namespace.append(name)
  yield _get_ns()
  _namespace.pop() 

__all__ = filter(lambda i: not i.startswith("_"), locals())
