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

import random
import copy

NO_TRACKER = -1 # Tracker id for untracked values

class TrackerInfo(object):
    '''Tracker state machine(singleton)

    The singleton instance of this class is used to manipulate tracker
    selection and tracked values, as well as interrogate tracker colors.

    _tracks - dictionary for tracked values.  The tracker id is used as the
        key.  A tracker is a dictionary that stored tracked values keyed by
        its coordinates(x, y).  _tracks[tracker_id][(x, y)] == tracked value

    current_tracker - The tracker id for the currently selected tracker
    showing_tracker - The tracker id for the tracker that is currently being
        viewed.  The point to this member is to store off the tracker when
        the player switches to "Untracked" so that the last tracker they were
        working on stays in view after the switch.
    '''
    __single = None
    _tracks = {}
    _colors = {}
    current_tracker = NO_TRACKER
    showing_tracker = NO_TRACKER

    def __new__(cls, *args, **kwargs):
        '''Overridden to implement as a singleton
        '''
        # Check to see if a __single exists already for this class
        # Compare class types instead of just looking for None so
        # that subclasses will create their own __single objects
        if cls != type(cls.__single):
            cls.__single = object.__new__(cls, *args, **kwargs)
        return cls.__single

    def __init__(self):
        # Only initialize the colors once
        if self._colors:
            return
        # Use tango colors recommended here:
        # http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines
        for tracker_id, cols in enumerate(
        [(32, 74, 135), # Sky Blue 3
         (78, 154, 6), # Chameleon 3
         (206, 92, 0), # Orange 3
         (143, 89, 2), # Chocolate 3
         (92, 53, 102), # Plum 3
         (85, 87, 83), # Aluminium 5
         (196, 160, 0) # Butter 3
        ]):
            self._colors[tracker_id] = tuple([x / 255.0 for x in cols])

    def load(self, pickle):
        self.current_tracker, self.showing_tracker, self._tracks = pickle

    def save(self):
        return (self.current_tracker, self.showing_tracker, self.get_trackers())

    def create_tracker (self, tracker_id = 0):
        '''Create storage for a new tracker

        tracker_id can be passed in to attempt creation of a specific id, but
        if the tracker_id already exists then the passed number will be
        incremented until a suitable key can be allocated.
        '''
        if not tracker_id:
            tracker_id = 0
        while self._tracks.has_key(tracker_id):
            tracker_id += 1
        self._tracks[tracker_id] = {}
        return tracker_id

    def get_tracker(self, tracker_id):
        if self._tracks.has_key(tracker_id):
            return self._tracks[tracker_id]

    def delete_tracker(self, tracker_id):
        if self._tracks.has_key(tracker_id):
            del self._tracks[tracker_id]

    def reset (self):
        ''' Reset the tracker information
        '''
        self._tracks = {}
        self.current_tracker = NO_TRACKER
        self.showing_tracker = NO_TRACKER

    def use_trackers (self, trackers):
        self._tracks = trackers

    def get_trackers(self):
        return copy.deepcopy(self._tracks)

    def set_tracker(self, tracker_id):
        self.current_tracker = tracker_id
        if tracker_id != NO_TRACKER:
            self.showing_tracker = tracker_id

    def hide_tracker(self):
        self.showing_tracker = NO_TRACKER

    def get_tracker_view(self):
        return((self.current_tracker, self.showing_tracker))

    def set_tracker_view(self, tview):
        self.current_tracker, self.showing_tracker = tview

    def get_color (self, tracker_id):
        # Untracked items don't get specially colored
        if tracker_id == NO_TRACKER:
            return None
        # Create a random color for new trackers that are beyond the defaults
        if not self._colors.has_key(tracker_id):
            random_color = self._colors[0]
            while random_color in self._colors.values():
                # If we have generated all possible colors, this will
                # enter an infinite loop
                random_color = (random.randint(0, 100)/100.0,
                                random.randint(0, 100)/100.0,
                                random.randint(0, 100)/100.0)
            self._colors[tracker_id] = random_color
        return self._colors[tracker_id]

    def get_color_markup(self, tracker_id):
        color_tuple = self.get_color (tracker_id)
        if not color_tuple:
            return None
        color_markup = '#'
        color_markup += str(hex(int(color_tuple[0]*255))[2:]).zfill(2)
        color_markup += str(hex(int(color_tuple[1]*255))[2:]).zfill(2)
        color_markup += str(hex(int(color_tuple[2]*255))[2:]).zfill(2)
        return color_markup.upper()

    def get_current_color(self):
        return self.get_color(self.current_tracker)

    def get_showing_color(self):
        return self.get_color(self.showing_tracker)

    def add_trace(self, x, y, value, tracker_id = None):
        '''Add a tracked value

        By default(tracker_id set to None) this method adds a value to the
        current tracker.  tracker_id can be passed in to add it to a specific
        tracker.
        '''
        if tracker_id == None:
            to_tracker = self.current_tracker
        else:
            to_tracker =  tracker_id
        # Need a tracker
        if to_tracker == NO_TRACKER:
            return
        # Make sure the dictionary is available for the tracker.
        if not self._tracks.has_key(to_tracker):
            self._tracks[to_tracker] = {}
        # Add it
        self._tracks[to_tracker][(x, y)] = value

    def remove_trace(self, x, y, from_tracker = None):
        '''Remove a tracked value

        By default(from_tracker set to None) this method removes all tracked
        values for a particular cell(x, y coords).  from_tracker can be passed
        to remove tracked values from a particular tracker only.
        '''
        if from_tracker == None:
            from_tracks = self._tracks.keys()
        else:
            from_tracks = [from_tracker]
        # Delete them
        for tracker in from_tracks:
            if self._tracks.has_key(tracker) and self._tracks[tracker].has_key((x, y)):
                del self._tracks[tracker][(x, y)]

    def get_trackers_for_cell(self, x, y):
        '''Return all trackers for a cell

        This function is used for the undo mechanism.  A list in the format
        (tracker, value) is returned so that it may later be reinstated with
        reset_trackers_for_cell().
        '''
        ret = []
        for tracker, track in self._tracks.items():
            if track.has_key((x, y)):
                ret.append((tracker, track[(x, y)]))
        return ret

    def reset_trackers_for_cell(self, x, y, old_trackers):
        '''Reset all trackers to a previous state for a cell

        This function is used for the undo mechanism.  It reinstates the
        tracked values the list created by get_trackers_for_cell().
        '''
        # Remove all the current traces
        for tracker, track in self._tracks.items():
            if track.has_key((x, y)):
                del self._tracks[tracker][(x, y)]
        # Add the old ones back
        for tracker, value in old_trackers:
            self._tracks[tracker][(x, y)] = value


