#!/usr/bin/env python
#
# Copyright (C) 2011-2018 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, division, absolute_import #, unicode_literals

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

import os
import re
import sys

class MyConfigParser(ConfigParser):

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

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

#
# Subroutines
#

# Makefile header
def makefile_header(name,stamp,pkgs):

  tmp_cmds = ""
  for pkg in pkgs:
    tmp_cmds += "\n\t-$(MAKE) %s" % (pkg)
    tmp_cmds += "\n\t@echo 'Checking build of %s fallback'" % (pkg)
    tmp_cmds += "\n\ttest -e stamps/%s-install-stamp" % (pkg)

  return """\
#                                                          -*- Automake -*-
# Makefile for ABINIT
# Generated by %s on %s

#
# IMPORTANT NOTE
#
# Any manual change to this file will systematically be overwritten.
# Please modify the %s script or its config file instead.
#

AM_CPPFLAGS = -I$(fallbacks_instdir)/include

-include config.mk

all-local:%s
""" % (name,stamp,name,tmp_cmds)



# Fallback template
def fallback_template(pkg,pkg_name,pkg_make,pkg_patches=None,pkg_deps=None):

  tmp_patches = ""
  for diff in pkg_patches:
    tmp_patches += "\n\t$(PATCH) -d sources/$(%s_pkg_name) -p1 <$(top_srcdir)/patches/%s" % \
      (pkg,diff)
  if ( tmp_patches == "" ):
    tmp_patches = "\n\t@echo \"No patch to apply\""

  if ( pkg_deps ):
    tmp_deplibs = "$(lib_" + "_libs) $(lib_".join(pkg_deps) + "_libs)"
  else:
    tmp_deplibs = ""

  return """\

                    ########################################

# Package: %s
%s_pkg_name = @%s_pkg_name@

if DO_BUILD_%s
%s: stamps/%s-install-stamp

stamps/%s-install-stamp: stamps/%s-build-stamp
	cd sources/$(%s_pkg_name) && $(MAKE) install @SET_MAKE@
	@touch stamps/%s-install-stamp

stamps/%s-build-stamp: stamps/%s-config-stamp
	cd sources/$(%s_pkg_name) && $(MAKE) -j %s @SET_MAKE@
	@touch stamps/%s-build-stamp

stamps/%s-config-stamp: stamps/%s-patch-stamp
	cd sources/$(%s_pkg_name) && \\
	  CPP="$(CPP)" \\
	  CPPFLAGS="$(CPPFLAGS_%s) $(INCLUDES)" \\
	  CC="$(CC)" \\
	  CFLAGS="$(CFLAGS_%s)" \\
	  CXX="$(CXX)" \\
	  CXXFLAGS="$(CXXFLAGS_%s)" \\
          FCCPP="$(TRUE_CPP)" \\
	  F77="$(FC)" \\
	  FFLAGS="$(FCFLAGS_FIXEDFORM) $(FCFLAGS_%s_EXT) $(FCFLAGS_%s) $(INCLUDES)" \\
	  F90="$(FC)" \\
	  F90FLAGS="$(FCFLAGS_FREEFORM) $(FCFLAGS_%s_EXT) $(FCFLAGS_%s) $(INCLUDES)" \\
	  FC="$(FC)" \\
	  FCFLAGS="$(FCFLAGS_FREEFORM) $(FCFLAGS_%s_EXT) $(FCFLAGS_%s) $(INCLUDES)" \\
	  LDFLAGS="$(FC_LDFLAGS_EXTRA) $(LDFLAGS_%s)" \\
	  LIBS="$(LIBS_%s) %s" \\
	  AR="$(AR)" \\
	  ARFLAGS="$(ARFLAGS)" \\
	  RANLIB="$(RANLIB)" \\
	  ac_fc_srcext="F90" \\
	  /bin/sh ./configure \\
	    --prefix="$(fallbacks_instdir)" \\
	    --bindir="$(fallbacks_instdir)/bin" \\
	    --libdir="$(fallbacks_instdir)/lib" \\
	    --program-suffix="-abinit" \\
	    $(CFGFLAGS_%s)
	@touch stamps/%s-config-stamp

stamps/%s-patch-stamp: stamps/%s-unfold-stamp%s
	@touch stamps/%s-patch-stamp

stamps/%s-unfold-stamp:
	cd sources && \\
	  gzip -cd $(abinit_tardir)/$(%s_pkg_name).tar.gz | tar xf -
	@touch stamps/%s-unfold-stamp
else
%s:
	@echo "The build of %s has been disabled"
	@touch stamps/%s-install-stamp
endif
""" % (pkg,
  pkg,pkg,
  pkg.upper(),
  pkg,pkg,
  pkg,pkg,pkg,pkg,
  pkg,pkg,pkg,pkg_make,pkg,
  pkg,pkg,pkg,
  pkg.upper(),pkg.upper(),pkg.upper(),pkg.upper(),pkg.upper(),pkg.upper(),
  pkg.upper(),pkg.upper(),pkg.upper(),pkg.upper(),pkg.upper(),tmp_deplibs,
  pkg.upper(),
  pkg,
  pkg,pkg,tmp_patches,pkg,
  pkg,pkg,pkg,
  pkg,pkg,pkg)



