#!/usr/bin/python3

'''Report the datastructures from best performers to worst for a given area of comparison'''

import re
#mport sys
import glob
#mport pprint
import functools
#mport collections

def return_javascript_code():
    '''
    Output the required javascript for drilling down to greater levels of detail.
    Derived from http://www.gnostice.com/nl_article.asp?id=208&t=Interactive_Tree_Control_Using_An_HTML_List_and_JavaScript
    '''
    # BTW, this dislikes it when you close your <li>'s with </li>'s
    # This xml tag makes chrome and perhaps others have problems
    #<?xml version="1.0" encoding="UTF-8"?>
    string = '''

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  <title>Python Dictionary Results Table</title>

  <style type="text/css">
ul.LinkedList { display: block; }
/* ul.LinkedList ul { display: none; } */
.HandCursorStyle { cursor: pointer; cursor: hand; }  /* For IE */
  </style>

  <script type="text/JavaScript">
    // Add this to the onload event of the BODY element
    function addEvents() {
      activateTree(document.getElementById("LinkedList1"));
    }

    // This function traverses the list and add links 
    // to nested list items
    function activateTree(oList) {
      // Collapse the tree
      for (var i=0; i < oList.getElementsByTagName("ul").length; i++) {
        oList.getElementsByTagName("ul")[i].style.display="none";            
      }                                                                  
      // Add the click-event handler to the list items
      if (oList.addEventListener) {
        oList.addEventListener("click", toggleBranch, false);
      } else if (oList.attachEvent) { // For IE
        oList.attachEvent("onclick", toggleBranch);
      }
      // Make the nested items look like links
      addLinksToBranches(oList);
    }

    // This is the click-event handler
    function toggleBranch(event) {
      var oBranch, cSubBranches;
      if (event.target) {
        oBranch = event.target;
      } else if (event.srcElement) { // For IE
        oBranch = event.srcElement;
      }
      cSubBranches = oBranch.getElementsByTagName("ul");
      if (cSubBranches.length > 0) {
        if (cSubBranches[0].style.display == "block") {
          cSubBranches[0].style.display = "none";
        } else {
          cSubBranches[0].style.display = "block";
        }
      }
    }

    // This function makes nested list items look like links
    function addLinksToBranches(oList) {
      var cBranches = oList.getElementsByTagName("li");
      var i, n, cSubBranches;
      if (cBranches.length > 0) {
        for (i=0, n = cBranches.length; i < n; i++) {
          cSubBranches = cBranches[i].getElementsByTagName("ul");
          if (cSubBranches.length > 0) {
            addLinksToBranches(cSubBranches[0]);
            cBranches[i].className = "HandCursorStyle";
            cBranches[i].style.color = "blue";
            cSubBranches[0].style.color = "black";
            cSubBranches[0].style.cursor = "auto";
          }
        }
      }
    }
  </script>
</head>

<body onload="addEvents();">
'''
    return string

def get_last_line(file_):
    '''Get the last line of file_'''
    last = None
    for line in file_:
        last = line
    return last

@functools.total_ordering
class Number_Pattern_Key(object):
    '''Store a number pattern key'''
    # pylint: disable=too-few-public-methods
    def __init__(self, number_pattern):
        self.number_pattern = number_pattern

    def __hash__(self):
        return hash(self.number_pattern)

    def __str__(self):
        return self.number_pattern

    __repr__ = __str__

    def cmp(self, other):
        '''Compare two number pattern keys'''
        if self.number_pattern < other.number_pattern:
            return -1
        elif self.number_pattern > other.number_pattern:
            return 1
        else:
            return 0

    def __lt__(self, other):
        if self.cmp(other) == -1:
            return True
        else:
            return False

    def __eq__(self, other):
        if self.cmp(other) == 0:
            return True
        else:
            return False


@functools.total_ordering
class Size_Key(object):
    '''Store a size key'''
    # pylint: disable=too-few-public-methods
    def __init__(self, size):
        self.size = size

    def __hash__(self):
        return hash(self.size)

    def __str__(self):
        return str(self.size)

    __repr__ = __str__

    def cmp(self, other):
        '''Compare two size keys in order'''
        if self.size < other.size:
            return -1
        elif self.size > other.size:
            return 1
        else:
            return 0

    def __lt__(self, other):
        if self.cmp(other) == -1:
            return True
        else:
            return False

    def __eq__(self, other):
        if self.cmp(other) == 0:
            return True
        else:
            return False


@functools.total_ordering
class Interpreter_Key(object):
    '''A class to hold an interpreter key'''
    # pylint: disable=too-few-public-methods
    def __init__(self, interpreter):
        self.interpreter = interpreter

    def __hash__(self):
        return hash(self.interpreter)

    def __str__(self):
        return str(self.interpreter)

    __repr__ = __str__

    def cmp(self, other):
        '''Compare two interpreter names'''
        if self.interpreter < other.interpreter:
            return -1
        elif self.interpreter > other.interpreter:
            return 1
        else:
            return 0

    def __lt__(self, other):
        if self.cmp(other) == -1:
            return True
        else:
            return False

    def __eq__(self, other):
        if self.cmp(other) == 0:
            return True
        else:
            return False


