#!/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)



# -----------------------------
def filePresent(paths,files,warning=False):

  for path in paths:
    for file in files:
      if os.path.isfile(os.path.join(path,file)): return True

  if warning: print "Warning: %s not found in %s"%(','.join(files),','.join(paths))

  return False


########################################################
# 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 = 'ifort' if subprocess.call(['which', 'ifort'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 \
             else 'gfortran'

#--- default option values  --------------------------------------------------------------------------
BLAS_order = ['IMKL','ACML','LAPACK','OPENBLAS']

defaults={'DAMASK_BIN':'depending on access rights',
          'F90':compiler,
          'FFTW_ROOT':'/usr',
          'MSC_ROOT' :'/msc',
          'DAMASK_NUM_THREADS':4,
          'MARC_VERSION':'2015',
          'blasType':'LAPACK',
          'blasRoot':{'LAPACK'   :'/usr',
                      'ACML'     :'/opt/acml6.1.0',
                      'IMKL'     : os.getenv('MKLROOT') if os.getenv('MKLROOT') else '/opt/intel/composerxe/mkl',
                      'OPENBLAS' :'/usr',
                     },
          'spectralOptions':{},
         }



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

#--- set default values according to read in values  ------------------------------------------------
try:
  with open(configFile,'r') as f:
    print('\n<<<<< reading default values from %s\n'%configFile)
    for line in f:
      line = line.strip()
      if line.startswith('#') or line == '':
        pass

      [key,value] = (re.split('[= ]',line)+[None,None])[:2]
      
      if key == 'DAMASK_NUM_THREADS':
        defaults['DAMASK_NUM_THREADS'] = int(value)
      if key == 'DAMASK_BIN':
        defaults['DAMASK_BIN'] = value
      if key in ['F90','FFTW_ROOT','MSC_ROOT','spectralOptions','MARC_VERSION']:
        defaults[key] = value
      for theKey in reversed(BLAS_order):
        if key == theKey+'_ROOT' and value != None and value != '':
          defaults['blasType'] = theKey
          defaults['blasRoot'][theKey] = value
except IOError:
  pass

parser.add_option('--prefix',                dest='prefix', metavar='string',
                                             help='location of (links to) DAMASK executables [%default]')
parser.add_option('--with-FC','--with-fc',               
                                             dest='compiler', metavar='string',
                                             help='F90 compiler [%default]')
parser.add_option('--with-FFTW-dir','--with-fftw-dir',         
                                             dest='fftwRoot', metavar='string',
                                             help='root directory of FFTW [%default]')
parser.add_option('--with-MSC-dir','--with-msc-dir',          
                                             dest='mscRoot', metavar='string',
                                             help='root directory of MSC.Marc/Mentat [%default]')
parser.add_option('--with-MARC-version','--with-marc-version',          
                                             dest='marcVersion',  metavar='string',
                                             help='version of MSC.Marc/Mentat [%default]')
parser.add_option('--with-OMP-threads','--with-omp-threads',      
                                             dest='threads', type='int', metavar='int',
                                             help='number of openMP threads [%default]')
parser.add_option('--with-BLAS-type','--with-blas-type',         
                                             dest='blasType', metavar='string',
                                             help='type of BLAS/LAPACK library [%default] {{{}}}'.format(','.join(BLAS_order)))
parser.add_option('--with-BLAS-dir','--with-blas-dir',        
                                             dest='blasRoot',metavar='string',
                                             help='root directory of BLAS/LAPACK library [%default]')
parser.add_option('--with-spectral-options', dest='spectraloptions', action='extend', metavar='<string LIST>',
                                             help='options for compilation of spectral solver')

parser.set_defaults(prefix      = defaults['DAMASK_BIN'])
parser.set_defaults(compiler    = defaults['F90'])
parser.set_defaults(fftwRoot    = defaults['FFTW_ROOT'])
parser.set_defaults(mscRoot     = defaults['MSC_ROOT'])
parser.set_defaults(marcVersion = defaults['MARC_VERSION'])
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.lower().startswith('--with-blas-type'):
    if arg.lower().endswith('--with-blas-type'): 
      blasType = sys.argv[i+1].upper()
    else:
      blasType = sys.argv[i][17:].upper()
if blasType not in BLAS_order:
  blasType = defaults['blasType'].upper()

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

(options,filenames) = parser.parse_args()

