class SQLiteFreelistPage(SQLitePage):
	def __init__(self, pgn, parent, pageBytes, trunk=0):
		super(SQLiteFreelistPage, self).__init__(pgn, parent, pageBytes)
		self.isSubType = None
		self.isTrunk = trunk
		self.unallocated = ListRange( )
		if trunk:
			self.type = 'Freelist Trunk'
			(self.nextTrunkPage, self.leafpageCount) =  unpack( '>II', self.stream[0:8] )
			self.leafPages = []
			offset = 8
			for i in range(0, self.leafpageCount):
				self.leafPages.append( unpack( '>I', self.stream[offset:offset+4] )[0] )
				offset += 4
			self.unallocated.addRange( [offset, parent.pageSize] );
		else:
			#if isBTreePage( unpack(">B", pageBytes[0:1] )[0]):
			self.isSubType = SQLiteBTreePage(pgn, self.BTree, pageBytes)
			self.type = 'Freelist Leaf'
			self.unallocated.addRange( [1, parent.pageSize] );
			
	def __dump__(self):
		if self.isTrunk:
			print "\tNext freelist trunk page:", self.nextTrunkPage
			print "\tLeaf pages count:", self.leafpageCount
			print "\tLeaf pages: " + ",".join(["%d" % i for i in  self.leafPages ]) 
		if self.isSubType:
			print "\tSubtype:", self.isSubType.type
			self.isSubType.__dump__( )
	def __init__(self, pgn, parent, pageBytes, root=0):
		super(SQLiteBTreePage, self).__init__(pgn, parent, pageBytes)
		of = 0
		self.isFirstPage = root
		if self.isFirstPage:
			of += 100
		self.Flag = unpack(">B", self.stream[of:of+1])[0]
		of += 1
		if not isBTreePage (self.Flag):
			return
		self.FirstBlock = 0
		self.numnumCells = 0
		self.ContentArea = 0
		self.Fragment = 0
		self.avgCellSize = 0
		self.avgPayloadLen = 0
		self.unallocated = ListRange( )
		self.Pointer = 0
		(self.FirstBlock, self.numCells, self.ContentArea, self.Fragment) = unpack(">3HB", self.stream[of:of+7])
		self.maxLocal = self.BTree.maxLocal
		self.minLocal = self.BTree.minLocal
		of += 7
		if self.Flag & 0x8:
			self.strType = "Leaf" 
		else:
			self.strType = "Interior"
			self.Pointer = unpack(">I", pageBytes[of:of+4])[0]
			of += 4
		if self.Flag & 0x7 == 2:
			self.strType += " Index";
		elif self.Flag & 0x7 == 5:
			self.strType += " Table";
			self.maxLocal = self.BTree.maxLeaf
			self.minLocal = self.BTree.minLeaf
		if self.strType != "Unknown":
			self.type = "BTree"
		if self.numCells:
			self.cells = []
			self.cellGap.addRange( [self.ContentArea, self.BTree.usableSize] )
			avgSize = 0
			avgPayload = 0
			for i in range(0, self.numCells):
				co = unpack( ">H", self.stream[of:of+2] )[0]
				cell = self.readCellHeaderOff(co, i)
				self.cells.append( cell )
				self.cellGap.delRange( [cell['offset'], cell['offset']+cell['cellSize']] )
				avgSize += cell['cellSize']
				if 'payloadLen' in cell:
					avgPayload += cell['payloadLen']
				of += 2
			for r in self.cellGap:
				self.unallocated.addRange( r )
			self.avgCellSize = int(avgSize / self.numCells)
			self.avgPayloadLen = int(avgPayload / self.numCells)
		if self.ContentArea == self.BTree.usableSize:
			self.unallocated.addRange( [of, self.BTree.usableSize] )
		else:
			self.unallocated.addRange( [of, self.ContentArea-1] )
	def __init__(self, pgn, parent, pageBytes, trunk=0):
		super(SQLiteFreelistPage, self).__init__(pgn, parent, pageBytes)
		self.isSubType = None
		self.isTrunk = trunk
		self.unallocated = ListRange( )
		if trunk:
			self.type = 'Freelist Trunk'
			(self.nextTrunkPage, self.leafpageCount) =  unpack( '>II', self.stream[0:8] )
			self.leafPages = []
			offset = 8
			for i in range(0, self.leafpageCount):
				self.leafPages.append( unpack( '>I', self.stream[offset:offset+4] )[0] )
				offset += 4
			self.unallocated.addRange( [offset, parent.pageSize] );
		else:
			#if isBTreePage( unpack(">B", pageBytes[0:1] )[0]):
			self.isSubType = SQLiteBTreePage(pgn, self.BTree, pageBytes)
			self.type = 'Freelist Leaf'
			self.unallocated.addRange( [1, parent.pageSize] );
