
from operator import attrgetter
import os

from helper import count, least_bytes

class ByteChunk(object):
	def __init__(self, auto_recognition = None, *args):
		self._data = []

		if auto_recognition == None:
			raise Exception("Argument missing")

		else:
			chunk = auto_recognition
			if   isinstance(chunk, str):
				for char in chunk:
					self._data.append( ord(char) )

			elif (isinstance(chunk, int) or isinstance(chunk, long)) \
			and  chunk >= 0:
				while chunk > 0:
					self._data = [int(least_bytes(chunk, 1))] + self._data
					chunk >>= 8

			elif isinstance(chunk, list) \
			or   isinstance(chunk, tuple):
				if   all( [(isinstance(element, int) or isinstance(element, long)) and (0 <= element < 256) for element in chunk] ):
					self._data = [int(element) for element in chunk]
				elif all( [isinstance(element, ByteChunk) for element in chunk] ):
					self._data += element._data
				else:
					raise TypeError

			elif isinstance(chunk, ByteChunk):
				self._data = chunk._data

			else:
				raise TypeError("Cannot autodetect format!")

	def fill(self, width):
		return ByteChunk( ([0] * max(0, width - len(self))) + self._data )

	def lstrip(self):
		return ByteChunk( str(self).lstrip("\x00") )

	def rstrip(self):
		return ByteChunk( str(self).rstrip("\x00") )

	def strip(self):
		return self.lstrip().rstrip()

	def __len__(self):
		return len(self._data)

	def __iter__(self):
		return iter(self.split(1))

	def split(self, size = 1):
		if not len(self) % size == 0:
			raise Exception
		return tuple([ByteChunk(self._data[pos:pos+size]) for pos in range(0, len(self), size)])

	def __int__(self):
		return sum([val * (256 ** k) for k, val in enumerate(reversed(self._data))])

	def __hex__(self):
		return ( '0x' + '{:0>2X}' * len(self) ).format(*self._data)

	def __str__(self):
		return "".join([chr(byte) for byte in self._data])

	def __repr__(self):
		return hex(self)

	def __eq__(self, other):
		try:
			return self._data == ByteChunk(other)._data
		except TypeError:
			return False

	def __xor__(self, other):
		return ByteChunk(int(self) ^ int(other)).fill(len(self))
	def __rxor__(self, other):
		return ByteChunk(int(self) ^ int(other)).fill(len(self))

	def __ror__(self, other):
		return ByteChunk(int(self) | int(other)).fill(len(self))

	def __rand__(self, other):
		return ByteChunk(int(self) & int(other)).fill(len(self))	


class OUFFile:
	head    = property(attrgetter("_head"))
	table   = property(attrgetter("_table"))
	raw     = property(attrgetter("_fc"))

	offsets = property(attrgetter("_offsets"))

	def __init__(self, fc):
		self._fc = fc

		head_length  = int(ByteChunk(self[0:4]))
		self._head   = ByteChunk(self[0:head_length]).split(4)

		table_length = int(self.head[4]) * 12		
		self._table  = [ByteChunk(self[k:k+12]).split(4) for k in range(head_length, head_length + table_length, 12)]

		self._offsets = (head_length, head_length + table_length)

	def __getitem__(self, i):
		return self.raw[i]

	def __len__(self):
		return len(self.raw)