# -*- mode: python -*-
#------------------------------------------------------------------------
#
#------------------------------------------------------------------------
import argparse
from bs4 import BeautifulSoup
import os
import os.path
import re
import subprocess
import sys

exit_code = 0
EXIT_CODE_BINARY_REMOVED = 0x01
EXIT_CODE_BINARY_SYMBOLS = 0x02
EXIT_CODE_BINARY_UNKNOWN = 0x08
EXIT_CODE_SOURCE_REMOVED = 0x10
EXIT_CODE_SOURCE_SYMBOLS = 0x20
EXIT_CODE_SOURCE_UNKNOWN = 0x80

def which(program):
    import os
    def is_exe(fpath):
        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return exe_file
    return None

def analyse( report ):
    global exit_code
    binary_symbol_pattern = re.compile("^#Symbol_Binary_Problems_.*$")
    binary_pattern = re.compile("^#.*_Binary_.*$")
    source_symbol_pattern = re.compile("^#Symbol_Source_Problems_.*$")
    source_pattern = re.compile("^#.*_Source_.*$")
    if (prog_cppfilter):
        html_report = open( report, 'r' )
        try:
            report_string = subprocess.check_output([prog_cppfilter], stdin=html_report)
        except CalledProcessError as exception:
            exit_code = exit_code | EXIT_CODE_SOURCE_UNKNOWN | EXIT_CODE_BINARY_UNKNOWN
            return
    else:
        with open( report, 'r' ) as html_report:
            report_string = html_report.read( )
    soup = BeautifulSoup( report_string, "html.parser" )
    for summary_results in soup.find_all("table", attrs={"class":"summary"}):
        for table_row in summary_results.find_all("tr"):
            print( "table_row: %s" % (table_row))
            table_header = table_row.find("th")
            if( table_header and
                table_header.get_text() == "Version #1"):
                table_data = table_row.find("td").get_text( )
                old_library_version = int(table_data.split("_")[0])
                print( "old_library_version: %d" % (old_library_version))
                continue
            elif( table_header and
                table_header.get_text() == "Version #2"):
                table_data = table_row.find("td").get_text( )
                new_library_version = int(table_data.split("_")[0])
                print( "new_library_version: %d" % (new_library_version))
                continue
            last_td = table_row.find_all("td")[-1:]
            if( not last_td ):
                # Nothing to do as there is no table data element
                continue
            last_td = last_td[0]
            print( "last_td: %s" % (last_td))
            incident_count = last_td.get_text( )
            if ( not incident_count.isdigit( ) ):
                # Nothing to do as the table data element is not a number
                continue
            incident_count = int(incident_count)
            if ( incident_count <= 0 ):
                # No incidents to report
                continue
            classification = last_td.get("class")
            if ( not classification ):
                # No class associated with the table data
                continue
            if (isinstance(classification, list) ):
                classification = classification[0]
            incident_anchor = last_td.find("a")
            incident_type = incident_anchor.get("href")
            #------------------------------------------------------------
            # Evaluate the severity of the incident
            #------------------------------------------------------------
            if ( classification == "failed" ):
                if ( new_library_version > old_library_version ):
                    continue
                if (incident_type == "#Source_Removed" ):
                    exit_code = exit_code | EXIT_CODE_SOURCE_REMOVED
                    continue
                if (source_symbol_pattern.match(incident_type ) ):
                    exit_code = exit_code | EXIT_CODE_SOURCE_SYMBOLS
                    continue
                if (incident_type == "#Binary_Removed" ):
                    exit_code = exit_code | EXIT_CODE_BINARY_REMOVED
                    continue
                if (binary_symbol_pattern.match(incident_type ) ):
                    exit_code = exit_code | EXIT_CODE_BINARY_SYMBOLS
                    continue
                #--------------------------------------------------------
                # Unknown failures
                #--------------------------------------------------------
                if (source_pattern.match(incident_type ) ):
                    exit_code = exit_code | EXIT_CODE_SOURCE_UNKNOWN
                    continue
                if (binary_pattern.match(incident_type ) ):
                    exit_code = exit_code | EXIT_CODE_BINARY_UNKNOWN
                    continue
            elif ( classification == "new" ):
                #--------------------------------------------------------
                # Adding symbols does not result in backwards
                #   compatability issues
                #--------------------------------------------------------
                continue

def str2bool(v):
    if isinstance(v, bool):
        return( v );
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower( ) in ( 'no', 'false', 'f', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError('Boolean value expected.')

def main():
    program_name=os.path.basename(sys.argv[0])
    parser = argparse.ArgumentParser( usage='%(prog)s [options]' )
    parser.add_argument('--abi-check',
                        type=str2bool,
                        nargs='?',
                        default=True)
    parser.add_argument('--api-check',
                        type=str2bool,
                        nargs='?',
                        default=True)
    parser.add_argument('reports', nargs=argparse.REMAINDER)
    args = parser.parse_args( )
    for report in args.reports:
        analyse( report )

prog_cppfilter = which("c++filt")
if __name__ == "__main__":
    main( )
exit(exit_code)