#--- consistency checks --------------------------------------------------------------------------------
options.compiler = options.compiler.lower()
options.blasType = options.blasType.upper() 
options.fftwRoot = os.path.normpath(options.fftwRoot)
options.mscRoot  = os.path.normpath(options.mscRoot)
options.blasRoot = os.path.normpath(options.blasRoot)

locations = {
              'FFTW' :    [os.path.join(options.fftwRoot,'lib64'),os.path.join(options.fftwRoot,'lib')],
              'LAPACK' :  [os.path.join(options.blasRoot,'lib64'),os.path.join(options.blasRoot,'lib')],
              'OPENBLAS': [os.path.join(options.blasRoot,'lib64'),os.path.join(options.blasRoot,'lib')],
              'ACML' :    [os.path.join(options.blasRoot,'%s64/lib'%options.compiler)],
              'ACML_mp' : [os.path.join(options.blasRoot,'%s64_mp/lib'%options.compiler)],
              'IMKL' :    [os.path.join(options.blasRoot,'lib/intel64')],
}

libraries = {
              'FFTW' : [
                        'libfftw3.so','libfftw3.a',
                        'libfftw3_threads.so','libfftw3_threads.a',
                       ],
              'LAPACK' : [
                        'liblapack.so','liblapack.a','liblapack.dylib',
                       ],
              'OPENBLAS' : [
                        'libopenblas.so','libopenblas.a','libopenblas.dylib',
                       ],
              'ACML' : [
                        'libacml.so','libacml.a',
                       ],
              'ACML_mp' : [
                        'libacml_mp.so','libacml_mp.a',
                       ],
              'IMKL' : [
                        '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 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)

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


filePresent(locations['FFTW'],libraries['FFTW'],warning=True)

if options.blasType in ['LAPACK','OPENBLAS','IMKL']:
  filePresent(locations[options.blasType],libraries[options.blasType],warning=True)
elif options.blasType == 'ACML':
  filePresent(locations[options.blasType],libraries[options.blasType],warning=True)
  filePresent(locations[options.blasType+'_mp'],libraries[options.blasType+'_mp'],warning=True)
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:
      line = line.strip()
      items = re.split('[= ]',line)
    
      if (not line or items[0].startswith('#')):
        pass
      if items[0] == 'DAMASK_BIN':
        line = '%s=%s'%(items[0],options.prefix)
        options.prefix ='depending on access rights'
      if items[0] == 'F90':
        line = '%s=%s'%(items[0],options.compiler)
        options.compiler =''
      if items[0] == 'FFTW_ROOT':
        line = '%s=%s'%(items[0],options.fftwRoot)
        options.fftwRoot =''
      if items[0] == 'MSC_ROOT':
        line = '%s=%s'%(items[0],options.mscRoot)
        options.mscRoot =''
      if items[0] == 'MARC_VERSION':
        line = '%s=%s'%(items[0],options.marcVersion)
        options.marcVersion =''
      if items[0] == 'DAMASK_NUM_THREADS':
        line = '%s=%s'%(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'%(items[0],options.blasRoot)
          options.blasType=''
        elif items[0] == '#%s_ROOT'%blasType and items[0] == '#%s_ROOT'%options.blasType:
          line = '%s=%s'%(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'%(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 == 'prefix' and value != 'depending on access rights':
    output.append('DAMASK_BIN=%s'%value)
  if opt == 'compiler' and value != '':
    output.append('F90=%s'%value)
  if opt == 'fftwRoot' and value != '':
    output.append('FFTW_ROOT=%s'%value)
  if opt == 'mscRoot' and value != '':
    output.append('MSC_ROOT=%s'%value)
  if opt == 'marcVersion' and value != '':
    output.append('MARC_VERSION=%s'%value)
  if opt == 'threads' and value != '':
    output.append('DAMASK_NUM_THREADS=%s'%value)
  if opt == 'blasType' and value != '':
    output.append('%s_ROOT=%s'%(options.blasType,options.blasRoot))

for spectralOption in options.spectraloptions:
  output.append(spectralOption)

#--- decide where do save the data -------------------------------------------------------------------
configDir = '/etc' if os.access('/etc/', os.W_OK) \
                   else os.path.join(os.getenv('HOME'),'.damask')           # use system-wide config if possible
configFileNew = os.path.join(configDir,'damask.conf')

if not os.path.isdir(configDir):
  os.mkdir(configDir)

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