import math 
import numpy as np

class TestLog(object):
    def __init__(self):
        self.frames = [0]
        self.bg_name = None
        self.fg_name = None
        self.activities = {}
    
    def _compute_absolute_position(self, relative_frame,  start_frame, stride):
        return int(math.floor(relative_frame*stride+start_frame))

    def _compute_relative_position(self, absolute_frame, start_frame, stride):
        return (absolute_frame-start_frame)/stride

    def _compute_left_right_frame(self, temporal_box, frames):
        relative_left_frame, relative_right_frame, \
                presence_conf =  temporal_box.tolist()
        _, start_frame, end_frame, stride = frames
        left_frame = self._compute_absolute_position(relative_left_frame,
                                    start_frame, stride)
        right_frame = self._compute_absolute_position(relative_right_frame,
                                    start_frame,stride)

        if left_frame > right_frame:
            left_frame, right_frame = right_frame, left_frame
        return left_frame, right_frame

    def _compute_temporal_endpoints(self, temporal_box_one, temporal_box_two,
                                    frames_one, frames_two):
        left_frame_one, right_frame_one = self._compute_left_right_frame(
                                    temporal_box_one, frames_one)
        left_frame_two, right_frame_two = self._compute_left_right_frame(
                                    temporal_box_two, frames_two)
        left_frame  = max(left_frame_one, left_frame_two)
        right_frame = min(right_frame_one, right_frame_two)
        is_left_from_first = left_frame_one == left_frame
        is_right_from_first = right_frame_one == right_frame
        return left_frame, right_frame, is_left_from_first, is_right_from_first

    def _compute_temporal_intersection(self, temporal_box_one, temporal_box_two,
                                        frames_one, frames_two):
        left_frame_one, right_frame_one = self._compute_left_right_frame(
                                    temporal_box_one, frames_one)
        left_frame_two, right_frame_two = self._compute_left_right_frame(
                                    temporal_box_two, frames_two)
        left_frame, right_frame, _, __ = self._compute_temporal_endpoints(
                                        temporal_box_one, temporal_box_two, 
                                        frames_one, frames_two)

        if left_frame > right_frame:
            intersection = 0
        else:
            intersection = right_frame - left_frame
        
        union = right_frame_one - left_frame_one + right_frame_two - \
                left_frame_two - intersection
        return float(intersection) / union
        
    def compute_merge_indices(self, activity_one, activity_two, frames_one, 
                                frames_two, temporal_intersection_threshold):
        merge_dict = {}
        for box_one_index, temporal_box_one in enumerate(activity_one):
            for box_two_index, temporal_box_two in enumerate(activity_two):
                temporal_intersection =  self._compute_temporal_intersection(temporal_box_one, 
                                        temporal_box_two, frames_one, frames_two)
                if temporal_intersection >  temporal_intersection_threshold:
                    if box_two_index in merge_dict.keys():
                        if merge_dict[box_two_index][1] < temporal_intersection:
                            merge_dict[box_two_index] = (box_one_index, 
                                                        temporal_intersection)
                    else:
                        merge_dict[box_two_index] = (box_one_index, 
                                                      temporal_intersection)
        merge_indices = []
        for box_two_index, value in merge_dict.iteritems():
            box_one_index, _ = value
            merge_indices.append((box_one_index, box_two_index))
                    
        return merge_indices
    
    def merge_activities(self, activity_one, activity_two, frames_one, 
                            frames_two, merge_indices):
        removal_index = []
        for merge_index in merge_indices:
            left_frame, right_frame, is_left_frame_one, is_right_frame_one = \
                    self._compute_temporal_endpoints(activity_one[merge_index[0]],
                            activity_two[merge_index[1]], frames_one, frames_two)
            if not is_left_frame_one:
                relative_left_frame = self._compute_relative_position(left_frame,
                        frames_one[1], frames_one[3])
            else:
                relative_left_frame = activity_one[merge_index[0]][0]
            if not is_right_frame_one:
                relative_right_frame = self._compute_relative_position(right_frame,
                        frames_one[1], frames_one[3])
            else:
                relative_right_frame = activity_one[merge_index[0]][1]
            activity_one[merge_index[0]][0] = relative_left_frame
            activity_one[merge_index[0]][1] = relative_right_frame
            removal_index.append(merge_index[1])
        new_activity_two = []
        for index in range(len(activity_two)):
            if index not in removal_index:
                new_activity_two.append(activity_two[index])
        new_activity_two = np.array(new_activity_two)
        return activity_one, new_activity_two

    def combine_logs(self, other_log, temporal_intersection_threshold):
        if self.fg_name != other_log.fg_name:
            return self, other_log
        for activity_index in self.activities.keys():
            for other_activity_index in other_log.activities.keys():
                if activity_index != other_activity_index:
                    continue
                merge_indices = self.compute_merge_indices(
                                        self.activities[activity_index], 
                                        other_log.activities[other_activity_index],
                                        self.frames, other_log.frames,
                                        temporal_intersection_threshold)
                self.activities[activity_index], activity_two = \
                        self.merge_activities(self.activities[activity_index], 
                                        other_log.activities[other_activity_index],
                                        self.frames, other_log.frames, merge_indices)
                if activity_two.shape[0] > 0: 
                    other_log.activities[other_activity_index] = activity_two
                else:
                    del other_log.activities[other_activity_index]
        return self, other_log
    
    def __repr__(self):
        return "Frames: {0} \nVideo Name: {1} \n Activities: {2} \n".format(
                self.frames, self.fg_name, self.activities)
