#!/usr/bin/env python
# coding: utf-8

import sys, os
import matplotlib
matplotlib.use('Agg')
from pylab import *
from matplotlib.transforms import TransformedBbox 

class EventFactory(object):
    #CPU time pid cpu (si pid == -1 -> idle)
    cpu_event= lambda x: Event(x[0],EventFactory.Events.keys().index(x[0]),int(x[1]),int(x[2]),int(x[3]))
    #EVENT time pid  
    other_event= lambda x: Event(x[0],EventFactory.Events.keys().index(x[0]),int(x[1]),int(x[2]),-1)
    #CONTEXT CPU cpu time (se pone pid == -2) 
    context_switch_event= lambda x: Event(x[0] ,EventFactory.Events.keys().index(x[0]),int(x[3]),-2,int(x[2]))
    Events = {'LOAD': other_event,
              'CPU':cpu_event, 
              'BLOCK': other_event, 
              'UNBLOCK': other_event, 
              'EXIT': other_event,
              'CONTEXT':context_switch_event,
              'WAITING':None,
              'NOT_LOAD': None,
              'CPU_BLOCK': None}
    
    @classmethod
    def get_event(cls, event_line):
        splited_event_line= event_line.split()
        if splited_event_line[0] in EventFactory.Events.keys():
            return EventFactory.Events[splited_event_line[0]](splited_event_line)
        else:
            return None
        

class Event(object):
    def __init__(self, event_type, event_code, time, pid, core):
        self.event_type= event_type
        self.event_code= event_code
        self.time= time
        self.pid= pid
        self.core= core
    def __str__(self):
        return 'Type: ' + self.event_type + ', Code: ' + str(self.event_code) + ', Time: ' + str(self.time) + ', Pid: ' + str(self.pid) + ', Core: ' + str(self.core)

def parseInput(fin):
    ln = 0
    result = []
    cores = 0
    pids= 0
    settings = None
    cpus_timeline= dict()
    for line in fin:
        ln += 1
        vls = line.split()
        if line and line[0] == '#':
            if line.startswith('# SETTINGS '):
                settings = line[11:].strip()
                continue
            else:
                line= line[2:].strip() # Queda --> 'CONTEXT CPU cpu  time

        event= EventFactory.get_event(line)
        result.append(event)
        
        if event.event_type == 'CPU':
            if (cores <=event.core):
                cores = event.core+1

        if event.event_type == 'LOAD':
            if(pids <= event.pid):
                pids = event.pid +1
            
    return settings, result, cores, pids

def dataGathering(data, cores, pids):
    core_timeline= dict() # core: list(pids) NOTA: se supone que en cada tick hay un pid
    
    block_lapse= dict()

    for event in data:
        #core_time
        if event.core != -1:
             if event.core not in core_timeline: core_timeline[event.core]= []
             core_timeline[event.core].append(event.pid)
        
    return core_timeline

def draw_cores_timeline_gannt(cores_timeline, filename):
    ''' pre: cores_timeline = {core: list(pid) } '''
    
    # Necesitaria tener la info algo asi como
    # core: {pid: ini_1,fin_1,ini_2,fin_2} (es decir por intervalos)
    # broken_barh necesita (inicio, longitud)
    
    colors={'pid':'#c0ffc0','switch':'#b7b7f7', 'idle':'#d0d0d0'}

    fig= figure(figsize=(11.8,8.3))
    ax = fig.add_subplot(111) 
    title('Tareas en Core por tiempo')
    yticks(cores_timeline.keys())
#    ax.xaxis.set_major_locator( 
    ax.xaxis.set_major_locator( IndexLocator(2,1) )
    #xticks(range(len(cores_timeline[0])),range(0,len(cores_timeline[0]),5))
    xlabel('Tiempo')
    ylabel('Core')
    ylim((-1,len(cores_timeline.keys())))
    ax.grid(True)            

    for core in cores_timeline:
        pids_by_time= cores_timeline[core]
        intervals= dict()
        last_pid= None
        for time in range(len(pids_by_time)):
            if last_pid != pids_by_time[time]:
                last_pid = pids_by_time[time]
                if last_pid not in intervals: intervals[last_pid]= []
                intervals[last_pid].append((time, 1))
                #intervals.push((last_pid,time,1))
            else:
                #pid, time, interval_size= intervals.pop()
                #intervals.push((pid, time, interval_size+1))
                time, interval_size= intervals[last_pid].pop()
                intervals[last_pid].append((time, interval_size+1))
        
        for pid in intervals:
            if pid >= 0:
                rect= ax.broken_barh(intervals[pid], (core-0.25, 0.5), facecolor=colors['pid'])
                for init, size in intervals[pid]:
                    ax.text(init+(size/2.0),core, str(pid), ha="center", va="center", size=9, weight='bold')
            elif pid == -1:
                rect= ax.broken_barh(intervals[pid], (core-0.25, 0.5), facecolor=colors['idle'])
            elif pid == -2:
                rect= ax.broken_barh(intervals[pid], (core-0.25, 0.5), facecolor=colors['switch'])
            else:
                print 'ERROR!'
    
    tarea_dummy = Rectangle((0, 0), 1, 1, fc=colors['pid'])
    switch_dummy = Rectangle((0, 0), 1, 1, fc=colors['switch'])
    idle_dummy = Rectangle((0, 0), 1, 1, fc=colors['idle'])
    legend([tarea_dummy, switch_dummy, idle_dummy], ['Tarea','Cambio de contexto','Inactivo'])
    tight_layout()
    fig.autofmt_xdate()
    ax.legend()
    savefig(filename+'.png', dpi=300, format='png')

def main(argv):
    if len(argv) <= 1:
        fin = sys.stdin
        fout_cores_timeline= 'out_cores_timeline'
    else:
        fin = open(argv[1], 'r')
        preffix= argv[1]
        fout_cores_timeline= preffix + '_cores_timeline' 
    
    print 'parsing input'
    settings, data, cores, pids = parseInput(fin)
    
    print 'data gathering'
    cores_timeline= dataGathering(data, cores, pids)
    print cores_timeline
    #todo dump de los datos
    print 'drawing cores timeline'
    draw_cores_timeline_gannt(cores_timeline, fout_cores_timeline)

if __name__ == "__main__":
    main(sys.argv)
