#!/usr/bin/env python
#
# Copyright (C) 2006-2014 ABINIT Group (Yann Pouillon)
#
# This file is part of the ABINIT software package. For license information,
# please see the COPYING file in the top-level directory of the ABINIT source
# distribution.
#

from __future__ import print_function

try:
    from configparser import ConfigParser,NoOptionError
except ImportError:
    from ConfigParser import ConfigParser,NoOptionError
from time import gmtime,strftime

import os
import re
import sys

if ( sys.version_info.major == 3 ):
    from subprocess import getoutput
else:
    from commands import getoutput

class MyConfigParser(ConfigParser):

  def optionxform(self,option):
    return str(option)



# ---------------------------------------------------------------------------- #

#
# Subroutines
#

# Macro header
def macro_header(name,stamp):

  return """# Generated by %s on %s

#
# Environment variables relevant to ABINIT
#

#
# IMPORTANT NOTE
#
# This file has been automatically generated by the %s
# script. Any change will systematically be overwritten.
#
""" % (name,stamp,name)



# Autoconf macro template
def abi_macro_autoconf():

  return """


# AFB_ENV_AC_UPDATE(LANG)
# ---------------------------------
#
# Updates Autoconf build environment.
#
AC_DEFUN([AFB_ENV_AC_UPDATE],[
  dnl Please note that the existing environment will always be overwritten.
  dnl Thus you should save sensistive data before calling this macro.

  dnl Check arguments
  m4_if([$1], [tmp_lang="$1"], [tmp_lang="Fortran"])dnl

  dnl Update linker flags
  case "$1" in
    C)
      LDFLAGS="${CC_LDFLAGS}"
      ;;
    C++)
      LDFLAGS="${CXX_LDFLAGS}"
      ;;
    Fortran)
      LDFLAGS="${FC_LDFLAGS}"
      ;;
  esac

  dnl Update linker additional libs
  LIBS="${CC_LIBS}"
  CXXLIBS="${CXX_LIBS}"
  FCLIBS="${FC_LIBS}"

  unset tmp_lang
]) # AFB_ENV_AC_UPDATE
"""



# Define macro template
def abi_macro_init():

  return """


# AFB_ENV_INIT()
# --------------
#
# Declares ABINIT environment variables and initializes the internal
# variables.
#
AC_DEFUN([AFB_ENV_INIT],[
@MACRO@
]) # AFB_ENV_INIT
"""



# Backup macro template
def abi_macro_backup():

  return """


# AFB_ENV_BACKUP()
# ----------------
#
# Saves all ABINIT environment variables.
#
AC_DEFUN([AFB_ENV_BACKUP],[
  dnl All variables will be saved, yet please note that they may be
  dnl conditionally restored (see AFB_ENV_RECALL for details).
@MACRO@
]) # AFB_ENV_BACKUP
"""



# Recall macro template
def abi_macro_recall():

  return """


# AFB_ENV_RECALL()
# ----------------
#
# Restores all previously-saved non-empty ABINIT environment variables.
#
AC_DEFUN([AFB_ENV_RECALL],[
  dnl The following ensures that non-empty environment variables always override
  dnl what is read from the config files.
@MACRO@
]) # AFB_ENV_RECALL
"""



# Restore macro template
def abi_macro_restore():

  return """


# AFB_ENV_RESTORE()
# -----------------
#
# Restores all previously-saved ABINIT environment variables.
#
AC_DEFUN([AFB_ENV_RESTORE],[
@MACRO@
]) # AFB_ENV_RESTORE
"""



# ---------------------------------------------------------------------------- #

#
# Main program
#

# Initial setup
my_name    = "make-macros-environment"
my_configs = ["config/specs/environment.conf"]
my_output  = "config/m4/auto-environment.m4"

# Check if we are in the top of the ABINIT source tree
if ( not os.path.exists("configure.ac") or
     not os.path.exists("config/specs/fallbacks.conf") ):
  print("%s: You must be in the top of an ABINIT source tree." % my_name)
  print("%s: Aborting now." % my_name)
  sys.exit(1)

# Read config open(s)
for cnf_file in my_configs:
  if ( not os.path.exists(cnf_file) ):
    print("%s: Could not find config file (%s)." % (my_name,cnf_file))
    print("%s: Aborting now." % my_name)
    sys.exit(2)

# What time is it?
now = strftime("%Y/%m/%d %H:%M:%S +0000",gmtime())

# Init
cnf = MyConfigParser()
cnf.read(my_configs[0])
abinit_env_vars = cnf.sections()
abinit_env_vars.sort()

# Start writing macro
m4 = open(my_output,"w")
m4.write(macro_header(my_name,now))

# Write Autoconf macro
m4.write(abi_macro_autoconf())

# Process environment variables for init
src = ""
for var in abinit_env_vars:
  dsc = cnf.get(var,"description")
  try:
    val = cnf.get(var,"value")
  except NoOptionError:
    val = ""
  src += "\n  dnl %s\n" % (dsc)
  if ( cnf.get(var,"declare") == "yes" ):
    src += "  AC_ARG_VAR([%s],\n    [%s])\n" % (var,dsc)
  if ( cnf.get(var,"reset") == "yes" ):
    src += "  %s='%s'\n" % (var,val)
  if ( cnf.get(var,"substitute") == "yes" ):
    src += "  AC_SUBST(%s)\n" % (var)

# Write init macro
m4.write(re.sub("@MACRO@",src,abi_macro_init()))

# Process environment variables for backup
src = ""
for var in abinit_env_vars:
  src += "\n  dnl Save %s\n" % (cnf.get(var,"description"))
  src += "  abi_env_%s=\"${%s}\"\n" % (var,var)

# Write backup macro
m4.write(re.sub("@MACRO@",src,abi_macro_backup()))

# Process environment variables for recall
src = ""
for var in abinit_env_vars:
  src += "\n  dnl Recall %s\n" % (cnf.get(var,"description"))
  src += "  if test \"${abi_env_%s}\" != \"\"; then\n" % (var) \
      +  "    test \"${%s}\" != \"${abi_env_%s}\" && \\\n" % (var,var) \
      +  "      AC_MSG_NOTICE([overriding configuration of %s from environment])\n" % (var) \
      +  "    %s=\"${abi_env_%s}\"\n" % (var,var) \
      +  "  fi\n"

# Write recall macro
m4.write(re.sub("@MACRO@",src,abi_macro_recall()))

# Process environment variables for restore
src = ""
for var in abinit_env_vars:
  src += "\n  dnl Restore %s\n" % (cnf.get(var,"description"))
  src += "  %s=\"${abi_env_%s}\"\n" % (var,var)

# Write restore macro
m4.write(re.sub("@MACRO@",src,abi_macro_restore()))

# Finish
m4.close()
tmp = getoutput("./config/scripts/add-header-typed Autoconf %s" % (my_output))
if ( tmp != "" ):
  print(tmp)

# Write environment dumper (for debugging)
dumper = open("fallbacks.dump.in","a")
dumper.write("\n                    # ------------------------------------ #\n")
dumper.write("\n# Environment variables (script: %s)\n" % (my_name))
for var in abinit_env_vars:
  dumper.write("%s=\"@%s@\"\n" % (var,var))
dumper.close()
