# ******************************************************
## Revision "$LastChangedDate: 2014-01-18 14:05:11 +0100 (Sat, 18 Jan 2014) $"
## Date "$LastChangedRevision: 454 $"
## Author "$LastChangedBy: arthurbeusen $"
## URL "$HeadURL: https://pbl.sliksvn.com/globalnutrients/N_model/trunk/tools/cmd_options_n.py $"
# ******************************************************

import os
import sys
import optparse
import getpass
import time
import ascraster
import cmd_options_general

# Switch debug messages on or off
_debug = False

def set_debug(val):
    global _debug
    _debug = val

def _dbg(msg):
    if _debug:
        print "DBG-->", msg

class InputN(cmd_options_general.Input,object):
    """Parse arguments and store them using the optparse module.
    
     Pass sys.argv into class: Input(sys.argv).
     Options will be included in 'options' object of instance of Input().
     Call options using:
     
     <instance>.options.<option>
     
     Available options are:
     * root
     * file_mask
     * scenarioname
     * scenariodir
     * year
     * outputdir
     * parameter_ini
     * water_inputdir
     * ldebug
     * aggregationgrid
     * lss_grw
     * inifile
     
     Fixed options:
     
     All other commandline arguments will be stored in <instance>.args
    """
    def __init__(self,list_args):

        # Extract scriptname to set working directory
        scriptname = list_args.pop(0)

        # If an inifile is provided, integrate values into list_args
        if "--inifile" in list_args:
            # Insert inifile arguments before other commandline arguments.
            # This way commandline options prevail over inifile options
            print "Inifile option found in the command line."
            list_args = self._parse_inifile(list_args) + list_args    

        usage = "usage: call_python %prog [options]"
        _dbg("Argument list: %s" % list_args)
        

        # Change working directory to one level above script
        os.chdir(os.path.join(os.path.split(scriptname)[0],".."))
        _dbg("Current workdir: %s" % os.getcwd())
        
        # Initialisation of the OptionParser
        parser = optparse.OptionParser(prog=scriptname,usage=usage)      
               
        # Set defaults for test mode
        parser.set_defaults(root = os.getcwd(),
                            parameter_ini = os.path.join(os.getcwd(), os.path.join("tools","parameters.ini")),
                            scenarioname = "default",
                            scenariodir = os.path.join(os.getcwd(), r"../../scen_input"),
                            year = 2000,
                            outputdir = os.path.join(os.getcwd(), r"output"),
                            N_histdir =os.path.join(os.getcwd(), r"n_histdir"),
                            aggregationgrid = None,
                            water_inputdir = os.path.join(os.getcwd(), r"../../water_input"),
                            scen_water_inputdir = os.path.join(os.path.join(os.getcwd(), r"../../water_input"),"2000"),                           
                            lss_grw = 0,
                            ldebug = 0
                            )
        parser.add_option("-r", "--root",
                          type = "string",
                          dest = "root",
                          action="store",
                          help="Working directory for N model")
        parser.add_option("-m", "--file_mask",
                          type = "string",
                          dest = "file_mask",
                          action="store",
                          help="Full path to ascii grid to be used as mask")
        parser.add_option("-i", "--parameter_ini",
                          type = "string",
                          dest = "parameter_ini",
                          action="store",
                          help="Full path to file with all 'constant' parameter values like filenames etc.")                            
        parser.add_option("-n", "--scenarioname",
                          type = "string",
                          dest = "scenarioname",
                          action="store",
                          help="Scenario name")
        parser.add_option("-s", "--scenariodir",
                          type = "string",
                          dest = "scenariodir",
                          action="store",
                          help="Directory name of scenario input")                          
        parser.add_option("-y", "--year",
                          type = "int",
                          dest = "year",
                          action="store",
                          help="Year of the simulation")
        parser.add_option("-w", "--water_inputdir",
                          type = "string",
                          dest = "water_inputdir",
                          action="store",
                          help="Input directory with the water information")
        parser.add_option("-W", "--scen_water_inputdir",
                          type = "string",
                          dest = "scen_water_inputdir",
                          action="store",
                          help="Input directory with scenario input with the water information")
        parser.add_option("-o", "--outputdir",
                          type = "string",
                          dest = "outputdir",
                          action="store",
                          help="Output directory")
        parser.add_option("-a", "--aggregationgrid",
                          type = "string",
                          dest = "aggregationgrid",
                          action="store",
                          help="File name of grid to aggregate tabular output information on, multiple files separated by ,")
        parser.add_option("-p", "--N_histdir",
                          type = "string",
                          dest = "N_histdir",
                          action="store",
                          help="Input directory with historical N leaching grid files")                            
        parser.add_option("-d","--ldebug",
                          dest = "ldebug",
                          action="store",
                          help="Print debug messages 1: yes, 0: no")
        parser.add_option("-g","--lss_grw",
                          dest = "lss_grw",
                          action="store",
                          help="Steady state groundater flow 1: yes, 0: no")                          
        parser.add_option("--inifile",
                          type = "string",
                          dest = "inifile",
                          action="store",
                          help="""Path to ini-file.
                          In inifile use commandline options, followed by '=', followed by argument, e.g. --outputdir = OECD.
                          Comments must be preceded by '#'. Everything after '#' on a line will be ingnored by the parser"""
                          )

        (self.options, self.args) = parser.parse_args(args=list_args)
        # Make the same object as self.options, but with only the variable input options.
        # This is needed for writing in the log file.
        (self.options_var, self.args1) = parser.parse_args(args=list_args)
        
        # This is to use all the directory information, which is given by the user
        if self.options.file_mask == None:
           self.options.lmask = False
        else:   
           self.options.lmask = True
               
        # Set ldebug and lss_grw as int parameters:
        self.options.ldebug = int(self.options.ldebug)
        self.options.lss_grw = int(self.options.lss_grw)

        # Expand all file names to an absolute reference
        self.options.root = os.path.join(os.getcwd(), self.options.root)
        self.options.outputdir = os.path.join(self.options.root, self.options.outputdir)
        self.options.N_histdir = os.path.join(self.options.root, self.options.N_histdir)
        self.options.scenariodir = os.path.join(self.options.root, self.options.scenariodir)
        self.options.water_inputdir = os.path.join(self.options.root, self.options.water_inputdir)
        self.options.scen_water_inputdir = os.path.join(self.options.root, self.options.scen_water_inputdir)

        if (self.options.aggregationgrid != None):
            self.options.aggregationgrid_number = 0
            # Make a number of attributes
            filenames = self.options.aggregationgrid.split(",")
            for item in range(len(filenames)):
                setattr(self.options,"aggregationgrid"+str(self.options.aggregationgrid_number),\
                          os.path.join(self.options.root, filenames[item]))
                self.options.aggregationgrid_number += 1                

        if (self.options.lmask):
            self.options.file_mask = os.path.join(self.options.scenariodir,self.options.file_mask)   
            
        # Read all the default parameter settings out of the parameter.ini
        # Here all the other semi constant parameters for the model are set!
        self._parse_parameter_inifile(self.options,self.options.parameter_ini)

        self.options.fix_input = os.path.join(self.options.root,self.options.fix_input)
        self.options.glwd_dir = os.path.join(self.options.fix_input,self.options.glwd_dir)
        self.options.litho_dir = os.path.join(self.options.fix_input,self.options.litho_dir)

        # Add all the directories to the filenames
        self.options.pgrass   = os.path.join(self.options.scenariodir,self.options.pgrass)
        self.options.igrass   = os.path.join(self.options.scenariodir,self.options.igrass)
        self.options.cropmixp = os.path.join(self.options.scenariodir,self.options.cropmixp)
        self.options.croppasp = os.path.join(self.options.scenariodir,self.options.croppasp)
        self.options.landarea = os.path.join(self.options.scenariodir,self.options.landarea)
        
        self.options.N_bal_grass    = os.path.join(self.options.scenariodir,self.options.N_bal_grass)
        self.options.N_bal_arable   = os.path.join(self.options.scenariodir,self.options.N_bal_arable)
        self.options.N_bal_natural  = os.path.join(self.options.scenariodir,self.options.N_bal_natural)
        # This option must be redundant after a while. For downward compatiblity
        try:
            self.options.N_uptake_grass    = os.path.join(self.options.scenariodir,self.options.N_uptake_grass)
            self.options.N_uptake_arable   = os.path.join(self.options.scenariodir,self.options.N_uptake_arable)
            self.options.luptake_tot = 0
        except:
            try:
                self.options.luptake_tot = 1
                self.options.N_uptake       = os.path.join(self.options.scenariodir,self.options.N_uptake)
            except AttributeError:
                raise optparse.OptionValueError("Error: label N_uptake or (N_uptake_arable and N_uptake_grass) must be in parameters file.")

        self.options.N_deposition      = os.path.join(self.options.scenariodir,self.options.N_deposition)
        self.options.N_aquaculture     = os.path.join(self.options.scenariodir,self.options.N_aquaculture)
        self.options.N_point           = os.path.join(self.options.scenariodir,self.options.N_point)
        
        self.options.temperature       = os.path.join(self.options.scen_water_inputdir,self.options.temperature)
        self.options.water_temperature       = os.path.join(self.options.scen_water_inputdir,self.options.water_temperature)
        self.options.pnet              = os.path.join(self.options.scen_water_inputdir,self.options.pnet)
        self.options.water_storage     = os.path.join(self.options.scen_water_inputdir,self.options.water_storage)
        self.options.flooding_depth    = os.path.join(self.options.scen_water_inputdir,self.options.flooding_depth)
        self.options.flooding_fraction = os.path.join(self.options.scen_water_inputdir,self.options.flooding_fraction)
        self.options.wetland_runoff    = os.path.join(self.options.scen_water_inputdir,self.options.wetland_runoff)
        self.options.wetland_fraction  = os.path.join(self.options.scen_water_inputdir,self.options.wetland_fraction)
        self.options.discharge         = os.path.join(self.options.scen_water_inputdir,self.options.discharge)


        self.options.fQsro         = os.path.join(self.options.water_inputdir,self.options.fQsro)
        self.options.fQgw          = os.path.join(self.options.water_inputdir,self.options.fQgw)
        self.options.ldd           = os.path.join(self.options.water_inputdir,self.options.ldd)
        self.options.basin         = os.path.join(self.options.water_inputdir,self.options.basin)
        self.options.mouth         = os.path.join(self.options.water_inputdir,self.options.mouth)
        self.options.lakeid         = os.path.join(self.options.water_inputdir,self.options.lakeid)
        self.options.fraction_water = os.path.join(self.options.water_inputdir,self.options.fraction_water)
        self.options.water_area     = os.path.join(self.options.water_inputdir,self.options.water_area)
        self.options.outlakeid      = os.path.join(self.options.water_inputdir,self.options.outlakeid)
        self.options.cellarea       = os.path.join(self.options.water_inputdir,self.options.cellarea)
        self.options.endo_lakes     = os.path.join(self.options.water_inputdir,self.options.endo_lakes)
        self.options.coast_distance = os.path.join(self.options.water_inputdir,self.options.coast_distance)
        
        self.options.tawcd     = os.path.join(self.options.fix_input,self.options.tawcd)
        self.options.tawcs     = os.path.join(self.options.fix_input,self.options.tawcs)
        self.options.s_oc      = os.path.join(self.options.fix_input,self.options.s_oc)
        self.options.d_oc      = os.path.join(self.options.fix_input,self.options.d_oc)
        self.options.s_txt     = os.path.join(self.options.fix_input,self.options.s_txt)
        self.options.d_txt     = os.path.join(self.options.fix_input,self.options.d_txt)
        self.options.s_drain   = os.path.join(self.options.fix_input,self.options.s_drain)
        self.options.d_drain   = os.path.join(self.options.fix_input,self.options.d_drain)
        self.options.slope     = os.path.join(self.options.fix_input,self.options.slope)
        self.options.s_ph      = os.path.join(self.options.fix_input,self.options.s_ph)
        self.options.gnpp                   = os.path.join(self.options.fix_input,self.options.gnpp)
        self.options.texture_erosion_file   = os.path.join(self.options.fix_input,self.options.texture_erosion_file)
                
        # Switch debugging on/off
        set_debug(self.options.ldebug)
        # If no arguments are provided, print usage
        if len(list_args) == 0:
            print parser.print_help()
            raise parser.error("No arguments provided.")
                       
        # Display all options in case of debug.
        _dbg("Start checking the command line arguments")
        # Write all settings to screen
        if self.options.ldebug:
            _dbg("Command line arguments found:")
            _dbg(self.options)

        # Check given input parameters
        self.run_checks()

        # This code is only used when there is a sensitivity analyse performed.
        if (self.label_present('lsensitivity')):
            # Test whether this label is one, then sensitivity analyses is performed.
            if (self.options.lsensitivity == 1):
                print "SENSITIVITY ANALYSES IS PERFORMED."
                self.options.fQsro = self.grid_mult_scalar('fQsro_factor',self.options.fQsro,xmin=0.0, xmax=1.0)
                # Pnet and discharged must be changed the same.
                self.options.pnet = self.grid_mult_scalar('pnet_factor',self.options.pnet)
                self.options.discharge = self.grid_mult_scalar('pnet_factor',self.options.discharge)
                self.options.N_bal_grass = self.grid_mult_scalar('N_bal_grass_factor',self.options.N_bal_grass)
                self.options.N_bal_arable = self.grid_mult_scalar('N_bal_arable_factor',self.options.N_bal_arable)
                self.options.N_bal_natural = self.grid_mult_scalar('N_bal_natural_factor',self.options.N_bal_natural)
                if (self.options.luptake_tot):
                    self.options.N_uptake = self.grid_mult_scalar('N_uptake_factor',self.options.N_uptake,xmin=0.0)
                else:
                    self.options.N_uptake_grass = self.grid_mult_scalar('N_uptake_grass_factor',self.options.N_uptake_grass,xmin=0.0)
                    self.options.N_uptake_arable = self.grid_mult_scalar('N_uptake_arable_factor',self.options.N_uptake_arable,xmin=0.0) 
                self.options.N_point = self.grid_mult_scalar('N_point_factor',self.options.N_point,xmin=0.0)
                # Now air temperature and water temperature are changed the same.
                self.options.temperature = self.grid_add_scalar('temperature_factor',self.options.temperature)
                self.options.water_temperature = self.grid_add_scalar('temperature_factor',self.options.water_temperature)

                self.options.flooding_depth = self.grid_mult_scalar('flooding_depth_factor',self.options.flooding_depth,xmin=0.0)
                self.options.water_storage = self.grid_mult_scalar('water_storage_factor',self.options.water_storage,xmin=0.0)
                self.options.flooding_fraction = self.grid_mult_scalar('flooding_fraction_factor',self.options.flooding_fraction,xmin=0.0,xmax=1.0)
                self.options.fQgw = self.grid_mult_scalar('fQgw_factor',self.options.fQgw,xmin=0.0, xmax=1.0)
                self.options.N_deposition = self.grid_mult_scalar('N_deposition_factor',self.options.N_deposition,xmin=0.0)
                if os.path.isfile(self.options.N_aquaculture):
                    self.options.N_aquaculture = self.grid_mult_scalar('N_aquaculture_factor',self.options.N_aquaculture,xmin=0.0)
                self.options.gnpp = self.grid_mult_scalar('gnpp_factor',self.options.gnpp,xmin=0.0)

                # Sensitivity analyses for parameters which depend on lithology
                import litho_class
                if (self.label_present('porosity_factor')):
                    for i in range(len(litho_class.litho_lib)):
                        litho_class.litho_lib[i].porosity = self._check_range(self.options.porosity_factor * litho_class.litho_lib[i].porosity,0.0,1.0)
                if (self.label_present('dt50_deep_factor')):
                    for i in range(len(litho_class.litho_lib)):
                        # The dt50_deep is made dependent on dt50_shallow, because the default value for dt50_deep is zero.
                        litho_class.litho_lib[i].dt50_deep = self._check_min(self.options.dt50_deep_factor * litho_class.litho_lib[i].dt50_shallow,0.0)                         
                if (self.label_present('dt50_shallow_factor')):
                    for i in range(len(litho_class.litho_lib)):
                        litho_class.litho_lib[i].dt50_shallow = self._check_min(self.options.dt50_shallow_factor * litho_class.litho_lib[i].dt50_shallow,0.0)                         
       

    def run_checks(self):
        """Check options
        
        """
        self.validate_directory(self.options.root, bool_write=False)
       
        # Create new output directory if necessary
    	if not os.path.exists(self.options.outputdir):
            os.mkdir(self.options.outputdir)
            
        self.validate_directory(self.options.outputdir, bool_write=True)
        self.validate_directory(self.options.N_histdir, bool_write=False)
        self.validate_directory(self.options.scenariodir, bool_write=False)
        self.validate_directory(self.options.water_inputdir, bool_write=False)
        self.validate_directory(self.options.scen_water_inputdir, bool_write=False)
        self.validate_directory(self.options.fix_input, bool_write=False)
        self.validate_directory(self.options.glwd_dir, bool_write=False)
        self.validate_directory(self.options.litho_dir, bool_write=False)
               
        # Check the existence of all given input files                      
        if (self.options.lmask):
            self.validate_file(self.options.file_mask)
            
        self.validate_file(self.options.pgrass)
        self.validate_file(self.options.igrass)
        self.validate_file(self.options.cropmixp)
        self.validate_file(self.options.croppasp)
        self.validate_file(self.options.landarea)

        self.validate_file(self.options.N_bal_grass)
        self.validate_file(self.options.N_bal_arable)
        self.validate_file(self.options.N_bal_natural)
        if (self.options.luptake_tot):
            self.validate_file(self.options.N_uptake)
        else:
            self.validate_file(self.options.N_uptake_arable)
            self.validate_file(self.options.N_uptake_grass)
        self.validate_file(self.options.N_point)
        self.validate_file(self.options.N_deposition)

        self.validate_file(self.options.fQsro)
        self.validate_file(self.options.fQgw)
        self.validate_file(self.options.pnet)
        self.validate_file(self.options.water_storage)
        self.validate_file(self.options.flooding_depth)
        self.validate_file(self.options.flooding_fraction)
        self.validate_file(self.options.wetland_fraction)
        self.validate_file(self.options.wetland_runoff)
        # Validate of discharge is not needed.

        self.validate_file(self.options.gnpp)
        self.validate_file(self.options.texture_erosion_file)

        self.validate_file(self.options.ldd)
        self.validate_file(self.options.basin)
        self.validate_file(self.options.mouth)
        self.validate_file(self.options.lakeid)
        self.validate_file(self.options.fraction_water)
        self.validate_file(self.options.water_area)
        self.validate_file(self.options.outlakeid)
        self.validate_file(self.options.cellarea)
        self.validate_file(self.options.endo_lakes)
        self.validate_file(self.options.coast_distance)

        self.validate_file(self.options.temperature)
        self.validate_file(self.options.water_temperature)
        
        self.validate_file(self.options.tawcd)
        self.validate_file(self.options.tawcs)
        self.validate_file(self.options.s_oc)
        self.validate_file(self.options.d_oc)
        self.validate_file(self.options.s_txt)
        self.validate_file(self.options.d_txt)
        self.validate_file(self.options.s_drain)
        self.validate_file(self.options.d_drain)        
        self.validate_file(self.options.slope)
        self.validate_file(self.options.s_ph)
        
        if (self.options.aggregationgrid != None):
            for item in range(self.options.aggregationgrid_number):
                filename = getattr(self.options,"aggregationgrid"+str(item))
                self.validate_file(filename)
                equal = ascraster.compare_grids(self.options.landarea,\
                                                filename)
                if (not equal):
                    raise optparse.OptionValueError("Error: Aggregation file "+ filename +\
                                                    " has not the right projection.")
        
        # Check whether the ascraster files have the same header.
        equal = ascraster.compare_grids(self.options.gnpp,\
                                self.options.pgrass,\
                                self.options.igrass,\
                                self.options.cropmixp,\
                                self.options.croppasp,\
                                self.options.landarea,\

                                self.options.N_bal_grass,\
                                self.options.N_bal_arable,\
                                self.options.N_bal_natural,\
                                self.options.N_point,\
                                self.options.N_deposition,\

                                self.options.fQsro,\
                                self.options.fQgw,\
                                self.options.pnet,\
                                self.options.water_storage,\
                                self.options.flooding_depth,\
                                self.options.flooding_fraction,\
                                self.options.wetland_fraction,\
                                self.options.wetland_runoff,\

                                self.options.ldd,\
                                self.options.basin,\
                                self.options.temperature,\
                                self.options.water_temperature,\
                                self.options.lakeid,\
                                self.options.fraction_water,\
                                self.options.water_area,\
                                self.options.outlakeid,\
                                self.options.cellarea,\
                                self.options.endo_lakes,\
                                self.options.coast_distance,\

                                self.options.tawcd,\
                                self.options.tawcs,\
                                self.options.s_oc,\
                                self.options.d_oc,\
                                self.options.s_txt,\
                                self.options.d_txt,\
                                self.options.s_drain,\
                                self.options.d_drain,\
                                self.options.slope,\
                                self.options.s_ph)
        if (not equal):
            raise optparse.OptionValueError("Error: not all input maps have the same projections.")

        if (self.options.lmask):
            equal = ascraster.compare_grids(self.options.landarea,\
                                            self.options.file_mask)
            if (not equal):
                raise optparse.OptionValueError("Error: mask map has not the right projection.")

        if (self.options.luptake_tot):
            equal = ascraster.compare_grids(self.options.landarea,\
                                self.options.N_uptake)
        else:
            equal = ascraster.compare_grids(self.options.landarea,\
                                self.options.N_uptake_arable,\
                                self.options.N_uptake_grass)

        if (not equal):
            raise optparse.OptionValueError("Error: not all uptake maps have the same projections.")

        if os.path.isfile(self.options.N_aquaculture):
            # The file exist so check whether grid header is correct.
            equal = ascraster.compare_grids(self.options.landarea,\
                                self.options.N_aquaculture)
            if (not equal):
                raise optparse.OptionValueError("Error: not all aquaculture maps have the same projections.")
        


