# -*- coding: utf-8 -*-
# Wendelin.bigarray | ZODB-Persistent BigArray
# Copyright (C) 2014-2019  Nexedi SA and Contributors.
#                          Kirill Smelkov <kirr@nexedi.com>
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.

"""Package array_zodb provides BigArray backed by ZODB.

ZBigArray provides BigArray with data stored in ZODB.

Please see documentation for BigArray (bigarray/__init__.py) and ZBigFile
(bigfile/file_zodb.py) as ZBigArray is just a minimal glue for those two and
inherits most of the properties and specifics from its parents.
"""

from wendelin.bigarray import BigArray
from wendelin.bigfile.file_zodb import ZBigFile
from wendelin.lib.zodb import LivePersistent
import inspect


# TODO document that first access data must be either before commit or Connection.add

# {} BigArray arg -> default value
_ = inspect.getargspec(BigArray.__init__)
BigArray_defaults = dict(zip(reversed(_.args), reversed(_.defaults)))


class ZBigArray(BigArray,
                # Live: don't allow us to go to ghost
                # (not to loose ._v_fileh which works as DataManager)
                LivePersistent):
    __slots__ = (
        'zfile',            # ZBigFile serving this array
    )


    # XXX default blksize hardcoded
    def __init__(self, shape, dtype, order='C', blksize=2*1024*1024):
        LivePersistent.__init__(self)
        # NOTE BigArray is cooperative to us - it names all helping (= not needing
        # to be saved) data members starting with _v_. Were it otherwise, we
        # could not inherit from BigArray and would need to keep its instance
        # in e.g. ._v_array helper and redirect all BigArray method from us to it.
        BigArray._init0(self, shape, dtype, order)
        self.zfile = ZBigFile(blksize)
        self._v_fileh = None

    # __setstate__ (re-)loads ZBigArray data from DB, e.g. after live ZBigArray
    # object was invalidated.
    def __setstate__(self, state):
        super(ZBigArray, self).__setstate__(state)

        # for backward compatibility: if a member is missing in state - set it
        # to BigArray default. Ex. `order` was not set in early versions of
        # ZBigArray and when loading such objects from DB, without adjustment,
        # they won't work properly.
        for k, v in BigArray_defaults.items():
            if not hasattr(self, '_'+k):
                setattr(self, '_'+k, v)

        # (re-)set ._v_fileh; make sure not to loose fileh and its cache if
        # .zfile has not changed. By preserving fileh we don't throw away data
        # cache on e.g. just .shape change.
        #
        # TODO test/bench that cache is preserved after .shape change (e.g. append)
        _v_fileh = getattr(self, '_v_fileh', None)
        if _v_fileh is not None and self.zfile is not _v_fileh.zfile:
            _v_fileh = None
        self._v_fileh = _v_fileh


    # open fileh lazily, so that when we open it, zfile was already associated
    # with Connection (i.e. zfile._p_jar is not None). This is needed for
    # ZBigFile working.
    @property
    def _fileh(self):
        if self._v_fileh is None:
            self._v_fileh = self.zfile.fileh_open()
        return self._v_fileh
