"""section builder

	Provides a set of functions to build a basic MPEG2-TS PSI section.
"""

CRC32 = [
		0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,	0x130476dc, 0x17c56b6b,
		0x1a864db2, 0x1e475005,	0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
		0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,	0x4c11db70, 0x48d0c6c7,
		0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
		0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,	0x791d4014, 0x7ddc5da3,
		0x709f7b7a, 0x745e66cd,	0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
		0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,	0xbe2b5b58, 0xbaea46ef,
		0xb7a96036, 0xb3687d81,	0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
		0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,	0xc7361b4c, 0xc3f706fb,
		0xceb42022, 0xca753d95,	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
		0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,	0x34867077, 0x30476dc0,
		0x3d044b19, 0x39c556ae,	0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
		0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,	0x018aeb13, 0x054bf6a4,
		0x0808d07d, 0x0cc9cdca,	0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
		0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,	0x5e9f46bf, 0x5a5e5b08,
		0x571d7dd1, 0x53dc6066,	0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
		0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,	0xbfa1b04b, 0xbb60adfc,
		0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
		0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,	0xe0b41de7, 0xe4750050,
		0xe9362689, 0xedf73b3e,	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
		0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,	0xd5b88683, 0xd1799b34,
		0xdc3abded, 0xd8fba05a,	0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
		0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,	0x4f040d56, 0x4bc510e1,
		0x46863638, 0x42472b8f,	0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
		0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,	0x36194d42, 0x32d850f5,
		0x3f9b762c, 0x3b5a6b9b,	0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
		0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,	0xf12f560e, 0xf5ee4bb9,
		0xf8ad6d60, 0xfc6c70d7,	0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
		0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,	0xc423cd6a, 0xc0e2d0dd,
		0xcda1f604, 0xc960ebb3,	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
		0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,	0x9b3660c6, 0x9ff77d71,
		0x92b45ba8, 0x9675461f,	0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
		0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,	0x4e8ee645, 0x4a4ffbf2,
		0x470cdd2b, 0x43cdc09c,	0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
		0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,	0x119b4be9, 0x155a565e,
		0x18197087, 0x1cd86d30,	0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
		0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,	0x2497d08d, 0x2056cd3a,
		0x2d15ebe3, 0x29d4f654,	0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
		0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,	0xe3a1cbc1, 0xe760d676,
		0xea23f0af, 0xeee2ed18,	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
		0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,	0x9abc8bd5, 0x9e7d9662,
		0x933eb0bb, 0x97ffad0c,	0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
		0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4]

def create_section_data_block(data_length):
	"""Create a block of data to represend a section
	
	Creates a list of the given size and returns it. The list will have every
	byte set to 0xff. This is because reserved bits are normally set to 1
	Arguments:
		data_length -- amount of bytes needed for the data block
	Returns:
		A list of bytes of the desired length with zero in every byte
	"""
	data = [0xff] * data_length
	return data
	
def set_table_id(data, table_id):
	"""Set the Table ID in a Section
	
	Given a block of section data, sets the Table ID
	Arguments:
		data -- List of bytes. Data to manipulate
		table_id -- the table id to set
	Returns:
		table id that was set
	"""
	data[0] = table_id
	return table_id
	
def set_section_syntax_indicator(data, indicator):
	"""Set the section syntax indicator in a Section
	
	Given a block of section data, sets the section syntax indicator
	Arguments:
		data -- List of bytes. Data to manipulate
		indicator -- Boolean value to set the indicator to
	"""
	data[1] = data[1] & int ('01111111', 2)
	if indicator: data[1] = data[1] | int ('10000000', 2)

def set_private_indicator(data, indicator):
	"""Sets the private section indicator in the given section data
	
	Given a block of section data, sets the private indicator
	Arguments:
		data -- List of bytes. Data to manipulate
		indicator -- Boolean value to set the indicator to
	"""
	data[1] = data[1] & int ('10111111', 2)
	if indicator: data[1] = data[1] | int ('01000000', 2)

def set_section_length(data, section_length):
	"""Sets the section length in the given section data
	
	Given a block of section data, sets the section length
	Arguments:
		data -- List of bytes. Data to manipulate
		section_length -- value to set
	"""
	#clear the bit feilds first
	data[1] = data[1] & int('11110000', 2)
	data[2] = 0x00;
	#now break up the input
	part1 = section_length & 0x0f00
	part1 = part1 >> 8
	part2 = section_length & 0x00ff
	data[1] |= part1
	data[2]  = part2


