# -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
import ulib.base.i_need_py23

__all__ = ('SimpleStore',)

import os, re
from os import path

from ulib.base.uio import _s, _u
from ulib.base.lines import Lines
from ulib.base.paths import mkdirof
from ulib.base.tmpfiles import actemp, cltemp
from ulib.base.flock import rdopen, wropen
from base import *
from consts import *
from store import AbstractStore, UnsavedModificationError

LASTID = 'lastid'

class SimpleStore(AbstractStore):
    GENERIC_PROPERTIES = AbstractStore.GENERIC_PROPERTIES + [LASTID]

    def __init__(self, inf=None):
        AbstractStore.__init__(self)
        self.__reset()
        if inf is not None: self.load(inf)

    def __reset(self):
        self._tasks = []
        self._lastid = 0
        self._tids = {}

    _tasks, tasks, get_tasks = make_sprop('_tasks', None, setter=False, deleter=False)
    _lastid, lastid, get_lastid, set_lastid, del_lastid = make_suprop('_lastid', 0)

    _tids = None
    def _nextid(self):
        _u = self._uio.u
        nextid = int(self._lastid) + 1
        while self._tids.has_key(_u(nextid)):
            nextid += 1
        self._lastid = nextid
        return nextid

    def _ensure_tid(self, task):
        if task._tid is None: task.tid = self._nextid()

    def _task_attached(self, task, validate=False):
        self._ensure_tid(task)
        if not self._tids.has_key(task._tid):
            self._tids[task._tid] = task
        AbstractStore._task_attached(self, task, validate)

    def remove(self, task):
        if task not in self._tasks: return False
        del self._tids[task._tid]
        self._tasks.remove(task)
        self._modified = True
        return True

    _fdn = None
    _fd = None
    _fn = None

    def get_name(self):
        u"""Retourner le nom de ce store
        """
        return _u(self._fn)
    name = property(get_name)

    RE_SPROPS = re.compile(r'##\s+sprops:')
    def load(self, infdn=None, force=False, **ignored):
        if self.is_modified() and not force:
            raise UnsavedModificationError

        if infdn is None:
            infdn = self._fdn
            if infdn is None: raise ValueError("Source file is required")
        else:
            self._fdn = path.abspath(infdn)
            self._fd, self._fn = path.split(self._fdn)
        self.__reset()
        self._valid = False
        lines = Lines()
        if path.exists(infdn):
            lines.readlines(infdn, open_func=rdopen)
            for line in lines:
                if self.RE_SPROPS.match(line) is not None:
                    self.set_props(**self._line2props(line, False))
                elif not line.strip() or line.startswith(u'#'):
                    # ignorer lignes vides et commençant par "#"
                    pass
                else:
                    task = self._tf.build_task(**self._line2props(line))
                    self.tasks.append(task)
                    self._task_attached(task, True)
        self._modified = False
        self._valid = True

    def __is_sprop(self, line):
        return self.RE_SPROPS.match(line) is not None

    def should_reload(self):
        infdn = self._fdn
        if infdn is None: return False
        if not path.exists(infdn): return False
        lines = Lines()
        lines.readlines(infdn, open_func=rdopen, until=self.__is_sprop)
        props = {}
        for line in lines:
            if self.RE_SPROPS.match(line) is not None:
                props = self._line2props(line, False)
                break
        return self.serial != props.get(SERIAL, None)

    ENCODING = u"""## -*- coding: %(encoding)s -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=%(encoding)s"""
    SPROP = u"""## sprops:"""

    def save(self, outfdn=None, force=False):
        if not force and not self.is_modified(): return False

        if outfdn is None: outfdn = self._fdn
        if outfdn is None: raise ValueError("Destination file is required")

        lines = self.get_lines(int(self.serial or 0) + 1)

        outf = wropen(outfdn)
        try:
            tmpfdn = outfdn + '.tmp'
            mkdirof(tmpfdn)
            li = actemp(tmpfdn)
            try:
                lines.writelines(tmpfdn)
                try:
                    os.rename(tmpfdn, outfdn)
                except OSError:
                    os.remove(outfdn)
                    os.rename(tmpfdn, outfdn)
            finally:
                cltemp(li)
        finally:
            outf.close()

        for task in self._tasks: task.modified = False
        self._modified = False
        return True
