###############################################################################
# Name: doctools.py                                                           #
# Purpose: Tools for managing document services                               #
# Author: Cody Precord <cprecord@editra.org>                                  #
# Copyright: (c) 2008 Cody Precord <staff@editra.org>                         #
# License: wxWindows License                                                  #
###############################################################################

"""
Provides helper functions and classes for managing documents and their services.

"""

__author__ = "Cody Precord <cprecord@editra.org>"
__svnid__ = "$Id: doctools.py 60537 2009-05-07 02:39:20Z CJP $"
__revision__ = "$Revision: 60537 $"

#--------------------------------------------------------------------------#
# Imports
import os
import sys

# Editra Libraries
import util
from profiler import Profile_Get

#--------------------------------------------------------------------------#

class DocPositionMgr(object):
    """Object for managing the saving and setting of a collection of
    documents positions between sessions. Through the use of an in memory
    dictionary during run time and on disk dictionary to use when starting
    and stopping the editor.
    @note: saves config to ~/.Editra/cache/

    """
    def __init__(self):
        """Creates the position manager object

        """
        object.__init__(self)

        # Attributes
        self._init = False
        self._book = None
        self._records = dict()

        # Non persisted navigation cache
        self._poscache = list()
        self._cpos = -1

    def InitPositionCache(self, book_path):
        """Initialize and load the on disk document position cache.
        @param book_path: path to on disk cache

        """
        self._init = True
        self._book = book_path
        if Profile_Get('SAVE_POS'):
            self.LoadBook(book_path)

    def AddNaviPosition(self, fname, pos):
        """Add a new postion to the navigation cache
        @param fname: file name
        @param pos: position

        """
        clen = len(self._poscache)
        if self._cpos+1 != clen:
            self._poscache = self._poscache[:self._cpos]

        self._poscache.append((fname, pos))
        self._cpos = len(self._poscache) - 1

    def AddRecord(self, vals):
        """Adds a record to the dictionary from a list of the
        filename vals[0] and the position value vals[1].
        @param vals: file path, cursor postion
        @type vals: tuple (str, int)

        """
        if len(vals) == 2:
            self._records[vals[0]] = vals[1]
            return True
        else:
            return False

    def GetBook(self):
        """Returns the current book used by this object
        @return: path to book used by this manager

        """
        return self._book

    def GetNextNaviPos(self, fname=None):
        """Get the next stored navigation position
        The optional fname parameter will get the next found position for
        the given file.
        @param fname: filename (note currently not supported)
        @return: int or None

        """
        if not len(self._poscache):
            return None

        # If at end wrap to begining
        if self._cpos >= len(self._poscache):
            self._cpos = 0

        rval = None
        # Get the position at the current history position marker
        rval = self._poscache[self._cpos]
        self._cpos += 1

    def GetPreviousNaviPos(self, fname=None):
        """Get the last stored navigation position
        The optional fname parameter will get the last found position for
        the given file.
        @param fname: filename (note currently not supported)
        @return: int or None

        """
        if not len(self._poscache):
            return None

        # If past begining wrap to the end
        if self._cpos < 0:
            self._cpos = len(self._cpos) - 1

        rval = None
#        if fname is None:
        # Get the position at the current history position marker
        rval = self._poscache[self._cpos]
        self._cpos -= 1
#        else:
#            # Get the last known position for the given file
#            idx = 0
#            for name, pos in reversed(self._poscache[:self._cpos]):
#                if name == fname:
#                    rval = pos

    def GetPos(self, name):
        """Get the position record for a given filename
        returns 0 if record is not found.
        @return: postion value for the given filename

        """
        return self._records.get(name, 0)

    def IsInitialized(self):
        """Has the cache been initialized
        @return: bool

        """
        return self._init

    def LoadBook(self, book):
        """Loads a set of records from an on disk dictionary
        the entries are formated as key=value with one entry
        per line in the file.
        @return: whether book was loaded or not
        @rtype: boolean

        """
        # If file does not exist create it and return
        if not os.path.exists(book):
            try:
                tfile = util.GetFileWriter(book)
                tfile.close()
            except (IOError, OSError):
                util.Log("[docpositionmgr][err] failed to load book: %s" % book)
                return False
            except AttributeError:
                util.Log("[docpositionmgr][err] Failed to create: %s" % book)
                return False

        reader = util.GetFileReader(book, sys.getfilesystemencoding())
        if reader != -1:
            lines = list()
            try:
                lines = reader.readlines()
            except:
                reader.close()
                return False
            else:
                reader.close()

            for line in lines:
                line = line.strip()
                vals = line.split(u'=')
                if len(vals) != 2 or not os.path.exists(vals[0]):
                    continue

                try:
                    vals[1] = int(vals[1])
                except (TypeError, ValueError), msg:
                    util.Log("[docpositionmgr][err] %s" % str(msg))
                    continue
                else:
                    self._records[vals[0]] = vals[1]

            util.Log("[docpositionmgr][info] successfully loaded book")
            return True

    def WriteBook(self):
        """Writes the collection of files=pos to the config file
        @postcondition: in memory doc data is written out to disk

        """
        writer = util.GetFileWriter(self.GetBook(), sys.getfilesystemencoding())
        if writer != -1:
            try:
                for key, val in self._records.iteritems():
                    try:
                        writer.write(u"%s=%d\n" % (key, val))
                    except UnicodeDecodeError:
                        continue
                writer.close()
            except IOError, msg:
                util.Log("[docpositionmgr][err] %s" % str(msg))
        else:
            util.Log("[docpositionmgr][err] Failed to open %s" % self.GetBook())