@functools.total_ordering
class Tree_Type_Key(object):
    '''Store a tree type key'''
    # pylint: disable=too-few-public-methods
    def __init__(self, tree_type):
        self.tree_type = tree_type

    def __hash__(self):
        return hash(self.tree_type)

    def __str__(self):
        return str(self.tree_type)

    __repr__ = __str__

    def cmp(self, other):
        '''Compare two Tree_Type_keys'''
        if self.tree_type < other.tree_type:
            return -1
        elif self.tree_type > other.tree_type:
            return 1
        else:
            return 0

    def __lt__(self, other):
        if self.cmp(other) == -1:
            return True
        else:
            return False

    def __eq__(self, other):
        if self.cmp(other) == 0:
            return True
        else:
            return False


@functools.total_ordering
class Performer(object):
    '''Class for holding a description of a top performing datastructure'''
    # pylint: disable=too-few-public-methods,too-many-return-statements
    def __init__(self, size, tree_type, duration, standard_deviation):
        self.size = int(size.size)
        self.tree_type = tree_type
        self.duration = float(duration)
        self.standard_deviation = float(standard_deviation)

    def cmp(self, other):
        '''Compare two Top_Performer's'''
        # Compare size in reverse
        if self.size < other.size:
            return 1
        elif self.size > other.size:
            return -1
        else:
            # Compare duration forward
            if self.duration < other.duration:
                return -1
            elif self.duration > other.duration:
                return 1
            #else:
            #    if self.tree_type < other.tree_type:
            #        return -1
            #    elif self.tree_type > other.tree_type:
            #        return 1
            #    else:
            #        return 0
            else:
                return 0

    def __lt__(self, other):
        if self.cmp(other) == -1:
            return True
        else:
            return False

    def __eq__(self, other):
        if self.cmp(other) == 0:
            return True
        else:
            return False



def gen_performers(dict_):
    '''Generate the top performers'''

    for size_key in dict_:
        tree_type_dict = dict_[size_key]
        for tree_type_key in tree_type_dict:
            mean = tree_type_dict[tree_type_key][0]
            standard_deviation = tree_type_dict[tree_type_key][1]
            yield Performer(size_key, tree_type_key, mean, standard_deviation)

def main():
    # pylint: disable=too-many-locals
    '''Main function'''
    dict_ = {}
    for filename in glob.glob('*.dat'):
        fixed_red_black = filename.replace('red-black', 'red_black')
        fields = fixed_red_black.split('-')
        interpreter = '-'.join(fields[:2])
        number_pattern = fields[2]
        # tree_type = re.sub(r'\.dat$', '', '-'.join(fields[5:7]))
        tree_type = re.sub(r'\.dat$', '', fields[3])
        with open(filename, 'r') as file_:
            last_line = get_last_line(file_)
        size, duration, standard_deviation = last_line.split()
        size = int(size)
        duration = round(float(duration), 1)
        standard_deviation = round(float(standard_deviation), 3)
        number_pattern_key = Number_Pattern_Key(number_pattern)
        size_key = Size_Key(size)
        interpreter_key = Interpreter_Key(interpreter)
        tree_type_key = Tree_Type_Key(tree_type)

        if number_pattern_key not in dict_:
            dict_[number_pattern_key] = {}

        if interpreter_key not in dict_[number_pattern_key]:
            dict_[number_pattern_key][interpreter_key] = {}

        if size_key not in dict_[number_pattern_key][interpreter_key]:
            dict_[number_pattern_key][interpreter_key][size_key] = {}

        if tree_type_key not in dict_[number_pattern_key][interpreter_key][size_key]:
            dict_[number_pattern_key][interpreter_key][size_key][tree_type_key] = (duration, standard_deviation)

    num_top_performers = 20

    print(return_javascript_code())
    print('<ul id="LinkedList1" class="LinkedList">')
    for number_pattern in sorted(dict_):
        print('<li>{}'.format(number_pattern))
        print('\t<ul>')
        for interpreter_key in sorted(dict_[number_pattern]):
            print('\t<li>{}'.format(interpreter_key))
            print('\t\t<ul>')
            performers_it = gen_performers(dict_[number_pattern][interpreter_key])
            performers = list(performers_it)
            performers.sort()
            for performer in performers[:num_top_performers]:
                # print('1:', type(performer.size))
                # print('2:', type(performer.duration))
                # print('3:', type(performer.tree_type))
                string = '\t\t\t<li>Ops: {:>7}, duration: {:>5} +/- {}, ops/sec: {:>11.2f}, dictionary type: {}'.format(
                    performer.size,
                    performer.duration,
                    performer.standard_deviation,
                    float(str(performer.size)) / float(str(performer.duration)),
                    performer.tree_type,
                    )
                print(string.replace(' ', '&nbsp;'))
            print('\t\t</ul>')
        print('\t</ul>')
    print('</ul>')
    print('</body>')
    print('</html>')



main()