import sys
import os
import iso8601
import numpy as np

from typing import List
from os import listdir
from os.path import isfile, join


class LogLine(object):
    def __init__(self, thread_number, log_date, message, line_content):
        self.thread_number = thread_number
        self.log_date = log_date
        self.message = message
        self.line_content = line_content


def read_log_files(log_files):
    file_data = ""
    for file_name in log_files:
        with open(file_name, "rb") as f:
            file_data += f.read()

    return file_data.decode("utf-8")


def get_thread_number(line):
    return line.split("-")[7].strip()


def get_message(line_content):
    index = line_content.index("MessageProcessorThread")
    index2 = line_content.index("$", index)
    return line_content[index2 + 1:]


def get_log_date(line_content):
    return iso8601.parse_date(line_content[0:23])


def get_log_lines(log_data):
    lines = []
    line_content = ""
    first_line = None
    for line in log_data.splitlines():
        if "MessageProcessorThread" not in line:
            continue

        line_start = line[0:23]
        try:
            iso8601.parse_date(line_start)
            if line_content != "":
                lines.append(
                    LogLine(
                        get_thread_number(first_line),
                        get_log_date(first_line),
                        get_message(line_content),
                        line_content)
                )
                line_content = ""

            first_line = line
        except Exception as _:  # noqa
            continue

        line_content += line + "\r\n"

    if line_content != "":
        try:
            lines.append(
                LogLine(
                    get_thread_number(first_line),
                    get_log_date(first_line),
                    get_message(line_content),
                    line_content)
            )
        except Exception as _:  # noqa
            pass

    return lines


def separate_lines_by_thread(log_lines):
    # type: (List[LogLine]) -> List[List[LogLine]]
    line_dict = {}
    for line in log_lines:
        if line.thread_number not in line_dict:
            line_dict[line.thread_number] = []
        line_dict[line.thread_number].append(line)

    return line_dict.values()


def get_connection_id(message):
    index = message.index("c_void_p(")
    index2 = message.index(")", index)
    return message[index + 9:index2]


def main():
    log_directory = os.path.abspath(sys.argv[1])
    log_files = [os.path.join(log_directory, f) for f in listdir(log_directory) if isfile(join(log_directory, f)) and "TimeActions.log" in f]
    log_lines = get_log_lines(read_log_files(log_files))
    separated_lines = separate_lines_by_thread(log_lines)

    message_times = {}
    message_max_times = {}
    start_time = None
    message = None
    start_line = None

    for thread_logs in separated_lines:
        for line in thread_logs:
            line.message = line.message.strip()

            if "MB_ReplyMessage" not in line.message and start_time is None:
                start_time = line.log_date
                start_line = line
                try:
                    message = line.message.split("$")[0]
                except Exception as _:  # noqa
                    start_time = None

            elif "MB_ReplyMessage" in line.message:
                end_time = line.log_date

                if end_time is not None and start_time is not None:
                    elapsed_time = (end_time - start_time).total_seconds()
                    if elapsed_time > 1000:
                        print(start_line)
                        start_time = None
                        continue

                    if elapsed_time > 0:
                        if message not in message_times:
                            message_times[message] = []
                        message_times[message].append(elapsed_time)

                        if message not in message_max_times or elapsed_time > message_max_times[message]:
                            message_max_times[message] = elapsed_time
                start_time = None

    messages = message_times.keys()
    messages.sort()
    for message in messages:
        times = message_times[message]
        a = np.array(times)
        p = np.percentile(a, 95)  # return 95th percentile

        ret_message = "{:40} - [{:>5}]: {:>6.3f} / {:>6.3f} / {:>6.3f}"
        print(ret_message.format(message, len(times), np.average(times), p, message_max_times[message]))
    print("")


main()
