#!/bin/bash
# /*******************************************************************************
# Copyright Intel Corporation.
# This software and the related documents are Intel copyrighted materials, and your use of them
# is governed by the express license under which they were provided to you (License).
# Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose
# or transmit this software or the related documents without Intel's prior written permission.
# This software and the related documents are provided as is, with no express or implied warranties,
# other than those that are expressly stated in the License.
#
# *******************************************************************************/

METADATA=0
SUMMARY=0
API=0

FPGA_STATUS=0
DISTRO_STATUS=-1
RELEASE_STATUS=-1
GCC_VER_STATUS=-1




function parse_args() {
    if [[ "$#" != 1 ]]; then
		exit 1
    else
        if [[ "$1" == "--get_metadata" ]]; then
			METADATA=1
        elif [[ "$1" == "--get_summary" ]]; then
			SUMMARY=1
        elif [[ "$1" == "--get_api_version" ]]; then
			API=1
        else
            exit 1
        fi
	fi

}

function run() {
    output='{\"Value\": {\"gcc is installed\": {\"Command\": \"which gcc\",'
    if [[ -z $(which gcc) ]]; then
        output="${output}\\\"Value\\\": \\\"No\\\",\\\"RetVal\\\": \\\"FAIL\\\",\\\"Message\\\": \\\"Intel(R) oneAPI DPC++ Compiler requires gcc to be installed.\\\"},"
    else
        output="${output}\\\"Value\\\": \\\"Yes\\\",\\\"RetVal\\\": \\\"PASS\\\"}, \\\"gcc version\\\": {\\\"Command\\\": \\\"gcc -dumpversion\\\","
        gversion=`gcc -dumpversion`
        GCC_VER=${gversion}
        if [[ ${gversion:0:1} -lt 5 ]] || [[ ${gversion:0:1} -eq 5 && ${gversion:2:1} -lt 1 ]]; then
            output="${output}\\\"Value\\\": ${gversion},\\\"RetVal\\\": \\\"FAIL\\\",\\\"Message\\\": \\\"Intel(R) oneAPI DPC++ Compiler requires gcc version 5.1 or higher.\\\"},"
        else
            output="${output}\\\"Value\\\": ${gversion},\\\"RetVal\\\": \\\"PASS\\\"},"
            GCC_VER_STATUS=0
        fi
    fi
    # Check if the user is on a supported OS
    # If they are on RHEL7 we only work with a non-standard gcc so check for that case as well
    message=""
    howtofix=""

    if type lsb_release >/dev/null 2>&1; then
      DISTRO=$(lsb_release -is)
      DISTRO_STATUS=$?
      RELEASE=$(lsb_release -rs)
      RELEASE_STATUS=$?
    elif [ -f /etc/os-release ]; then
      . /etc/os-release
      if [ -z $ID ]; then
        DISTRO_STATUS=-1
      else
        DISTRO=$ID
        DISTRO_STATUS=0
      fi
      if [ -z $VERSION_ID ]; then
        RELEASE_STATUS=-1
      else
        RELEASE=$VERSION_ID
        RELEASE_STATUS=0
      fi
    fi

    DISTRO=${DISTRO,,}

    if [[ $DISTRO_STATUS != 0 ]] || [[ $RELEASE_STATUS != 0 ]]; then
        message="${message}Warning: Unable to determine OS. The FPGA backend may not work correctly. "
        FPGA_STATUS=-1
    elif [ $GCC_VER_STATUS != 0 ]; then
        message="${message}Warning: Unable to determine gcc version. The FPGA backend may not work correctly. "
        FPGA_STATUS=-1
    else
      if [ "$DISTRO" == "redhatenterpriseserver" ] || [ "$DISTRO" == "rhel" ] || [ "$DISTRO" == "centos" ] && [[ $RELEASE =~ ^7 ]]; then
        if [ "$GCC_VER" != "7.4.0" ]; then
            message="${message}Warning: the gcc version found on your path is not 7.4.0. The FPGA backend requires libstdc++ from gcc 7.4.0."
            howtofix="${howtofix}If you have installed gcc in a non-standard location you can point the compiler to that location with the --gcc-toolchain flag."
            FPGA_STATUS=-1
        else
            message="${message}Found a compatible OS and gcc version. "
        fi
      elif [ "$DISTRO" == "redhatenterpriseserver" ] || [ "$DISTRO" == "redhatenterprise" ] || [ "$DISTRO" == "rhel" ] || [ "$DISTRO" == "centos" ] && [[ $RELEASE =~ ^8 ]]; then
          # If the user is on RHEL/CentOS 8 we assume they have the default gcc
          message="${message}Found a compatible OS. "
      elif [ "$DISTRO" == "ubuntu" ] && [[ $RELEASE =~ ^(18|20).04 ]]; then
          # If the user is on Ubuntu we assume they have the default gcc
          message="${message}Found a compatible OS. "
      elif [ "$DISTRO" == "suse" ] || [ "$DISTRO" == "opensuse" ] && [[ $RELEASE =~ ^15 ]]; then
          # If the user is on Ubuntu we assume they have the default gcc
          message="${message}Found a compatible OS. "
      else
          message="${message}Warning: Your OS is not supported by the FPGA backend. "
          FPGA_STATUS=-1
      fi
    fi


    SCRIPTDIR=$(readlink -e $(dirname "${BASH_SOURCE[0]}"))
    INTEL_TARGET_PLATFORM="linux"
    OCLFPGA=$SCRIPTDIR/../${INTEL_TARGET_PLATFORM}/lib/oclfpga
    # Check if user is in an FPGA environment
    if [ "$INTELFPGAOCLSDKROOT" == "" ]; then
        message="${message}Warning: INTELFPGAOCLSDKROOT is not set. "
        howtofix="${howtofix}Please source oneAPI setvars.sh to set it. "
        FPGA_STATUS=-1
    else
        message="${message}INTELFPGAOCLSDKROOT is correctly set to $INTELFPGAOCLSDKROOT."
    fi

    if [ "$INTEL_FPGA_ROOT" == "" ]; then
        message="Warning: INTEL_FPGA_ROOT is not set. "
        howtofix="${howtofix}Please ensure Intel(R) FPGA Add-on for oneAPI Base Toolkit is installed "
        howtofix="${howtofix}if you want to perform FPGA bitstream generation. "
        FPGA_STATUS=-1
    else
        message="${message}Using Quartus in $INTEL_FPGA_ROOT."
    fi

    if [ ! -e /dev/intel-fpga-port.* ]; then
        message="${message}Warning: Open Programmable Acceleration Engine (OPAE) driver was not found. "
        howtofix="${howtofix}If Intel(R) Programmable Acceleration Card with Intel(R) Arria(R) 10 GX FPGA "
        howtofix="${howtofix}or Intel(R) Stratix(R) 10 FPGA is installed in the system, please "
        howtofix="${howtofix}install OPAE driver by running "
        howtofix="${howtofix}aocl install <board package name or path>. "
        FPGA_STATUS=-1
    else
        message="${message}FPGA device driver is installed. "

        FPGAINFO_PATH=$(which fpgainfo 2>&1)
        if [ $? -ne 0 ]; then
            message="${message}Warning: fpgainfo not found. "
            FPGA_STATUS=-1
        else
          # Check firmware version of the installed PAC
          fpgainfo fme |while read -r line
          do
            if [ "$line" != "" ]; then
              if [[ "$line" =~ ^"Pr Interface Id" ]]; then
                  EXPECTED_ID_PAC_A10="38d782e3-b612-5343-b934-2433e348ac4c"
                  EXPECTED_ID_PAC_S10="9346116d-a52d-5ca8-b06a-a9a389ef7c8d"
                  PR_IF_ID=$(echo $line |grep -o '[a-zA-Z0-9\-]*$')
                  if [[ "${PR_IF_ID}" != "${EXPECTED_ID_PAC_A10}" && "${PR_IF_ID}" != "${EXPECTED_ID_PAC_S10}" ]]; then
                    message="${message}Error: Installed Intel(R) Programmable Acceleration Card has unsupported firmware installed."
                    howtofix="${howtofix}Please refer to Intel(R) Programmable Acceleration Card Getting Started Guide to update it."
                    FPGA_STATUS=-1
                else
                    message="${message}Correct firmware installed on Intel(R) Programmable Acceleration Card"
                    message="${message}PR Interface ID: $PR_IF_ID"
                fi
              fi
            fi
          done
        fi

      # Check if intel_a10gx_pac or intel_s10sx_pac is installed.
      BOARD_PACKAGE_INSTALLED_PAC_A10=0;
      BOARD_PACKAGE_INSTALLED_PAC_S10=0;
      board_package_lines=$(aoc -list-board-packages)
      while read -r line; do
        line="$(echo -e "$line" | sed -e 's/^[[:space:]]*//')"
        if [[ "$line" == "Board packages shipped with"* ]]; then
          break
        elif [[ "$line" == "Installed board packages:" ]]; then
          continue
        elif [[ "$line" == "No packages installed" ]]; then
          continue
        else
          if [[ "$line" =~ "intel_a10gx_pac" ]]; then
            BOARD_PACKAGE_INSTALLED_PAC_A10=1;
            continue
          fi
          if [[ "$line" =~ "intel_s10sx_pac" ]]; then
            BOARD_PACKAGE_INSTALLED_PAC_S10=1;
            continue
          fi
        fi
      done <<< "$board_package_lines"

      # intel_a10gx_pac or inte_s10sx_pac wasn't found in installed packages.
      if [[ ${BOARD_PACKAGE_INSTALLED_PAC_A10}+${BOARD_PACKAGE_INSTALLED_PAC_S10} -eq 0 ]]; then
        message="${message}Warning: FPGA board package for this version of oneAPI is not installed."
        howtofix="${howtofix}If this host will be used for FPGA hardware run, please install it by "
        howtofix="${howtofix}running aocl install <board package name or path>. "
        FPGA_STATUS=-1
      elif [[ ${BOARD_PACKAGE_INSTALLED_PAC_A10}+${BOARD_PACKAGE_INSTALLED_PAC_S10} -eq 1 ]]; then
        # intel_a10gx_pac OR inte_s10sx_pac has been installed
        FCD_SETUP=0
        FCD_FILE_PAC_A10=${ACL_BOARD_VENDOR_PATH}/intel_a10gx_pac.fcd
        if [[ -f "$FCD_FILE_PAC_A10" ]]; then
          FCD_LIBRARY=$(cat ${FCD_FILE_PAC_A10})
          if [[ "$FCD_LIBRARY" =~ "${INTELFPGAOCLSDKROOT}/board/intel_a10gx_pac" || "$FCD_LIBRARY" =~ "${INTEL_FPGA_ROOT}/board/intel_a10gx_pac" ]]; then
            FCD_SETUP=1
            message="${message}FPGA board package intel_a10gx_pac is installed"
          fi
        fi

        FCD_FILE_PAC_S10=${ACL_BOARD_VENDOR_PATH}/intel_s10sx_pac.fcd
        if [[ -f "$FCD_FILE_PAC_S10" ]]; then
          FCD_LIBRARY=$(cat ${FCD_FILE_PAC_S10})
          if [[ "$FCD_LIBRARY" =~ "${INTELFPGAOCLSDKROOT}/board/intel_s10sx_pac" || "$FCD_LIBRARY" =~ "${INTEL_FPGA_ROOT}/board/intel_s10sx_pac" ]]; then
            FCD_SETUP=1
            message="${message}FPGA board package intel_s10sx_pac is installed. "
          fi
        fi

        if [[ $FCD_SETUP -eq 0 ]]; then
          message="${message}Warning: FPGA board package installation is corrupt."
          howtofix="${howtofix}Please fix it by running "
          howtofix="${howtofix}1. aocl uninstall <board package name or path> "
          howtofix="${howtofix}2. aocl install <board package name or path> "
          FPGA_STATUS=-1
        else
          message="${message}FPGA FCD is installed correctly. "
          # Run aocl diagnose to see if board enumerates
          AOCL_DIAGNOSE=$(aocl diagnose)
          if [ $? -eq 0 ]; then
            message="${message}${AOCL_DIAGNOSE}"
          else
            message="${message}${AOCL_DIAGNOSE}"
          fi
        fi

      else
        message='${message}Warning: FPGA board package "inte_a10gx_pac" and "inte_s10sx_pac" should not be '
        message="${message}simultaneously installed on the same machine. "
        howtofix="${howtofix}Please uninstall both by running "
        howtofix="${howtofix}aocl uninstall <board package name or path> "
        howtofix="${howtofix}then install one of them by running "
        howtofix="${howtofix}aocl install <board package name or path>. "
        FPGA_STATUS=-1
      fi
    fi

    if [[ $FPGA_STATUS == 0 ]]; then
        output="${output}\\\"OpenCL FPGA runtime\\\": {\\\"Value\\\": \\\"Checked\\\",\\\"RetVal\\\": \\\"PASS\\\",\\\"Message\\\": \\\"${message}\\\"}}}"
    else
        output="${output}\\\"OpenCL FPGA runtime\\\": {\\\"Value\\\": \\\"Checked\\\",\\\"RetVal\\\": \\\"FAIL\\\",\\\"Message\\\": \\\"${message}\\\",\\\"HowToFix\\\": \\\"${howtofix}\\\"}}}"
    fi

    echo -n ${output}

}

function get_metadata() {
    echo '{"name": "dpcpp_compiler_sys_check","type": "Data","tags": "sys_check","descr": "This check verifies if the environment is ready to use the Intel(R) oneAPI DPC++ Compiler.","dataReq": "{}","merit": 0,"timeout": 10,"version": 1,"run": ""}'
}

function get_summary() {
    echo -n '{"result": "'
    run
    echo '"}'
}

function get_api_version() {
    echo "0.1"
}

parse_args $@
if [[ $METADATA == 1 ]]; then
    get_metadata
elif [[ $SUMMARY == 1 ]]; then
    get_summary
elif [[ $API == 1 ]]; then
    get_api_version
fi
