#
#  $Id: Store.py,v 1.10 1999/11/29 06:57:18 rob Exp $
#
#  Copyright 1999 Rob Tillotson <robt@debian.org>
#  All Rights Reserved
#
#  Permission to use, copy, modify, and distribute this software and
#  its documentation for any purpose and without fee or royalty is
#  hereby granted, provided that the above copyright notice appear in
#  all copies and that both the copyright notice and this permission
#  notice appear in supporting documentation or portions thereof,
#  including modifications, that you you make.
#
#  THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
#  THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
#  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
#  RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
#  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
#  CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE!
#
"""Data Store API.

Stores may have the following properties:

  read    - can open existing databases for reading
  write   - can open existing databases for writing
  create  - can create new databases (which are implicitly open for writing)
  delete  - can delete databases
  list    - can list databases and their info
  install - has an install method which should be used when copying into
            this store, instead of using the normal 'copy' method
"""

__version__ = '$Id: Store.py,v 1.10 1999/11/29 06:57:18 rob Exp $'

__copyright__ = 'Copyright 1999 Rob Tillotson <robt@debian.org>'

import Pyrite
from Pyrite import Plugin, Database, PrefBlock, _

import sys
#import DBTypes

class BaseStore:
    properties = ()
    db_properties = ()
    
    def __init__(self):
	self.context = None

    def set_context(self, c):
	self.context = c

    def __getattr__(self, k):
	if hasattr(self.context, 'option_values') and \
	   self.context.option_values.has_key(k):
	    return self.context.option_values[k]
	else: raise AttributeError, k
	
    def has_option(self, k):
	return hasattr(self.context, 'option_values') and \
	       self.context.option_values.has_key(k)

    def get_option(self, k):
	if hasattr(self.context, 'option_values'):
	    return self.context.option_values[k]
	else: raise KeyError, k

    def get_plugin(self, *a, **kw):
	return apply(self.context.get_plugin, a, kw)

    def list_plugins(self, *a, **kw):
	return apply(self.context.list_plugins, a, kw)

    def list_plugin_info(self, *a, **kw):
	return apply(self.context.list_plugin_info, a, kw)
    
    # property support
    def has_property(self, p): return p in self.properties

    def test_db_properties(self, properties):
	"""Check whether this store can provide a particular type of database.
	
	Given a list of DB properties, this function tests whether a database
	with the requested properties is possible using this store.  If it is,
	this function returns a propertly list which describes the resulting
	database.  (Note that the returned property list might be LONGER than
	the one supplied to this function: that is, if you ask for a database
	with the 'foo' property and the only way the store can grant that is
	by returning a ['foo', 'bar', 'baz'] database, that's what will be
	returned.  If the property combination isn't possible, returns None.

	The standard behavior of this function assumes that all databases opened
	by a particular Store have the same properties.  Should this not be
	the case, the Store should override.
	"""
	for p in properties:
	    if p not in self.db_properties: return None
	return self.db_properties

    def open(self, name, mode='rs', dbclass=Database, properties=(), **kw):
	if not self.test_db_properties(properties):
	    raise RuntimeError, _("cannot provide db with properties %s") % properties
	t = apply(self._open, (name, mode, properties), kw)
	return apply(dbclass, t)
    
    def create(self, name, creator, type, flags=0, version=1, dbclass=Database,
	       properties=(),
	       filename = None, info=None, **kw):
	if not self.test_db_properties(properties):
	    raise RuntimeError, _("cannot provide db with properties %s") % properties
	t = apply(self._create, (name, creator, type, flags, version, filename,
				 info), kw)
	return apply(dbclass, t)

    def delete(self, name, **kw):
	"""Delete a database in the store."""
	raise RuntimeError, _("unimplemented method")

    def info(self, name, **kw):
	"""Get info about a database."""
	raise RuntimeError, _("unimplemented method")

    def list(self, **kw):
	"""Return a list of database names."""
	raise RuntimeError, _("unimplemented method")

    def listinfo(self, name=None, creator=None, type=None, **kw):
	"""Return a list of database names with information."""
	raise RuntimeError, _("unimplemented method")

    def getpref(self, creator, id, saved=1):
	"""Return a preference (as a raw string!)."""
	raise RuntimeError, _("unimplemented method")

    def setpref(self, raw, creator, id, version=0, saved=1):
	"""Set a preference."""
	raise RuntimeError, _("unimplemented method")
	
    #####
    def install(self, store, name):
	store.copy(self, name)
	
    def copy(self, store, name):
	"""Copy a database into another store."""
	idb = self.open(name, 'rs')
	try:
	    store.delete(name)
	except:
	    pass
	flags = 0
	if idb.info['flagReset']: flags = flags | 0x0020
	if idb.info['flagResource']: flags = flags | 0x0001
	if idb.info['flagNewer']: flags = flags | 0x0010
	if idb.info['flagExcludeFromSync']: flags = flags | 0x0080
	if idb.info['flagAppInfoDirty']: flags = flags | 0x0004
	if idb.info['flagReadOnly']: flags = flags | 0x0002
	if idb.info['flagBackup']: flags = flags | 0x0008
	if idb.info['flagOpen']: flags = flags | 0x8000
	odb = store.create(name, idb.info['creator'], idb.info['type'],
			   flags, idb.info['version'], info=idb.info)

	try:
	    a = idb.get_appblock()
	    odb.set_appblock(a)
	except:
	    pass

	# For some reason, there is a segfault inside pilot-link on my
	# system.  For now, this part is just skipped as a result.  For
	# anyone who is saying "but that means the sortblock isn't copied",
	# I simply point to the source to pilot-link, and note that it isn't
	# copied there either...
	#a = idb.getSortBlock()
	#odb.setSortBlock(a)
	#print "sortblock"

	if idb.info['flagResource']:
	    for i in range(0, len(idb)):
		r = idb[i]
		odb.append(r)
	else:
	    for i in range(0, len(idb)):
		r = idb[i]
		if r.deleted or r.archived:
		    continue
		odb.append(r)

	idb.close()
	odb.close()
    
class Store(Plugin.Plugin):
    type = 'Store'
    store_class = BaseStore

    def __call__(self, *a, **kw):
	o = apply(self.store_class, a, kw)
	o.set_context(self)
	# copy plugin attributes
	o.name = self.name
	o.version = self.version
	o.author = self.author
	o.description = self.description
	o.url = self.url
	return o
    
