#!/usr/bin/env python

import os
import sys
import types

import argparse

# Create an Action subclass that prints the
# default params for us without having to force the user to query the argparse
# print_params by hand
def make_PrintParamsAction(paramString):
    class CMPPAction(argparse.Action):
        def __call__(self, parser, args, values, option_string=None):
            print(paramString)
            setattr(args, self.dest, values)
            exit(1)
    return CMPPAction

def make_ConfigAction(cm):
  class CMCAction(argparse.Action):
    def __call__(self, parser, args, values, option_string=None):
      cm.handleConfigFile(values)
  return CMCAction

class ConfigMaster:
  opt = {}

  defaultParamsHeader = "#!/usr/bin/env python\n"
  defaultParams = ""
  #optionsToIgnore = ['dt', 'os']
  parser = None

  configFilePath = None

  def setDefaultParams(self, dp):
    if dp.lstrip()[0:2] == "#!":
      self.defaultParams = dp
    else:
      self.defaultParams = self.defaultParamsHeader + dp

  def printParams(self):
    for o in self.opt:
      #if o not in self.optionsToIgnore:
        #if type(self.opt[o]) != types.ModuleType:
          print o + ": " + str(self.opt[o])
        #    print self.opt

  def assignParameters(self,p):
    global opt
    self.opt = p

  def printDefaultParams(self):
    print self.defaultParams

  def assignDefaultParams(self):
#    global defaultParams
#    self.defaultParams = dp
    exec self.defaultParams in self.opt
    del self.opt['__builtins__']
    for ko in self.opt.keys():
      if type(self.opt[ko]) == types.ModuleType:
        del self.opt[ko]    

  
  def getConfigFilePath(self):
    return self.configFilePath
        
  # config file path
  def handleConfigFile(self,cfp):
    self.configFilePath = cfp
    config_path, config_file = os.path.split(cfp)

    if config_file[-3:] == ".py":
      config_file = config_file[:-3]

    if config_path != '':
      sys.path.append(config_path)

    #print "importing config file: {}".format(config_file)
    cf = __import__(config_file, globals(), locals(), [])
    for o in self.opt:
      #print "looking at {}".format(o)
      if o in dir(cf):
        #print "found in cf: setting to {}".format(getattr(cf,o))
        self.opt[o] = getattr(cf,o)

    dcf = dir(cf)
    for cfo in dcf:
      #cfo = dcf[kcfo]
      #print cfo,type(cf.__dict__[cfo])      
      if cfo in ["__builtins__","__doc__","__file__","__name__","__package__"]:
        continue
      if type(cf.__dict__[cfo]) == types.ModuleType:
        continue
      if cfo not in self.opt:
        print "\nERROR: Invalid parameter in configuration file {}: {}\n".format(cfp,cfo)
        exit(1)
    


  def init(self, program_description=None, **kwargs):
    self.assignDefaultParams()

    self.parser = argparse.ArgumentParser(description=program_description, formatter_class=argparse.RawDescriptionHelpFormatter)

    if "add_param_args" in kwargs:
      self.addParseArgs(add_param_args = kwargs["add_param_args"])
    else:
      self.addParseArgs()

    self.handleArgParse()


  def handleArgParse(self):
    args = self.parser.parse_args()

    for o in self.opt:
      #if o in self.optionsToIgnore:
      #  continue
      if o in dir(args):
        if getattr(args,o) != None:
          #print "seting {} to {} from command line".format(o,getattr(args,o))
          self.opt[o] = getattr(args,o)

  def addParseArgs(self, add_param_args=True):
    #parser.add_argument('-c','--config', help="The configuration file.")
    self.parser.add_argument('-c', '--config', help="The configuration file.", action=make_ConfigAction(self))
    self.parser.add_argument('-p', '--print_params', action=make_PrintParamsAction(self.defaultParams),
                        nargs=0, help="Generate a default configuration file.")

    if add_param_args:
      self.addAdvancedParseArgs()

    
  def addAdvancedParseArgs(self):
    #self.addParseArgs()

    for o in self.opt:
      #print "o is {} {} {}".format(o,self.opt[o],type(self.opt[o]))
      #if o in self.optionsToIgnore:
      #  continue

      #print "Type of {} is {}".format(o,type(self.opt[o]))
      
      if isinstance(self.opt[o], bool):
        bool_parser = self.parser.add_mutually_exclusive_group(required=False)
        #print "{} is a bool".format(o)
        argument = "--" + o
        action = "store_true"
        helpString = "Set " + o + " to True"
        bool_parser.add_argument(argument, action=action, help=helpString, default=None)
        argument = "--no-" + o
        action = "store_false"
        helpString = "Set " + o + " to False"
        bool_parser.add_argument(argument, action=action, help=helpString, default=None)
      elif isinstance(self.opt[o], (int,long,float,str)):
        #print "working on {}".format(o)
        argument = "--" + o
        helpString = "Overide the param file value of " + o
        self.parser.add_argument(argument, help=helpString)