import os
import ctypes
import struct


class BinaryReader:
	def __init__(self, buf, endian='<'):
		self.buf = buf
		self.endian = endian
		self.seek(0, 2)
		self.size = self.tell()
		self.seek(0)

	def read(self, *args):
		return self.buf.read(*args)

	def seek(self, *args):
		return self.buf.seek(*args)

	def tell(self):
		return self.buf.tell()

	def finished(self):
		return self.tell() == self.size

	def read_string(self, maxlen=0):
		r = []
		start = self.tell()
		while maxlen == 0 or len(r) <= maxlen:
			c = self.read(1)
			if not c:
				raise ValueError('Unterminated string starting at %d' % (start))
			if c == b'\x00':
				break
			r.append(c)
		return b''.join(r)

	def struct_calcsize(self, fmt):
		return struct.calcsize(fmt)

	def read_struct(self, fmt, endian=None):
		fmt = (endian or self.endian) + fmt;
		fmtlen = struct.calcsize(fmt)
		data = self.read(fmtlen)
		if len(data) != fmtlen:
			raise ValueError('Not enough bytes left in buffer to read struct')
		return struct.unpack(fmt, data)

	def read_struct_into(self, dest, fmt, endian=None):
		fields = self.read_struct(fmt, endian=endian)
		fields = list(fields) + [None] * (len(dest._fields) - len(fields))
		return dest._make(fields)

	def read_type(self, type_fmt, endian=None):
		r = self.read_struct(type_fmt, endian=endian)
		if len(r) != 1:
			raise ValueError('Format %r did not describe a single type' % (type_fmt))
		return r[0]


class LibraryNotFoundException(OSError):
    pass


def load_lib(*names):
	for name in names:
		try:
			libname = ctypes.util.find_library(name)
			if libname:
				return ctypes.CDLL(libname)
			else:
				dll_path = os.path.join(os.getcwd(), 'lib%s.dll' % name)
				return ctypes.CDLL(dll_path)
		except OSError:
			pass
	raise LibraryNotFoundException('Could not load the library %r' % (names[0]))
