#!/usr/bin/env python
# *******************************************************************************
# Copyright (c) 2017 UT-Battelle, LLC.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# and Eclipse Distribution License v.10 which accompany this distribution.
# The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
# and the Eclipse Distribution License is available at
# https://eclipse.org/org/documents/edl-v10.php
#
# Contributors:
#   Alexander J. McCaskey - initial API and implementation
# *******************************************************************************/
import argparse
import sys
import os
import subprocess
import multiprocessing
import platform

try:
   import pyxacc as xacc
except ImportError:
   print('Error - cannot import XACC Python Bindings. Exiting')
   sys.exit()

def parse_args(args):
   parser = argparse.ArgumentParser(description="XACC Framework Utility.",
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter,
                                     fromfile_prefix_chars='@')
   parser.add_argument("-p", "--plugins", nargs='*', type=str, help="The XACC Plugins to install.", required=False)
   parser.add_argument("-a", "--extra-cmake-args", nargs='*', type=str, help="Any extra CMake arguments to drive the install.", required=False)
   parser.add_argument("-j", "--make-threads", default=4, type=int, help="Number of threads to use in make", required=False)
   parser.add_argument("-i", "--initialize", action='store_true', help="Initialize the framework.", required=False)
   parser.add_argument("-L", "--location", action='store_true', help="Print the path to the XACC install location.", required=False)
   parser.add_argument("-b", "--branch", default='master',type=str, help="Print the path to the XACC install location.", required=False)
   
   opts = parser.parse_args(args)
   return opts

ornlqci = 'https://github.com/ornl-qci'
availablePluginUrls = { 'xacc-scaffold' : ornlqci+'/xacc-scaffold',
                    'xacc-rigetti' : ornlqci+'/xacc-rigetti',
                    'xacc-dwave' : ornlqci+'/xacc-dwave',
                    'xacc-ibm' : ornlqci+'/xacc-ibm',
                    'xacc-python' : ornlqci+'/xacc-python', 
                    'tnqvm' : ornlqci+'/tnqvm',
                    'ibm' : ornlqci+'/xacc-ibm',
                    'rigetti' : ornlqci+'/xacc-rigetti',
                    'scaffold' : ornlqci+'/xacc-scaffold',
                    'dwave' : ornlqci+'/xacc-dwave', 
		    'vqe' : ornlqci+'/xacc-vqe', 
		    'xacc-dwsapi-embedding' : ornlqci+'/xacc-dwsapi-embedding', 
                    'xacc-projectq' : ornlqci+'/xacc-projectq' }

def mkdir_p(path):
    """ Operates like mkdir -p in a Unix-like system """
    try:
        os.makedirs(path)
    except OSError as e:
        if os.path.exists(path) and os.path.isdir(path):
            pass
        else:
            print("")
            print("--------------------------- ERROR -----------------------------")
            print("Cannot create directory " + path)
            print("--------------------------- ERROR -----------------------------")
            print("")
            exit()

def initialize():
   xaccHome = os.environ['HOME']+'/.xacc'
   if not os.path.exists(xaccHome):
      os.makedirs(xaccHome)

   try:
      file = open(xaccHome+'/.internal_plugins', 'r')
      contents = file.read()
   except IOError:
      file = open(xaccHome+'/.internal_plugins', 'w')
      contents = ''

   file.close()

   file = open(xaccHome+'/.internal_plugins', 'w')
   
   xaccLocation = os.path.dirname(os.path.realpath(xacc.__file__))   
   if platform.system() == "Darwin":
       libname1 = "libxacc-quantum-gate.dylib"
       libname2 = "libxacc-quantum-aqc.dylib"
   else:
       libname1 = "libxacc-quantum-gate.so"
       libname2 = "libxacc-quantum-aqc.so"

   if xaccLocation+'/lib/'+libname1+'\n' not in contents:
      file.write(xaccLocation+'/lib/'+libname1+'\n')
   if xaccLocation+'/lib/'+libname2+'\n' not in contents:
      file.write(xaccLocation+'/lib/'+libname2+'\n')
   
   file.write(contents)
   file.close()
      
def main(argv=None):
   opts = parse_args(sys.argv[1:])

   if opts.initialize:
      initialize()
      sys.exit(0)

   if not os.path.exists(os.environ['HOME']+'/.xacc/.internal_plugins'):
      initialize()
     
   # pyxacc can be installed in site-packages/xacc-...-egg or 
   # in ${XACC_DIR}/lib/python, we need to figure out which one
   xaccLocation = os.path.dirname(os.path.realpath(xacc.__file__))
   if opts.location:
       print(xaccLocation)
       sys.exit(0)
   print('XACC Install Location = ', xaccLocation)
   xacc_cwd = os.getcwd()
   
   cpus = str(multiprocessing.cpu_count())
   if not opts.make_threads == None: 
      cpus = str(opts.make_threads)

   branch = 'master'
   if not opts.branch == None:
      branch = opts.branch
      print('Installing branch = ', branch)

   # Get the plugins we're supposed to install
   plugins = opts.plugins
   cmake_args = ''

   if not opts.extra_cmake_args == None:
      for arg in opts.extra_cmake_args:
         cmake_args += '-D'+ arg + ' '

   # Loop over the plugins and install them
   for plugin in plugins:
      if plugin not in availablePluginUrls:
         print("")
         print("--------------------------- ERROR -----------------------------")
         print("Invalid plugin name - " + plugin)
         print("--------------------------- ERROR -----------------------------")
         print("")
         exit()
         
      # Create a CMakeLists.txt file
      cmakeContents = """
      project(""" + plugin + """-project LANGUAGES CXX)
      cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
      set(CMAKE_STANDARD_REQUIRED ON)
      set(CMAKE_CXX_STANDARD 14)
      set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
      set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
      include(ExternalProject)
      ExternalProject_Add("""+plugin+"""
                 GIT_REPOSITORY """+availablePluginUrls[plugin]+"""
                 GIT_TAG """+branch+"""
                 CMAKE_ARGS -DXACC_DIR="""+xaccLocation+' '+cmake_args+"""
                 BUILD_ALWAYS 1
                 INSTALL_COMMAND ${CMAKE_MAKE_PROGRAM} install
              )
          """
      # Create a build directory
      mkdir_p(plugin+'-install')
      mkdir_p(plugin+'-install/build')
      os.chdir(plugin+'-install')

      # Write the CMakeLists file 
      cmakelists = open("CMakeLists.txt", "w")
      cmakelists.write("%s" % cmakeContents)
      cmakelists.close()
      
      # Execute the build
      os.chdir('build')
      cmakecmd = ['cmake', '..']
      subprocess.check_call(cmakecmd, stderr=subprocess.STDOUT, shell=False)
      subprocess.check_call(['make', '-j'+cpus], stderr=subprocess.STDOUT, shell=False)
      
      os.chdir(xacc_cwd)

if __name__ == "__main__":
    sys.exit(main())
