#coding=utf-8

'''
base on bsddb, key and value must be string or buffer
suport set, get, delete, has_key, setex, keys, values, flushall
'''

import time
import bsddb

class Cache:

	def __init__(self, db_file):
		self._db_file = db_file
		self._db = bsddb.btopen(self._db_file, 'c')

	def set(self, k, v):
		k, v = str(k), str(v)
		self._db[k] = v
		return self

	def get(self, k, default_v = None):
		k = str(k)
		time_set_k = self._gen_time_set_key(k)
		time_expire_k = self._gen_time_expire_key(k)
		v = None
		time_set_v = None
		time_expire_v = None
		try: v = self._db[k]
		except: pass
		try: time_set_v = self._db[time_set_k]
		except: pass
		try: time_expire_v = self._db[time_expire_k]
		except: pass
		if time_set_v and time_expire_v:
			'''
			add by setex()
			'''
			set_time = float(time_set_v)
			expire_time = float(time_expire_v)
			if time.time() - set_time >= expire_time:
				self._db.pop(k)
				self._db.pop(time_set_k)
				self._db.pop(time_expire_k)
				return default_v
		if v == None:
			return default_v
		return v
		
	def has_key(self, k):
		if self.get(k): 
			return True
		else:
			return False

	def delete(self, k):
		k = str(k)
		self._db.delete(k)
		return self

	def setex(self, k, v, seconds):
		k, v, seconds = str(k), str(v), int(seconds)
		time_set_k = self._gen_time_set_key(k)
		time_expire_k = self._gen_time_expire_key(k)
		cur_time = str(time.time())
		if seconds == 0: return
		if seconds < 0:
			self.set(k, v)
			return
		self.set(k, v)
		self.set(time_set_k, cur_time)
		self.set(time_expire_k, seconds)
		return self

	def _gen_time_set_key(self, k):
		return 'agileutil_cache_time_set_k_%s' % k

	def _gen_time_expire_key(self, k):
		return 'agileutil_cache_time_expire_k_%s' % k

	def keys(self):
		return filter(
			lambda x: 'agileutil_cache_time_set_k' not in x and 
			'agileutil_cache_time_expire_k_' not in x, 
			self._db.keys()
		)

	def values(self):
		return filter(lambda x: x is not None,  [self.get(k) for k in self.keys()])

	def flushall(self):
		self._db.clear()
		return self

	def delete(self, k):
		k = str(k)
		return self._db.pop(k)