##
# Copyright 2009-2019 Ghent University
#
# This file is part of EasyBuild,
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
# with support of Ghent University (http://ugent.be/hpc),
# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be),
# Flemish Research Foundation (FWO) (http://www.fwo.be/en)
# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en).
#
# https://github.com/easybuilders/easybuild
#
# EasyBuild is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation v2.
#
# EasyBuild is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with EasyBuild.  If not, see <http://www.gnu.org/licenses/>.
##
"""
EasyBuild support for building and installing Julia packages, implemented as an easyblock

@author: Victor Holanda (CSCS)
@author: Samuel Omlin (CSCS)
minor adjustments by Jens Henrik Goebbert (JSC)
"""
import os
import sys

import easybuild.tools.toolchain as toolchain

from easybuild.framework.easyconfig import CUSTOM
from easybuild.framework.extensioneasyblock import ExtensionEasyBlock
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.run import run_cmd, parse_log_for_error


class JuliaPackage(ExtensionEasyBlock):
    """
    Install an Julia package as a separate module, or as an extension.
    """

    @staticmethod
    def extra_options(extra_vars=None):
        if extra_vars is None:
            extra_vars = {}

        extra_vars.update({
            'system_name': [None, "Change julia's Project.toml pathname", CUSTOM],
            'arch_name': [None, "Change julia's Project.toml pathname", CUSTOM],
            'packagespec': [None, "Overwrite install options for Pkg.add(PackageSpec(<packagespec>))", CUSTOM],
            'mpiexec': [None, "Set the mpiexec command", CUSTOM],
            'mpiexec_args': [None, "Set the mpiexec command args", CUSTOM],
            'mpi_path': [None, "Set the MPI installation path", CUSTOM],
            'mpicc': [None, "Set mpicc command", "mpicc"],
        })
        return ExtensionEasyBlock.extra_options(extra_vars=extra_vars)

    def __init__(self, *args, **kwargs):
        super(JuliaPackage, self).__init__(*args, **kwargs)
        self.package_name = self.name
        names = self.package_name.split('.')
        if len(names) > 1:
            self.package_name = ''.join(names[:-1])

        julia_env_name = os.getenv('EBJULIA_ENV_NAME', '')
        self.depot = os.path.join(self.installdir, 'extensions')
        self.projectdir = os.path.join(self.depot, 'environments', julia_env_name)
        self.log.info("Depot for package installations: %s" % self.depot)

    def patch_step(self, beginpath=None):
        pass

    def fetch_sources(self, sources=None, checksums=None):
        pass

    def extract_step(self):
        """Source should not be extracted."""
        pass

    def configure_step(self):
        """No configuration for installing Julia packages."""
        pass

    def build_step(self):
        """No separate build step for Julia packages."""
        pass

    def make_julia_cmd(self, remove=False):
        """Create a command to run in julia to install an julia package."""

        if self.cfg['packagespec']:
            package_spec = self.cfg['packagespec']
        else:
            package_spec = "name=\"%s\", version=\"%s\"" % (self.package_name, self.version)

        pre_cmd = '%s unset EBJULIA_USER_DEPOT_PATH && unset EBJULIA_ADMIN_DEPOT_PATH && export JULIA_DEPOT_PATH=%s && export JULIA_PROJECT=%s' % (self.cfg['preinstallopts'], self.depot, self.projectdir)

        if self.cfg['mpi_path']:
            pre_cmd += ' && export JULIA_MPI_BINARY=system'
            pre_cmd += ' && export JULIA_MPI_PATH="%s"' % self.cfg['mpi_path']

        if self.cfg['mpiexec']:
            pre_cmd += ' && export JULIA_MPIEXEC="%s"' % self.cfg['mpiexec']

        if self.cfg['mpiexec_args']:
            pre_cmd += ' && export JULIA_MPIEXEC_ARGS="%s"' % self.cfg['mpiexec_args']

        if self.cfg['mpicc']:
            pre_cmd += ' && export JULIA_MPICC="%s"' % self.cfg['mpicc']

        if self.cfg['arch_name'] == 'gpu':
            pre_cmd += ' && export JULIA_CUDA_USE_BINARYBUILDER=false'

        if remove:
            cmd = ' && '.join([pre_cmd, "julia --eval 'using Pkg; Pkg.rm(PackageSpec(%s))'" % package_spec])
        else:
            cmd = ' && '.join([pre_cmd, "julia --eval 'using Pkg; Pkg.add(PackageSpec(%s))'" % package_spec])

        return cmd

    def install_step(self):
        """Install procedure for Julia packages."""

        cmd = self.make_julia_cmd(remove=False)
        cmdttdouterr, _ = run_cmd(cmd, log_all=True, simple=False, regexp=False)

        cmderrors = parse_log_for_error(cmdttdouterr, regExp="^ERROR:")
        if cmderrors:
            cmd = self.make_julia_cmd(remove=True)
            run_cmd(cmd, log_all=False, log_ok=False, simple=False, inp=sys.stdin, regexp=False)
            raise EasyBuildError("Errors detected during installation of Julia package %s!", self.name)

        self.log.info("Julia package %s installed succesfully" % self.name)

    def run(self):
        """Install Julia package as an extension."""
        self.install_step()

    def sanity_check_step(self, *args, **kwargs):
        """
        Custom sanity check for Julia packages
        """
        #NOTE: we don't use Pkg.status with arguments as only supported for Julia >=v1.1
        cmd = "unset EBJULIA_USER_DEPOT_PATH && unset EBJULIA_ADMIN_DEPOT_PATH && export JULIA_DEPOT_PATH=%s && export JULIA_PROJECT=%s && julia --eval 'using Pkg; Pkg.status()'" % (self.depot, self.projectdir)
        cmdttdouterr, _ = run_cmd(cmd, log_all=True, simple=False, regexp=False)
        self.log.error("Julia package %s sanity returned %s" % (self.name, cmdttdouterr))
        return len(parse_log_for_error(cmdttdouterr, regExp="%s\s+v%s" % (self.package_name, self.version))) != 0