class SQLiteBTreePage(SQLitePage):
	def __init__(self, pgn, parent, pageBytes, root=0):
		super(SQLiteBTreePage, self).__init__(pgn, parent, pageBytes)
		of = 0
		self.isFirstPage = root
		if self.isFirstPage:
			of += 100
		self.Flag = unpack(">B", self.stream[of:of+1])[0]
		of += 1
		if not isBTreePage (self.Flag):
			return
		self.FirstBlock = 0
		self.numnumCells = 0
		self.ContentArea = 0
		self.Fragment = 0
		self.avgCellSize = 0
		self.avgPayloadLen = 0
		self.unallocated = ListRange( )
		self.Pointer = 0
		(self.FirstBlock, self.numCells, self.ContentArea, self.Fragment) = unpack(">3HB", self.stream[of:of+7])
		self.maxLocal = self.BTree.maxLocal
		self.minLocal = self.BTree.minLocal
		of += 7
		if self.Flag & 0x8:
			self.strType = "Leaf" 
		else:
			self.strType = "Interior"
			self.Pointer = unpack(">I", pageBytes[of:of+4])[0]
			of += 4
		if self.Flag & 0x7 == 2:
			self.strType += " Index";
		elif self.Flag & 0x7 == 5:
			self.strType += " Table";
			self.maxLocal = self.BTree.maxLeaf
			self.minLocal = self.BTree.minLeaf
		if self.strType != "Unknown":
			self.type = "BTree"
		if self.numCells:
			self.cells = []
			self.cellGap.addRange( [self.ContentArea, self.BTree.usableSize] )
			avgSize = 0
			avgPayload = 0
			for i in range(0, self.numCells):
				co = unpack( ">H", self.stream[of:of+2] )[0]
				cell = self.readCellHeaderOff(co, i)
				self.cells.append( cell )
				self.cellGap.delRange( [cell['offset'], cell['offset']+cell['cellSize']] )
				avgSize += cell['cellSize']
				if 'payloadLen' in cell:
					avgPayload += cell['payloadLen']
				of += 2
			for r in self.cellGap:
				self.unallocated.addRange( r )
			self.avgCellSize = int(avgSize / self.numCells)
			self.avgPayloadLen = int(avgPayload / self.numCells)
		if self.ContentArea == self.BTree.usableSize:
			self.unallocated.addRange( [of, self.BTree.usableSize] )
		else:
			self.unallocated.addRange( [of, self.ContentArea-1] )
	
	def __dump__(self):
		if self.isFirstPage:
			print "\tFirst Page (with file header)"
		print "\tBTree Page Type", self.Flag, "(" + self.strType + ")"
		print "\tByte offset into First Freeblock:", self.FirstBlock
		print "\tNumber of cells:", self.numCells
		print "\tAvg. cell size:", self.avgCellSize
		if self.avgPayloadLen:
			print "\tAvg. payload size:", self.avgPayloadLen
		print "\tOffset to the first byte of content area:", self.ContentArea
		print "\tFragment:", self.Fragment
		if self.numCells:
			print "\tCell ptrs:", ",".join([ str(i['offset']) for i in self.cells])
		if not(self.Flag & 0x8):
			print "\tThe right-most pointer:", self.Pointer
		print "\tUnallocated space:", ",".join([str(i) for i in self.unallocated])
		print "\tCell gaps:", ",".join([str(i) for i in self.cellGap])