#!/usr/bin/env python3

################################################################################
#
# Copyright 2020 OpenHW Group
# Copyright 2020 Silicon Labs, Inc.
#
# Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     https://solderpad.org/licenses/
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier:Apache-2.0 WITH SHL-2.0
# 
################################################################################
#
# test_yaml
#
# Author: Steve Richmond
#  email: steve.richmond@silabs.com
#
# Written with Python 3.5.1 on RHEL 7.7.  Your python mileage may vary.
#
################################################################################

import argparse
import os
import sys
import tempfile
import re
import pprint
import logging
import yaml

logging.basicConfig()
logger = logging.getLogger(os.path.basename(__file__))
logger.setLevel(logging.INFO)

# Constants
TOPDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
VALID_YAMLS = ('corev-dv.yaml', 'test.yaml')
REQUIRED_KEYS = ('name', 'uvm_test', 'description',)
CFG_PATH = (
            '<CV_CORE>/tests/cfg',
            )
TEST_PATHS = (
            '<CV_CORE>/tests/programs/corev-dv/',
            '<CV_CORE>/tests/programs/embench/',
            '<CV_CORE>/tests/programs/custom',
            )
try:
    DEFAULT_CORE=os.environ['CV_CORE']
except KeyError:
    DEFAULT_CORE=None

if (sys.version_info < (3,0,0)):
    print ('Requires python 3')
    exit(1)

def read_file(test, type, run_index):
    '''Read a YAML test specification'''

    matches = [os.path.join(TOPDIR, p, test, type) for p in TEST_PATHS 
                if os.path.exists(os.path.join(TOPDIR, p, test, type))]

    # It is a fatal error to find less than 1 or more than 1 match
    if len(matches) == 0:
        logger.fatal('Could not find [{}] in any directories:'.format(type))
        for p in TEST_PATHS:
            logger.fatal(os.path.join(TOPDIR, p, test, type)) 
        os.sys.exit(2)

    if len(matches) >1 :
        logger.fatal('Found multiple matches for {}:{} in directories:'.format(test, type))
        for p in TEST_PATHS:
            logger.fatal(os.path.join(TOPDIR, p, test, type)) 
        os.sys.exit(2)

    stream = open(matches[0], 'r')
    logger.debug('Reading test specification: {}'.format(matches[0]))
    # Newer PyYAMLs must specify explicit loader (policy) or will issue warnings
    # Older PyYAMLs will not support the Loader argument
    # So try the new way first, then catch to the old way
    try:
        test_spec = yaml.load(stream, Loader=yaml.FullLoader)
    except AttributeError:
        test_spec = yaml.load(stream)
    stream.close()
    test_spec['test_dir'] = os.path.dirname(matches[0])

    # Debug the YAML parsing
    pp = pprint.PrettyPrinter()
    logger.debug('Read YAML:')
    logger.debug(pp.pformat(test_spec))

    # Substitute <RUN_INDEX>
    for k in test_spec:        
        if k == 'disable_csr_check':
            test_spec[k] = '+'.join(test_spec[k])
        test_spec[k] = str(test_spec[k])
        test_spec[k] = re.sub('<RUN_INDEX>', run_index, test_spec[k])

    # Validation
    for k in REQUIRED_KEYS:        
        if not k in test_spec:
            logger.fatal('Key [{}] was not found in test specification YAML:'.format(k))
            logger.fatal('-> : {}'.format(matches[0]))
            os.sys.exit(2)

    if not 'program' in test_spec:
        test_spec['program'] = test_spec['name']
        
    # Debug the YAML parsing
    pp = pprint.PrettyPrinter()
    logger.debug('Read YAML:')
    logger.debug(pp.pformat(test_spec))

    return test_spec

def emit_make(test_spec, prefix):
    '''Emit a hash from the YAML test specification into a makefile that can be included'''
    fh = tempfile.NamedTemporaryFile(mode='w', delete=False)
    for k,v in sorted(test_spec.items()):
        fh.write('{}{}={}\n'.format('' if not prefix else prefix.upper() + '_', k.upper(), v.rstrip()))
    fh.close()

    return fh.name

################################################################################
# Command-line arguments

parser = argparse.ArgumentParser()
parser.add_argument('-t', '--test', help='Required: Test to look for')
parser.add_argument('--yaml', choices=VALID_YAMLS, help='Required: Name of YAML test specification to find')
parser.add_argument('--core', default=DEFAULT_CORE, help='Default core to test')
parser.add_argument('--prefix', help='Prefix to add to make variables generated')
parser.add_argument('--run-index', default='0', help='Add a run index to append to test specifications')
parser.add_argument('-d', '--debug', action='store_true', help='Display debug messages')
args = parser.parse_args()

if args.debug:
    logger.setLevel(level=logging.DEBUG)

if not args.core:
    logger.fatal('Must specify core with CV_CORE envrionment variable or --core')
    os.sys.exit(2)

# Validate 
if not args.yaml:
    logger.fatal('Must specify the YAML type with --yaml')
    logger.fatal('Valid choices: {}'.format(VALID_YAMLS))
    os.sys.exit(2)

if not args.test:
    logger.fatal('Must specify a test with -t or --test')
    os.sys.exit(2)

CFG_PATH = [p.replace('<CV_CORE>', args.core.lower()) for p in CFG_PATH]
TEST_PATHS = [p.replace('<CV_CORE>', args.core.lower()) for p in TEST_PATHS]

test_spec = read_file(test=args.test, type=args.yaml, run_index=args.run_index)
temp_file = emit_make(test_spec=test_spec, prefix=args.prefix)

logger.debug('File written to {}'.format(temp_file))
print(temp_file)
