#!/usr/bin/env python3

import collections

from osaca.frontend import Frontend
from osaca.parser import ParserAArch64v81, ParserX86ATT
from osaca.semantics import (INSTR_FLAGS, KernelDG, MachineModel, SemanticsAppender,
                             reduce_to_section)


class KerncraftAPI(object):
    def __init__(self, arch):
        self.machine_model = MachineModel(arch=arch)
        self.semantics = SemanticsAppender(self.machine_model)
        isa = self.machine_model.get_ISA().lower()
        if isa == 'aarch64':
            self.parser = ParserAArch64v81()
        elif isa == 'x86':
            self.parser = ParserX86ATT()

    def analyze_code(self, code):
        parsed_code = self.parser.parse_file(code)
        kernel = reduce_to_section(parsed_code, self.machine_model.get_ISA())
        for i in range(len(kernel)):
            self.semantics.assign_src_dst(kernel[i])
            self.semantics.assign_tp_lt(kernel[i])
        return kernel

    def create_output(self, kernel, show_lineno=False):
        kernel_graph = KernelDG(kernel, self.parser, self.machine_model)
        frontend = Frontend(arch=self.machine_model.get_arch())
        frontend.print_throughput_analysis(kernel, show_lineno=show_lineno)
        frontend.print_latency_analysis(kernel_graph.get_critical_path())

    def get_unmatched_instruction_ratio(self, kernel):
        unmatched_counter = 0
        for instruction in kernel:
            if (
                INSTR_FLAGS.TP_UNKWN in instruction['flags']
                and INSTR_FLAGS.LT_UNKWN in instruction['flags']
            ):
                unmatched_counter += 1
        return unmatched_counter / len(kernel)

    def get_port_occupation_cycles(self, kernel):
        throughput_values = self.semantics.get_throughput_sum(kernel)
        port_names = self.machine_model['ports']
        return collections.OrderedDict(list(zip(port_names, throughput_values)))

    def get_total_throughput(self, kernel):
        return max(self.semantics.get_throughput_sum(kernel))

    def get_latency(self, kernel):
        kernel_graph = KernelDG(kernel, self.parser, self.machine_model)
        return sum([x if x['latency'] is not None else 0 for x in kernel_graph])