# Table body 2
def set_table_id_extension(data, table_id_extension):
	"""Sets the table id extension in the given section data
	
	Given a block of section data, sets the table id extension
	Arguments:
		data -- List of bytes. Data to manipulate
		table_id_extension -- value to set
	"""
	data[3] = (table_id_extension & 0xff00) >> 8
	data[4] = table_id_extension & 0x00ff

def set_version_number(data, version):
	"""Sets version number in the given section data
	
	Given a block of section data, sets the version number
	Arguments:
		data -- List of bytes. Data to manipulate
		version -- value to set
	"""
	data[5] &= int('11000001', 2) #clear the bit feilds first
	#version &= int('00011111', 2)
	version <<= 1
	version &= int('00111110', 2)
	data[5] |= version
	
def set_current_next_indicator(data, indicator):
	"""Sets the current/next indicator in the given section data
	
	Given a block of section data, sets the current/next indicator
	Arguments:
		data -- List of bytes. Data to manipulate
		indicator -- Boolean value to set the indicator to
	"""
	data[5] &= int ('11111110', 2)
	if indicator: data[5] |= int ('00000001', 2)

def set_section_number(data, section_number):
	"""Sets the section number in the given section data
	
	Given a block of section data, sets the section number
	Arguments:
		data -- List of bytes. Data to manipulate
		section_number -- value to set
	"""
	data[6] = section_number & 0xff

def set_last_section_number(data, last_section_number):
	"""Sets the last section number in the given section data
	
	Given a block of section data, sets the last section number
	Arguments:
		data -- List of bytes. Data to manipulate
		last_section_number -- value to set
	"""
	data[7] = last_section_number & 0xff
	
def set_data(data, payload, offset):
	"""Sets the data payload in the given section data
	
	Given a block of section data, sets the data payload (including CRC)
	Arguments:
		data -- List of bytes. Data to manipulate
		payload -- payload bytes
		offset -- the byte at which the data should start
	"""	
	j=0
	for i in range(offset, len(data)):
		data[i] = payload[j]
		j+=1

def calculate_crc(data):
	"""Calculate the CRC from a block of section data
	
	Given a block of section data, calculates and returns the CRC
	Arguments:
		data -- List of bytes. Data to manipulate
	Return:
		the CRC
	"""
	polynomial = 0x4c11db7
	crc32 = 0xffffffffL
	for byte in data:
		#byte = ord(byte)
		for bit in range(7,-1,-1):  # MSB to LSB
			z32 = crc32>>31    # top bit
			crc32 = crc32 << 1
			if ((byte>>bit)&1) ^ z32:
				crc32 = crc32 ^ polynomial
			crc32 = crc32 & 0xffffffffL
	crc_array = []
	crc_array.append(int(crc32>>24 & 0x000000ff))
	crc_array.append(int(crc32>>16 & 0x000000ff))
	crc_array.append(int(crc32>>8  & 0x000000ff))
	crc_array.append(int(crc32     & 0x000000ff))
	return crc_array
	
def append_crc(data):
	"""Calculate and append the CRC to a block of section data
	
	Given a block of section data, calculates the CRC and then sets it at the end
	of the data block (last 4 bytes).
	Arguments:
		data -- List of bytes. Data to manipulate
	"""
	crc_array = calculate_crc(data[0:-4])
	data[-4:] = crc_array
	
'''UNIT TESTS -------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------
'''
if __name__ == '__main__':
	import unittest
	import section_parser as sparser
	import random
	print("hello world")

	SAMPLE_CAT = [
	0x01, 0xB0, 0x0F, 0xFF, 0xFF, 0xC1, 0x00, 0x00, 0x09,
	0x04, 0x06, 0x06, 0x05, 0x00, 0x90, 0x64, 0xC6, 0xD0]
	SAMPLE_CAT_NO_CRC = [
	0x01, 0xB0, 0x0F, 0xFF, 0xFF, 0xC1, 0x00, 0x00, 0x09,
	0x04, 0x06, 0x06, 0x05, 0x00, 0xFF, 0xFF, 0xFF, 0xFF]

	class Crc(unittest.TestCase):
		def test_append(self):
			append_crc(SAMPLE_CAT_NO_CRC)
			self.assertEqual(SAMPLE_CAT_NO_CRC, SAMPLE_CAT)
		def test_calculate(self):
			self.assertEqual(calculate_crc(SAMPLE_CAT_NO_CRC[0:-4]), SAMPLE_CAT[-4:])
	
	class All(unittest.TestCase):
		def test(self):