# ******************************************************
## Revision "$LastChangedDate: 2014-01-21 21:22:09 +0100 (Tue, 21 Jan 2014) $"
## Date "$LastChangedRevision: 455 $"
## Author "$LastChangedBy: arthurbeusen $"
## URL "$HeadURL: https://pbl.sliksvn.com/globalnutrients/P_model/trunk/tools/cmd_options_p.py $"
# ******************************************************

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

# 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 InputP(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
     * P_histdir
     * 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"),
                            P_histdir =os.path.join(os.getcwd(), r"p_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"),                             
                            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")                            
        parser.add_option("-p", "--P_histdir",
                          type = "string",
                          dest = "P_histdir",
                          action="store",
                          help="Input directory with historical P budget grid files")                          
        parser.add_option("-d","--ldebug",
                          dest = "ldebug",
                          action="store",
                          help="Print debug messages 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)

        # 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.P_histdir = os.path.join(self.options.root, self.options.P_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)
        self.options.soil_shield_dir = os.path.join(self.options.fix_input,self.options.soil_shield_dir)

        # Add fix_input to filenames
        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)
        self.options.s_txt  = os.path.join(self.options.fix_input,self.options.s_txt)
        self.options.Pcontent_file  = os.path.join(self.options.fix_input,self.options.Pcontent_file)
        self.options.bulkdensity_file  = os.path.join(self.options.fix_input,self.options.bulkdensity_file)

        # 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.P_bal_grass    = os.path.join(self.options.scenariodir,self.options.P_bal_grass)
        self.options.P_bal_arable   = os.path.join(self.options.scenariodir,self.options.P_bal_arable)
        self.options.P_bal_natural  = os.path.join(self.options.scenariodir,self.options.P_bal_natural)

        # This option must be redundant after a while. For downward compatiblity
        try:
            self.options.P_uptake_grass    = os.path.join(self.options.scenariodir,self.options.P_uptake_grass)
            self.options.P_uptake_arable   = os.path.join(self.options.scenariodir,self.options.P_uptake_arable)
            self.options.luptake_tot = 0
        except:
            try:
                self.options.luptake_tot = 1
                self.options.P_uptake       = os.path.join(self.options.scenariodir,self.options.P_uptake)
            except AttributeError:
                raise optparse.OptionValueError("Error: label P_uptake or (P_uptake_arable and P_uptake_grass) must be in parameters file.")

        self.options.P_aquaculture  = os.path.join(self.options.scenariodir,self.options.P_aquaculture)
        self.options.P_point        = os.path.join(self.options.scenariodir,self.options.P_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.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.slope     = os.path.join(self.options.fix_input,self.options.slope)
                
        # 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.P_bal_grass = self.grid_mult_scalar('P_bal_grass_factor',self.options.P_bal_grass)
                self.options.P_bal_arable = self.grid_mult_scalar('P_bal_arable_factor',self.options.P_bal_arable)
                self.options.P_bal_natural = self.grid_mult_scalar('P_bal_natural_factor',self.options.P_bal_natural)

                if (self.options.luptake_tot):
                    self.options.P_uptake = self.grid_mult_scalar('P_uptake_factor',self.options.P_uptake,xmin=0.0)
                else:
                    self.options.P_uptake_grass = self.grid_mult_scalar('P_uptake_grass_factor',self.options.P_uptake_grass,xmin=0.0)
                    self.options.P_uptake_arable = self.grid_mult_scalar('P_uptake_arable_factor',self.options.P_uptake_arable,xmin=0.0) 

                self.options.P_point = self.grid_mult_scalar('P_point_factor',self.options.P_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.gnpp = self.grid_mult_scalar('gnpp_factor',self.options.gnpp,xmin=0.0)
                # To get a good contribution of the Pcontent file we have to manipulate the historical data set.
                if (self.options.year > 1900.):
                    # We have to change the p_histdir for this a make a copy of all the files needed for this.
                    # First look for the last year given in the p_histdir
                    for iyear in range(self.options.year-1,1899,-1):
                        filename = os.path.join(self.options.P_histdir,"Pcontent_"+str(iyear)+".asc")
                        if (os.path.isfile(filename)):
                            # We found the last year so now we copy the files to the output directory
                            my_sys.my_copyfile(filename,os.path.join(self.options.outputdir,\
                                     "Pcontent_"+str(iyear)+".asc"),my_sys_ldebug=0,fatal=1)
                            filename = os.path.join(self.options.P_histdir,"Psoilloss_agri_"+str(iyear)+".asc")
                            my_sys.my_copyfile(filename,os.path.join(self.options.outputdir,\
                                     "Psoilloss_agri_"+str(iyear)+".asc"),my_sys_ldebug=0,fatal=1)
                            filename = os.path.join(self.options.P_histdir,"P_bal_total_"+str(iyear)+".asc")
                            my_sys.my_copyfile(filename,os.path.join(self.options.outputdir,\
                                     "P_bal_total_"+str(iyear)+".asc"),my_sys_ldebug=0,fatal=1)
                            # Now change the P_histdir to the output directory, so that the new files can be found.
                            self.options.P_histdir = self.options.outputdir
                            # Change the historical Pcontent file
                            filename = os.path.join(self.options.P_histdir,"Pcontent_"+str(iyear)+".asc")
                            qq = self.grid_mult_scalar('Pcontent_file_factor',filename,xmin=0.0)
                else:
                     self.options.Pcontent_file = self.grid_mult_scalar('Pcontent_file_factor',self.options.Pcontent_file,xmin=0.0)

                self.options.bulkdensity_file  = self.grid_mult_scalar('bulkdensity_file_factor',self.options.bulkdensity_file,xmin=0.8)
                if os.path.isfile(self.options.P_aquaculture):
                    self.options.P_aquaculture = self.grid_mult_scalar('P_aquaculture_factor',self.options.P_aquaculture,xmin=0.0)

                # Sensitivity analyses for parameters which depend on lithology
                import litho_class
                if (self.label_present('backgroundPO4_factor')):
                    for i in range(len(litho_class.litho_lib)):
                        litho_class.litho_lib[i].backgroundPO4 = self._check_min(self.options.backgroundPO4_factor * litho_class.litho_lib[i].backgroundPO4,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.makedirs(self.options.outputdir)
            
        self.validate_directory(self.options.outputdir, bool_write=True)
        self.validate_directory(self.options.P_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)
        self.validate_directory(self.options.soil_shield_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.gnpp)
        self.validate_file(self.options.s_txt)
        self.validate_file(self.options.Pcontent_file)
        self.validate_file(self.options.bulkdensity_file)
        self.validate_file(self.options.texture_erosion_file)    
        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.P_bal_grass)
        self.validate_file(self.options.P_bal_arable)
        self.validate_file(self.options.P_bal_natural)
        if (self.options.luptake_tot):
            self.validate_file(self.options.P_uptake)
        else:
            self.validate_file(self.options.P_uptake_arable)
            self.validate_file(self.options.P_uptake_grass)
        self.validate_file(self.options.P_point)

        self.validate_file(self.options.fQsro)
        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.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.temperature)
        self.validate_file(self.options.water_temperature)
        
  
        self.validate_file(self.options.slope)
        
        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.s_txt,\
                                self.options.Pcontent_file,\
                                self.options.bulkdensity_file,\
                                self.options.pgrass,\
                                self.options.igrass,\
                                self.options.cropmixp,\
                                self.options.croppasp,\
                                self.options.landarea,\
                               
                                self.options.P_bal_grass,\
                                self.options.P_bal_arable,\
                                self.options.P_bal_natural,\
                                self.options.P_point,\

                                self.options.fQsro,\
                                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.slope)
        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.P_uptake)
        else:
            equal = ascraster.compare_grids(self.options.landarea,\
                                self.options.P_uptake_arable,\
                                self.options.P_uptake_grass)

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

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