# -*- coding: utf-8 -*-
"""
Created on Tue Mar 31 17:21:26 2020

@author: Manuel Camargo
"""

# -*- coding: utf-8 -*-
"""
Created on Wed Nov 21 21:23:55 2018

@author: Manuel Camargo
"""
import os
import itertools
from operator import itemgetter


import pandas as pd
import numpy as np
from support_modules import nn_support as nsup
from support_modules import role_discovery as rl
from support_modules.readers import log_reader as lr


def calculate_resource_dedication(one_timestamp, log, df_resources):
    """Main method of intercase features calculations.
    Args:
        args (dict): parameters for training the network.
    """
    # Event indexing
    log['event_id'] = log.index
    log = calculate_event_duration(log, one_timestamp)
    # splitt events in start and complete
    splitted_events = splitt_events(log, one_timestamp)
    # Matching events with slices
    ranges = match_slices(splitted_events)
    # Reshaping of ranges
    simplified = dict()
    for i in range(0, len(ranges)):
        simplified[i] = list(ranges[i]['events'])
    ranges_df = pd.DataFrame.from_dict(ranges, orient='index')
    ranges_dur = ranges_df[['start', 'end', 'duration']]
    ranges_slides = (pd.DataFrame.from_dict(simplified, orient='index')
                        .stack()
                        .reset_index()
                        .drop('level_1', axis=1)
                        .rename(columns={'level_0': 'slide', 0: 'event_id'}))
    ranges_slides = ranges_slides.merge(ranges_dur, left_on='slide', right_index=True, how='left')
    # Join with event log
    expanded_log = ranges_slides.merge(log, on='event_id', how='left')
    # Work item level features calculation
    expanded_log = calculate_work_item_features(expanded_log, df_resources)
    # Event level features calculation
    log = calculate_event_features(expanded_log, log)
    # log = nsup.scale_feature(log, 'ev_rp_occ', 'max', True)
    return log
    

def calculate_work_item_features(expanded_log, df_resources):
#   wi_mt: Same resource ejecuting multiple tasks (count)(work item)
    wi_mt = pd.DataFrame(
            expanded_log.groupby(['slide', 'user'])
            .count()['event_id']
            .reset_index(name = 'wi_mt'))
#   Active resources per pool and resource pool ocupation 
    pool_size = df_resources.groupby(['role']).count()['user'].reset_index(name = 'psize')
#    print(pool_size)
    wi_rlc = pd.DataFrame(
            expanded_log.groupby(['slide', 'role'])
            .count()['event_id']
            .reset_index(name = 'wi_rlc'))
    wi_rlc = wi_rlc.merge(pool_size, on='role', how='left')
    wi_rlc['rp_occ'] = wi_rlc['wi_rlc']/wi_rlc['psize']
#   Join features to the extended log 
    expanded_log = expanded_log.merge(wi_mt, on=['slide', 'user'], how='left')
    expanded_log = expanded_log.merge(wi_rlc, on=['slide', 'role'], how='left')
    return expanded_log

    
def calculate_event_features(expanded_log, log):
#   Weigth of slide calculation (slide duration / event duration)
    expanded_log['wi_w'] = np.divide(expanded_log['duration'],
                                        expanded_log['ev_duration'])
#   Resource dedication per event
    expanded_log['wi_rd'] = np.divide(expanded_log['duration'],
                                              expanded_log['wi_mt'])
    ev_rd = pd.DataFrame(
            expanded_log.groupby(['event_id'])
            .sum()['wi_rd']
            .reset_index(name = 'ev_rd'))
#   Resource pool ocupation weighted average
    expanded_log['wi_w_rp_occ'] = np.multiply(expanded_log['wi_w'],
                                                  expanded_log['rp_occ'])
    ev_rp_occ = pd.DataFrame(
            expanded_log.groupby(['event_id'])
            .sum()['wi_w_rp_occ']
            .reset_index(name = 'ev_rp_occ'))
