from gluon.dal import Table, Field

class OrderedList(Table):
	def __init__(self,db,tablename,*fields,**args):
		fields = fields + (
			Field('line', 'integer', default=0),
		)
		self.db = db
		self.tablename = tablename
		super(OrderedList, self).__init__(db,tablename,*fields,**args)
	'''
	Override some base methods
	'''
	def delete(self, key):
		row = self._get_row(key)
		self.db(self.id==row.id).delete()
		self.db(self.line>row.line).update(line=self.line-1)
	
	def __delitem__(self, key):
		self.delete(key)

	def insert(self, **fields):
		#fields['line'] = self.line.max()+1 #will work?
		_id = super(OrderedTree, self).insert(**fields)
		self[_id].update_record(line=self.db(self).count()+1)
		return _id
		
	'''
	Move methods
	'''        
	def move_before(self, id1, id2):
		'''
		move id1 before id2
		'''
		r1, r2 = self._get_row(id1), self._get_row(id2)
		if r1.line<r2.line:
			a, b = int(r1.line), int(r2.line)-1
			delta = b-a
		else:
			a, b, delta = r2.line, r1.line, 1
		self._rotate_lines(a, b, delta)
	
	def move_after(self, id1, id2):
		'''
		move id1 after id2
		'''
		r1, r2 = self._get_row(id1), self._get_row(id2)
		if r1.line<r2.line:
			a, b = int(r1.line), int(r2.line)
			delta = b-a
		else:
			a, b, delta = int(r2.line)+1, int(r1.line), 1
		self._rotate_lines(a, b, delta)
		
	def _rotate_lines(self, a, b, delta):
		a1, b1 = min(a,b), max(a, b)
		if delta<b1-a1+1:
			delta1 = delta
		else:
			delta1 = b1-a1+1-delta
		query = "UPDATE %s SET line = %s + (line + %s) %% %s WHERE line >= %s AND line <= %s" 
		place_holders = (self.tablename, a1, -a1+delta1, b1-a1+1, a1, b1)
		q = query % place_holders 
		#raise Exception(q, a, b, delta)
		self.db.executesql( q )
		
	'''
	Utils
	'''
	def _get_row(self, key):
		try:
			_id = key.id
			_row = key
		except:
			_row = self[key]
		return _row
		
	def _get_id(self, key):
		try:
			_id = key.id
		except:
			_id = key
		return _id


class OrderedTree(OrderedList):
	def __init__(self,db,tablename,*fields,**args):
		fields = fields + (
			#Field('line', 'integer', default=0), #'line' will be added by OrderedList
			Field('level', 'integer', default=0),
			Field('num_children', 'integer', default=0),
			Field('parent_id', 'reference ' + tablename, default=0),
		)
		super(OrderedTree, self).__init__(db,tablename,*fields,**args)
	'''
	Override some base methods
	'''
	def delete(self, key):
		row = self._get_row(key)
		_count = row.num_children + 1
		if row.parent_id:
			self.db(self.id==row.parent_id).update(num_children=self.num_children-_count)
		self.db((self.line>=row.line)&(self.line<=row.line+row.num_children)).delete()
		self.db(self.line>row.line).update(line=self.line-_count)
	
	def __delitem__(self, key):
		self.delete(key)

	def insert(self, **fields):
		#fields['line'] = self.line.max()+1 #will work?
		_id = super(OrderedTree, self).insert(**fields)
		self[_id].update_record(line=self.db(self).count()+1)
		return _id
		
	'''
	Move methods
	'''        
	def move_before(self, id1, id2):
		'''
		move id1 before id2
		'''
		r1, r2 = self._get_row(id1), self._get_row(id2)
		if r1.parent_id != r2.parent_id:
			#change num_children and level 
			#only if there are different parents
			if r1.parent_id:
				p = self[r1.parent_id]
				p.update_record(num_children=p.num_children-(r1.num_children+1)) 
			if r2.parent_id:
				p = self[r2.parent_id]
				p.update_record(num_children=p.num_children+r1.num_children+1)    
			r1.update_record(parent_id = r2.parent_id)
			#raise Exception("works")
			self.db((self.line>=r1.line)&(self.line<=r1.line+r1.num_children)).update(level=self.level+r2.level-r1.level)    		
		if r1.line<r2.line:
			a, b = int(r1.line), int(r2.line)-1
			delta = (b-a+1)-(r1.num_children+1)
		else:
			a, b, delta = r2.line, r1.line+r1.num_children, r1.num_children+1
		self._rotate_lines(a, b, delta) 
	
	def move_after(self, id1, id2):
		'''
		move id1 after id2
		'''
		r1, r2 = self._get_row(id1), self._get_row(id2)
		if r1.parent_id != r2.parent_id:
			if r1.parent_id:
				p = self[r1.parent_id]
				p.update_record(num_children=p.num_children-(r1.num_children+1)) 
			if r2.parent_id:
				p = self[r2.parent_id]
				p.update_record(num_children=p.num_children+r1.num_children+1)  
			r1.update_record(parent_id = r2.parent_id)  
			self.db((self.line>=r1.line)&(self.line<=r1.line+r1.num_children)).update(level=self.level+r2.level-r1.level)
		if r1.line<r2.line:
			a, b = r1.line, r2.line+r2.num_children
			delta = (b-a+1)-(r1.num_children+1)
		else:
			a, b, delta = r2.line+r2.num_children+1, r1.line+r1.num_children, r1.num_children+1
		self._rotate_lines(a, b, delta)
		     
	def move_inside(self, id1, id2):
		'''
		move id1 inside id2 (as last children)
		'''
		r1, r2 = self._get_row(id1), self._get_row(id2)
		if r1.parent_id:
			p = self[r1.parent_id]
			p.update_record(num_children=p.num_children-(r1.num_children+1))    
		self.db((self.line>=r1.line)&(self.line<=r1.line+r1.num_children)).update(level=self.level+r2.level-r1.level+1)
		if r1.line<r2.line: 
			a, b = r1.line, r2.line+r2.num_children
			delta = (b-a+1)-(r1.num_children+1)
		else:
			a, b, delta = r2.line+r2.num_children+1, r1.line+r1.num_children, r1.num_children+1
		self._rotate_lines(a, b, delta)
		#r2 is the parent
		r2.update_record(num_children=r2.num_children+r1.num_children+1)  
		r1.update_record(parent_id = r2.id)
		
	'''
	Retrieve methods
	''' 
	def children(self, key):
		pass
		
	def parents(self, key):
		pass
		
	def siblings(self, key):
		pass
		