#!/usr/bin/env python
# Copyright(c) 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.

import argparse
import sys
import subprocess
import os
import re

import bist_app
import bist_common
import bist_def
import bist_dma
import bist_nlb3
import bist_nlb0

FEATURES = ['fme', 'port', 'temp', 'power', 'errors all']
PRIVATE_FEATURES = {bist_common.VCP_ID: ['phy', 'mac']}


def get_afu_id(bus):
    cmd = 'fpgainfo port -B 0x{}'.format(bus)
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
    output, _ = p.communicate()
    m = re.search('Accelerator Id\\s+:\\s(\\S+)', output)
    afu_id = (m.group(1) if m else m)
    return afu_id


def main(args):
    bdf = bist_common.get_all_fpga_bdfs(args)
    if not bdf:
        if vars(args)['device_id'] == '09c4':
            sys.exit("Please use '-i' option to specify FPGA device ID "
                     "other than 09c4")
        else:
            sys.exit("No FPGA devices of device ID {} found"
                     .format(vars(args)['device_id']))
    elif len(bdf) > 1 and not any([args.bus, args.device, args.function]):
        sys.exit("Multiple cards found. Specify a bus, device or function")
    elif any([vars(args)['bus'], vars(args)['device'],
              vars(args)['function']]):
        bdf = bist_common.get_bdf_from_args(args)
        if len(bdf) > 1 or not bdf:
            sys.exit("Could not find specified FPGA, please try again")
    bdf = bdf[0]
    start = "==========================================================\n\n" \
            "Beginning FPGA Built-In Self-Test\n\n"\
            "=========================================================="
    print start

    # Display data from FPGA info
    dev_id = int(vars(args)['device_id'], 16)
    afu_id = get_afu_id(bdf['bus'])
    ret = 0
    bist_common.dev_id = dev_id
    for feature in (FEATURES + PRIVATE_FEATURES.get(dev_id, [])):
        cmd = "fpgainfo {} -B 0x{}".format(feature, bdf['bus'])
        ret += subprocess.call(cmd, shell=True)
        print("\n")

    if dev_id == bist_common.VCP_ID:
        for name in ['nlb0', 'dma']:
            module = 'bist_{}'.format(name)
            mode = getattr(sys.modules.get(module),
                           '{}Mode'.format(name.capitalize()))()
            print "Running mode: {}".format(mode.name)
            ret += mode.run(None, bdf['bus'], bist_common.VCP_ID, afu_id)
    else:
        gbs_paths = args.gbs_paths
        afu_ids = {}
        for path in gbs_paths:
            afu_ids.update({bist_common.get_afu_id(path): path})
        if not gbs_paths:
            # Get the afu ID of the afu in the slot (defaut persona)
            afu_ids.update({bist_common.get_afu_id(): ""})

        for afu_id in afu_ids.keys():
            if afu_id == bist_def.DefaultMode().afu_id.replace("-", ""):
                mode = bist_def.DefaultMode()
            elif afu_id == bist_nlb3.Nlb3Mode().afu_id.replace("-", ""):
                mode = bist_nlb3.Nlb3Mode()
            elif afu_id == bist_dma.DmaMode().afu_id.replace("-", ""):
                mode = bist_dma.DmaMode()
            elif afu_id == bist_app.BistMode().afu_id.replace("-", ""):
                mode = bist_app.BistMode()
            else:
                sys.exit("Unknown AFU for available ID {}\n".format(afu_id))
            print "Running mode: {}".format(mode.name)
            ret += mode.run(afu_ids.get(afu_id), bdf['bus'])

    print "\nBuilt-in Self-Test Completed.\n"
    sys.exit(ret)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    bist_common.global_arguments(parser)
    args = parser.parse_args()
    bist_common.check_required_cmds()
    main(args)