#   Join with log dataframe
    log = log.merge(ev_rd, on='event_id', how='left')
    log = log.merge(ev_rp_occ, on='event_id', how='left')
#   Rate of resource dedication per event 
    log['ev_rd_p'] = np.divide(log['ev_rd'],log['ev_duration'])
    log['ev_rd_p'].fillna(0, inplace=True)
    return log
    
def calculate_event_duration(log, one_timestamp):
    log = log.to_dict('records')
    if one_timestamp:
        log = sorted(log, key=lambda x: x['caseid'])
        for key, group in itertools.groupby(log, key=lambda x: x['caseid']):
            events = list(group)
            events = sorted(events, key=itemgetter('end_timestamp'))
            for i in range(0, len(events)):
                # In one-timestamp approach the first activity of the trace is taken as instant
                # since there is no previous timestamp to find a range
                if i == 0:
                    events[i]['ev_duration'] = 0
                    events[i]['ev_acc_duration'] = 0
                else:
                    dur = (events[i]['end_timestamp']-events[i-1]['end_timestamp']).total_seconds() 
                    events[i]['ev_duration'] = dur
                    events[i]['ev_acc_duration'] = events[i-1]['ev_acc_duration'] + dur
    else:
        log = sorted(log, key=itemgetter('start_timestamp'))
        for event in log:
            # on the contrary is btw start and complete timestamp 
            event['ev_duration']=(event['end_timestamp'] - event['start_timestamp']).total_seconds()
    return pd.DataFrame.from_dict(sorted(log, key=lambda x: x['event_id']))
    

def splitt_events(log, one_timestamp):
    log = log.to_dict('records')
    splitted_events = list()
    # Define date-time ranges in event log   
    if one_timestamp:
        log = sorted(log, key=lambda x: x['caseid'])
        for key, group in itertools.groupby(log, key=lambda x: x['caseid']):
            events = list(group)
            events = sorted(events, key=itemgetter('end_timestamp'))
            for i in range(0, len(events)):
                # In one-timestamp approach the first activity of the trace is taken as instant
                # since there is no previous timestamp to find a range
                if i == 0:
                    splitted_events.append((events[i]['end_timestamp'], True, events[i]['end_timestamp'], events[i]['event_id']))
                    splitted_events.append((events[i]['end_timestamp'], False, events[i]['end_timestamp'], events[i]['event_id']))
                else:
                    splitted_events.append((events[i-1]['end_timestamp'], True, events[i]['end_timestamp'], events[i]['event_id']))
                    splitted_events.append((events[i]['end_timestamp'], False, events[i-1]['end_timestamp'], events[i]['event_id']))
    else:
        log = sorted(log, key=itemgetter('start_timestamp'))
        for event in log:
            # on the contrary is btw start and complete timestamp 
            splitted_events.append((event['start_timestamp'], True, event['end_timestamp'], event['event_id']))
            splitted_events.append((event['end_timestamp'], False, event['start_timestamp'], event['event_id']))

    splitted_events.sort(key=lambda tup: tup[0])
    return splitted_events

def match_slices(splitted_events):
    current_set = set()
    ranges = dict()
    current_start = -1

    for endpoint, is_start, other, symbol in splitted_events:
        if is_start:
            if current_start != -1 and endpoint != current_start and \
                   endpoint >= current_start and current_set:
                ranges[(len(ranges))]={
                        'duration': (endpoint-current_start).total_seconds(),
                        'start': current_start,
                        'end': endpoint,
                        'events': current_set.copy()}
            current_start = endpoint
            current_set.add(symbol)
        else:
            if current_start != -1 and endpoint >= current_start and current_set:
                ranges[(len(ranges))]={
                        'duration': (endpoint-current_start).total_seconds(),
                        'start': current_start,
                        'end': endpoint,
                        'events': current_set.copy()}
            current_set.remove(symbol)
            current_start = endpoint
    return ranges   