#!/usr/bin/env python
# Copyright(c) 2013-2017, Intel Corporation
#
# Redistribution  and  use  in source  and  binary  forms,  with  or  without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of  source code  must retain the  above copyright notice,
#   this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name  of Intel Corporation  nor the names of its contributors
#   may be used to  endorse or promote  products derived  from this  software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED TO,  THE
# IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT  SHALL THE COPYRIGHT OWNER  OR CONTRIBUTORS BE
# LIABLE  FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR
# CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT LIMITED  TO,  PROCUREMENT  OF
# SUBSTITUTE GOODS OR SERVICES;  LOSS OF USE,  DATA, OR PROFITS;  OR BUSINESS
# INTERRUPTION)  HOWEVER CAUSED  AND ON ANY THEORY  OF LIABILITY,  WHETHER IN
# CONTRACT,  STRICT LIABILITY,  OR TORT  (INCLUDING NEGLIGENCE  OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

#
# Create an ASE environment in a new directory and configure it for
# simulating an AFU.  Run "afu_sim_setup --help" for details.
#

import sys
import os
import errno
import shutil
from os.path import dirname, realpath, sep
import subprocess


#
# Figure out the root of the ASE source directory.  ASE is installed
# along with OPAE SDK, so either find it in the installation tree or in the
# source tree.
#
def getASESrcPath():
    # CMake will update any variables marked by @ with the proper values.
    project_src_dir = '@CMAKE_CURRENT_SOURCE_DIR@'
    ase_share_dir = '@ASE_INST_SHARE_DIR@'

    # Parent directory of the running script
    parent_dir = os.path.dirname(
        os.path.dirname(os.path.realpath(sys.argv[0])))

    # If this script is installed, the above variables are substituted.
    if (ase_share_dir[0] != '@'):
        # The script has at least had variables substituted.  Either
        # it is in the CMake build directory or it is installed.
        if (os.path.isfile(os.path.join(parent_dir, 'CMakeCache.txt'))):
            # We're in the CMake build directory.  Use the source tree's
            # database.
            ase_share = project_src_dir
        else:
            # The script is installed.  The installation path isn't known
            # since it can be changed when using rpm --prefix.  We do
            # guarantee that the OPAE bin and share directories have the
            # same parent.
            ase_share = os.path.join(parent_dir, ase_share_dir)
    else:
        # Running out of the source tree
        ase_share = parent_dir

    return ase_share


def errorExit(msg):
    sys.stderr.write('Error: {0}\n'.format(msg))
    sys.exit(1)


#
# Copy ASE base environment to target directory
#
def copy_ase_env(dst, force):
    # Where is the base environment
    ase_base = getASESrcPath()

    # Target directory can't exist (unless force is set)
    dst = dst.rstrip(os.path.sep)
    if dst == '':
        errorExit('Target directory not set')

    if (os.path.exists(dst)):
        if (os.path.islink(dst)):
            errorExit('Target ({0}) is a link.'.format(dst))
        if (not force):
            errorExit('Target ({0}) already exists.'.format(dst))
        if (os.path.isdir(dst)):
            shutil.rmtree(dst)
        else:
            os.remove(dst)

    # Copy ASE to target directory
    print('Copying ASE from {0}...'.format(ase_base))
    try:
        shutil.copytree(ase_base, dst)
    except Exception:
        shutil.rmtree(dst)
        print('Failed to copy {0} to {1}'.format(ase_base, dst))
        raise


#
# Configure ASE for the AFU
#
def setup_ase_sim(args):
    # generate_ase_environment doesn't handle MODELSIM
    tool = args.tool
    if (tool == 'MODELSIM'):
        tool = 'QUESTA'

    # Configure ASE
    cmd = ['./scripts/generate_ase_environment.py']
    if (tool):
        cmd.append('--tool=' + tool)
    if (args.platform):
        cmd.append('--platform=' + args.platform)

    # Transform path to sources relative to target directory
    if (not os.path.isabs(args.sources)):
        cmd.append('--sources=' + os.path.relpath(args.sources, args.dst))
    else:
        cmd.append('--sources=' + args.sources)

    try:
        os.chdir(args.dst)
        status = subprocess.call(cmd)
        if (status != 0):
            errorExit("ASE configuration failed.")
    except Exception:
        print("Failed to execute {0}".format(cmd[0]))
        raise

    # Set ASE mode
    os.system("sed -i 's/ASE_MODE.*/ASE_MODE = {0}/' ase.cfg".format(
        args.ase_mode))
    # Control transaction printing
    os.system(("sed -i 's/ENABLE_CL_VIEW.*/ENABLE_CL_VIEW = {0}/' " +
               "ase.cfg").format(int(args.ase_verbose)))


def main():
    import argparse
    parser = argparse.ArgumentParser(
        description="""Generate an ASE simulation environment for an AFU.
                       An ASE environment is instantiated from the OPAE
                       installation and then configured for the specified AFU.
                       AFU source files are specified in a text file that is
                       parsed by rtl_src_config, which is also part of the
                       OPAE base environment.""")

    parser.add_argument('-s', '--sources', required=1,
                        help="""AFU source specification file that will be
                                passed to rtl_src_config.  See "rtl_src_config
                                --help" for the file's syntax.  rtl_src_config
                                translates the source list into either Quartus
                                or RTL simulator syntax.""")
    parser.add_argument('-p', '--platform', default=None,
                        help='FPGA Platform to simulate.')
    parser.add_argument('-t', '--tool', default=None, type=str.upper,
                        choices=['VCS', 'QUESTA', 'MODELSIM'],
                        help='Default simulator.')
    parser.add_argument('-f', '--force',
                        action='store_true',
                        help="""Overwrite target directory if it exists.""")
    parser.add_argument('dst',
                        help="""Target directory path (directory must
                                not exist).""")

    # Changes to ase.cfg to set simulator behavior.
    parser.add_argument('--ase-mode', type=int, default=3,
                        help="""ASE execution mode (default, mode 3, exits on
                                completion).  See ase.cfg in the target
                                directory.""")
    parser.add_argument('--ase-verbose', action='store_true',
                        help="""When set, ASE prints each CCI-P transaction
                                to the command line. Transactions are always
                                logged to work/ccip_transactions.tsv, even
                                when not set. This switch sets ENABLE_CL_VIEW
                                in ase.cfg.""")

    args = parser.parse_args()

    copy_ase_env(args.dst, args.force)
    setup_ase_sim(args)


if __name__ == '__main__':
    main()
