#!/usr/bin/env python
import os,re,sys,string,subprocess,shutil

from optparse import OptionParser, Option

# -----------------------------
class extendableOption(Option):
# -----------------------------
# used for definition of new option parser action 'extend', which enables to take multiple option arguments
# taken from online tutorial http://docs.python.org/library/optparse.html
  
  ACTIONS = Option.ACTIONS + ("extend",)
  STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
  TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
  ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",)

  def take_action(self, action, dest, opt, value, values, parser):
    if action == "extend":
      lvalue = value.split(",")
      values.ensure_value(dest, []).extend(lvalue)
    else:
      Option.take_action(self, action, dest, opt, value, values, parser)



########################################################
# MAIN
########################################################

parser = OptionParser(option_class=extendableOption, usage='%prog options', description = """
Configures the compilation and installation of DAMASK

""" + string.replace('$Id$','\n','\\n')
)

#--- determine default compiler ----------------------------------------------------------------------
compiler = os.getenv('F90')
if compiler == None: compiler ={True:'ifort',False:'gfortran'}[\
                                subprocess.call(['which', 'ifort'],\
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0]

#--- default option values  --------------------------------------------------------------------------
defaults={'F90':compiler,\
          'FFTW_ROOT':'/usr/local',\
          'MSC_ROOT' :'/msc',\
          'HDF5_ROOT':'/usr/local',\
          'DAMASK_NUM_THREADS':4,\
          'blasType':'LAPACK',\
          'blasRoot':{'LAPACK':'/usr',\
                      'ACML'  :'/opt/acml5.3.1',\
                      'IMKL'  :{True:'/opt/intel/composerxe/mkl',False:os.getenv('MKLROOT')}\
                               [os.getenv('MKLROOT')==None]},\
          'spectralOptions':{}}

#--- if local config file exists, read, otherwise assume global config file  ------------------------
if os.path.isfile(os.path.join(os.getenv('HOME'),'.damask/damask.conf')):
  configFile = os.path.join(os.getenv('HOME'),'.damask/damask.conf')
else:
  configFile = '/etc/damask.conf'

#--- set default values according to read in values  ------------------------------------------------
try:
  with open(configFile,'r') as f:
    print('\n-----\n reading default values from %s\n-----'%configFile)
    for line in f:
      items = re.split('[= ]',line)
    
      if (not line.strip() or items[0].startswith('#')):
        pass
      elif items[0] == 'F90':
        defaults['F90']=items[1].rstrip()
      elif items[0] == 'FFTW_ROOT': 
        defaults['FFTW_ROOT']=items[1].rstrip()
      elif items[0] == 'MSC_ROOT': 
        defaults['MSC_ROOT']=items[1].rstrip()
      elif items[0] == 'HDF5_ROOT': 
        defaults['HDF5_ROOT']=items[1].rstrip()
      elif items[0] == 'DAMASK_NUM_THREADS': 
        defaults['DAMASK_NUM_THREADS']=int(items[1])
      elif items[0] in [name+'_ROOT' for name in defaults['blasRoot']]:
        defaults['blasType']=items[0][:-5]
        defaults['blasRoot'][items[0][:-5]] =items[1].rstrip()
      else:
        defaults['spectralOptions'][items[0]]=items[1].rstrip()
except IOError:
  pass


parser.add_option('--with-fc',               dest='compiler', type='string', metavar='string', \
                                             help='F90 compiler [%default]')
parser.add_option('--with-fftw-dir',         dest='fftwRoot', type='string', metavar='string', \
                                             help='root directory of FFTW [%default]')
parser.add_option('--with-msc-dir',          dest='mscRoot', type='string', metavar='string', \
                                             help='root directory of MSC.Marc/Mentat [%default]')
parser.add_option('--with-HDF5-dir',         dest='hdf5Root', type='string', metavar='string', \
                                             help='root directory of HDF5 [%default]')
parser.add_option('--with-OMP-threads',      dest='threads', type='int', metavar='int',\
                                             help='number of openMP threads [%default]')
parser.add_option('--with-blas-type',        dest='blasType', type='string', metavar='string', \
                                             help='type of BLAS/LAPACK library [%default]')
parser.add_option('--with-blas-dir',         dest='blasRoot', type='string', metavar='string', \
                                             help='root directory of BLAS/LAPACK library [%default]')
parser.add_option('--with-spectral-options', dest='spectraloptions', type='string', action='extend', metavar='<string LIST>', \
                                             help='options for compilation of spectral solver')

parser.set_defaults(compiler = defaults['F90'])
parser.set_defaults(fftwRoot = defaults['FFTW_ROOT'])
parser.set_defaults(mscRoot  = defaults['MSC_ROOT'])
parser.set_defaults(hdf5Root = defaults['HDF5_ROOT'])
parser.set_defaults(threads  = defaults['DAMASK_NUM_THREADS'])
parser.set_defaults(blasType = defaults['blasType'])

#--- set default for blasRoot depending on current option (or default) for blasType  --------------------

blasType = defaults['blasType'].upper()
for i, arg in enumerate(sys.argv):
  if arg.startswith('--with-blas-type'):
    if arg.endswith('--with-blas-type'): 
      blasType = sys.argv[i+1].upper()
    else:
      blasType = sys.argv[i][17:].upper()
if blasType not in ['LAPACK','ACML','IMKL']:
  blasType='LAPACK'

parser.set_defaults(blasRoot = [defaults['blasRoot'][blasType]][0])
parser.set_defaults(spectraloptions = [])

(options,filenames) = parser.parse_args()

#--- consistency checks --------------------------------------------------------------------------------
options.compiler=options.compiler.lower()
options.blasType=options.blasType.upper() 
options.fftwRoot=options.fftwRoot.rstrip('/')
options.mscRoot =options.mscRoot.rstrip('/')
options.hdf5Root=options.hdf5Root.rstrip('/') 
options.blasRoot=options.blasRoot.rstrip('/') 

if options.compiler not in ['ifort','gfortran']: 
  print('Error: Unknown compiler option: %s'%options.compiler)
  sys.exit(1)

if not subprocess.call(['which', options.compiler],stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:
  print('Compiler Warning: executable %s not found!'%options.compiler)

fftwLocations =[os.path.join(options.fftwRoot,'lib'),os.path.join(options.fftwRoot,'lib64')]
for lib in ['libfftw3.so','libfftw3.a',\
            'libfftw3_threads.so','libfftw3_threads.a']:
  if (not os.path.isfile(os.path.join(fftwLocations[0],lib))) and \
     (not os.path.isfile(os.path.join(fftwLocations[1],lib))):
    print('FFTW Warning: %s not found in %s or %s!'%(lib,fftwLocations[0],fftwLocations[1]))

if not os.path.isdir(options.mscRoot):
  print('MSC.Marc/Mentat Warning: Root directory %s not found!'%options.mscRoot)

if options.blasType == 'LAPACK':
  lapackLocations =[os.path.join(options.blasRoot,'lib'),os.path.join(options.blasRoot,'lib64')]
  for lib in ['liblapack.so','liblapack.a']:
    if (not os.path.isfile(os.path.join(lapackLocations[0],lib))) and \
       (not os.path.isfile(os.path.join(lapackLocations[1],lib))):
      print('LAPACK Warning: %s not found in %s or %s!'%(lib,lapackLocations[0],lapackLocations[1]))
elif options.blasType == 'ACML':
  blasLocationSerial=os.path.join(options.blasRoot,'%s64/lib'%options.compiler)
  for lib in ['libacml.so','libacml.a']:
    if not os.path.isfile(os.path.join(blasLocationSerial,lib)): 
      print('ACML Warning: %s not found in %s!'%(lib,blasLocationSerial))
  blasLocationParallel=os.path.join(options.blasRoot,'%s64_mp/lib'%options.compiler)
  for lib in ['libacml_mp.so','libacml_mp.a']:
    if not os.path.isfile(os.path.join(blasLocationParallel,lib)): 
      print('ACML Warning: %s not found in %s!'%(lib,blasLocationParallel))
elif options.blasType == 'IMKL':
  blasLocation=os.path.join(options.blasRoot,'lib/intel64')
  for lib in ['libmkl_core.so','libmkl_core.a',\
              'libmkl_sequential.so','libmkl_sequential.a',\
              'libmkl_intel_thread.so','libmkl_intel_thread.a',\
              'libmkl_intel_lp64.so','libmkl_intel_lp64.a',\
              'libmkl_gnu_thread.so','libmkl_gnu_thread.a',\
              'libmkl_gf_lp64.so','libmkl_gf_lp64.a']:
    if not os.path.isfile(os.path.join(blasLocation,lib)): 
      print('IMKL Warning: %s not found in %s!'%(lib,blasLocation))
else:
  print('Error: Unknown BLAS/LAPACK library: %s'%options.blasType)
  sys.exit(1)


#--- read config file if present to keep comments and order ---------------------------------------
output = []
try: 
  with open(configFile,'r') as f:
    for line in f:
      items = re.split('[= ]',line)
    
      if (not line.strip() or items[0].startswith('#')):
        pass
      if items[0] == 'F90':
        line = '%s=%s\n'%(items[0],options.compiler)
        options.compiler =''
      if items[0] == 'FFTW_ROOT':
        line = '%s=%s\n'%(items[0],options.fftwRoot)
        options.fftwRoot =''
      if items[0] == 'MSC_ROOT':
        line = '%s=%s\n'%(items[0],options.mscRoot)
        options.mscRoot =''
      if items[0] == 'HDF5_ROOT':
        line = '%s=%s\n'%(items[0],options.hdf5Root)
        options.hdf5Root =''
      if items[0] == 'DAMASK_NUM_THREADS':
        line = '%s=%s\n'%(items[0],options.threads)
        options.threads =''
      for blasType in defaults['blasRoot'].keys():
        if items[0] == '%s_ROOT'%blasType and items[0] == '%s_ROOT'%options.blasType:
          line = '%s=%s\n'%(items[0],options.blasRoot)
          options.blasType=''
        elif items[0] == '#%s_ROOT'%blasType and items[0] == '#%s_ROOT'%options.blasType:
          line = '%s=%s\n'%(items[0][1:],options.blasRoot)
          options.blasType=''
        elif items[0] == '%s_ROOT'%blasType: line = '#'+line
      for spectralOption in options.spectraloptions:
        [key,value] = re.split('[= ]',spectralOption)[0:2]
        if key == items[0]:
          line = '%s=%s\n'%(items[0],value)
          options.spectraloptions.remove(spectralOption)
      output.append(line)
except IOError:
  pass

#--- write remaining options --------------------------------------------------------------------------
for opt, value in options.__dict__.items():
  if opt == 'compiler' and value !='':
    output.append('F90=%s\n'%value)
  if opt == 'fftwRoot' and value !='':
    output.append('FFTW_ROOT=%s\n'%value)
  if opt == 'mscRoot' and value !='':
    output.append('MSC_ROOT=%s\n'%value)
  if opt == 'hdf5Root' and value !='':
    output.append('HDF5_ROOT=%s\n'%value)
  if opt == 'threads' and value !='':
    output.append('DAMASK_NUM_THREADS=%s\n'%value)
  if opt == 'blasType' and value !='':
    output.append('%s_ROOT=%s\n'%(options.blasType,options.blasRoot))

for spectralOption in options.spectraloptions:
  output.append(spectralOption+'\n')

#--- decide where do save the data ----------------------------------------------------------------------
root=os.access('/etc/', os.W_OK)
if root:
  configFileNew = '/etc/damask.conf'
else:
  configFileNew = os.path.join(os.getenv('HOME'),'.damask/damask.conf')
  if not os.path.isdir(os.path.join(os.getenv('HOME'),'.damask')):
    os.mkdir(os.path.join(os.getenv('HOME'),'.damask'))

print('\n-----\n writing values to %s\n-----'%configFileNew)
with open(configFileNew,'w') as f:
  for line in output:
    print(line.rstrip('\n'))
    f.write(line)