# Copyright (C) 2010 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA

"""Storage and retrieval of recently opened locations."""

import errno, os, pickle, time

from bzrlib import osutils, trace, urlutils

from bzrlib.plugins.explorer.lib.extensions import accessories


class HistoryManager(object):
    # The disk format is a Python pickle of a list of tuples.
    # Each tuples is url, kind, title, timestamp.

    # The maximum number of locations remembered
    max_size = 100

    def __init__(self):
        data_dir = accessories.wallet_dir()
        self._data_file = osutils.pathjoin(data_dir, "history.dat")
        self._history = self._load()

    def _load(self):
        """Get history list from disk, will be empty if file is unreadable"""
        try:
            f = open(self._data_file, "rb")
            try:
                return pickle.load(f)
            finally:
                f.close()
        except IOError, e:
            if e.errno == errno.ENOENT:
                # File does not exist yet, not a problem
                return []
        except (KeyboardInterrupt, SystemExit):
            # Do the right thing for Python 2.4 exception hierarchy
            raise
        except Exception:
            # Unpickling can raise all kinds of different Python exceptions,
            # so we have to just swallow everything unfortunately.
            pass
        trace.mutter("Failed to load bzr-explorer history from file %r",
            self._data_file)
        trace.log_exception_quietly()
        return []

    def _save(self):
        """Save history to disk."""
        try:
            file = open(self._data_file, "wb")
        except IOError, ex:
            trace.mutter("failed to write to %s: %s", self._data_file, ex)
            return
        try:
            pickle.dump(self._history[:self.max_size], file,
                pickle.HIGHEST_PROTOCOL)
        finally:
            file.close()

    def add_recent(self, path_or_url, kind, title):
        """Record a location as recently opened.

        :param path_or_url: the location path or URL
        :param kind: the location kind (a string)
        :param title: the location title
        """
        # If the url is already in history, delete it before adding it again
        url = urlutils.normalize_url(path_or_url)
        index = self._index_of(url)
        if index != -1:
            del self._history[index]
        timestamp = int(time.time())
        self._history.insert(0, (url, kind, title, timestamp))
        self._save()

    def _index_of(self, url):
        """Return the index of url in history or -1 if not there."""
        for i in range(0, len(self._history)):
            if self._history[i][0] == url:
                return i
        return -1

    def recent_locations(self, since=0, maximum=25, skip_missing_paths=True,
        as_paths=True):
        """Return the list of recently opened locations.

        :param since: only return locations opened after this timestamp
        :param maximum: the maximum number of locations to return
        :param skip_missing_paths: if True, only include local paths if they
          still exist, otherwise all locations are included.
        :param as_paths: if True, return local paths for file:// URLs,
          otherwise the URL is returned
        :return: a list of (location, kind, title) tuples, most recently
          opened first. location is either a path or URL, depending on the
          as_paths parameter.
        """
        result = []
        for url, kind, title, timestamp in self._history:
            if timestamp < since:
                break
            if url.startswith("file://"):
                path = urlutils.local_path_from_url(url)
                if skip_missing_paths and not os.path.exists(path):
                    continue
                if as_paths:
                    url = path
            result.append((url, kind, title))
            if len(result) >= maximum:
                break
        return result