# Fallback clean-up template
def fallback_clean_template(pkg,name,bins=None,hdrs=None,libs=None,mods=None):
  cleans  = ""
  if ( bins ):
    cleans += "\trm -rf exports/bin/%s-abinit\n" % \
      ("-abinit exports/bin/".join(bins))
  if ( hdrs ):
    cleans += "\trm -rf exports/include/%s\n" % \
      (" exports/include/".join(hdrs))
  if ( libs ):
    cleans += "\trm -rf exports/lib/%s\n" % \
      (" exports/lib/".join(libs))
  if ( hdrs ):
    cleans += "\trm -rf exports/include/%s\n" % \
      (" exports/include/".join(mods))

  return """\

                    ########################################

# Package clean-up: %s
clean_%s:
	rm -rf sources/%s stamps/%s-*
%s
""" % (pkg,pkg,name,pkg,cleans)



# Makefile footer
def makefile_footer(pkgs):

  chk_cmds = ""
  for pkg in pkgs:
    chk_cmds += "\n\t-cd sources/$(%s_pkg_name) && $(MAKE) check @SET_MAKE@" % \
      (pkg)

  return """\

                    ########################################
                    ########################################

check-local:%s

clean-local:
	rm -rf sources/* exports/* stamps/*

""" % (chk_cmds)



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

#
# Main program
#

# Initial setup
my_name    = "make-makefiles-fallbacks"
my_configs = ["config/specs/fallbacks.conf"]

# 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 Fallbacks source tree." % my_name)
  print("%s: Aborting now." % my_name)
  sys.exit(1)

# Read config file(s)
for cnf_file in my_configs:
  if ( os.path.exists(cnf_file) ):
    if ( re.search("\.cf$",cnf_file) ):
      exec(compile(open(cnf_file).read(), cnf_file, 'exec'))
  else:
    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 fallbacks
cnf = MyConfigParser()
cnf.read(my_configs[0])
pkg_list = cnf.sections()
pkg_list.sort()

# Sort fallbacks by dependencies
all_deps = dict()
pkg_num = dict()
i = 0
for pkg in pkg_list:
  pkg_num[pkg] = i
  i += 1
for i in range(len(pkg_list)):
  for pkg in pkg_list:
    if ( cnf.has_option(pkg,"depends") ):
      all_deps[pkg] = cnf.get(pkg,"depends").split()
      pkg_deps = all_deps[pkg]
      for dep in pkg_deps:
        if ( pkg_num[dep] >= pkg_num[pkg] ):
          pkg_num[pkg] = pkg_num[dep] + 1
    else:
      all_deps[pkg] = None
pkg_ranks = list(pkg_num.values())
pkg_ranks = list(set(pkg_ranks))
pkg_ranks.sort()

# Create package list in the correct order
abinit_fallbacks = list()
for rnk in pkg_ranks:
  for pkg in pkg_num:
    if ( pkg_num[pkg] == rnk ):
      abinit_fallbacks.append(pkg)

# Init
makefile = makefile_header(my_name,now,abinit_fallbacks)

# Process each package
for pkg in abinit_fallbacks:

  # Extract package information
  pkg_name = cnf.get(pkg,"name")
  pkg_make = cnf.get(pkg,"makej")
  pkg_bins = pkg_hdrs = pkg_libs = pkg_mods = None
  if ( cnf.has_option(pkg,"binaries") ):
    pkg_bins = cnf.get(pkg,"binaries").split()
  if ( cnf.has_option(pkg,"headers") ):
    pkg_hdrs = cnf.get(pkg,"headers").split()
  if ( cnf.has_option(pkg,"libraries") ):
    pkg_libs = cnf.get(pkg,"libraries").split()
  if ( cnf.has_option(pkg,"modules") ):
    pkg_mods = cnf.get(pkg,"modules").split()

  # Get patch list
  pkg_patches = list()
  for diff in os.listdir("./patches"):
    if ( re.match(pkg_name,diff) ):
        pkg_patches.append(diff)
  pkg_patches.sort()

  # Generate templates
  makefile += fallback_template(pkg,pkg_name,pkg_make,pkg_patches,all_deps[pkg])
  makefile += fallback_clean_template(pkg,pkg_name,pkg_bins,pkg_hdrs,
                pkg_libs,pkg_mods)

# Add makefile footer
makefile += makefile_footer(abinit_fallbacks)

# FIXME: add other files to distribute
makefile += "EXTRA_DIST ="
makefile += " \\\n\tautogen.sh \\\n\twipeout.sh"
for d in ("scripts", "specs", "wannier90"):
  for f in os.listdir(os.path.join("config", d)):
    makefile += "  \\\n\t%s" % os.path.join("config", d, f)
for f in os.listdir("patches"):
  if ( re.search("\.patch$",f) ):
    makefile += " \\\n\t%s" % (os.path.join("patches",f))
for f in os.listdir("transient"):
  if ( re.search("\.tar\.gz$",f) ):
    makefile += " \\\n\t%s" % (os.path.join("transient",f))
makefile += "\n\n"

# Write makefile
mf = open("./Makefile.am", "wt")
mf.write(makefile)
mf.close()
