#!/usr/bin/env python

import os
import sys
import time
import random
import tarfile

try:
	import StringIO as stringio
except ImportError:
	import io as stringio

class No_tell_file:
	'''
	This is somewhat file-like, but we make tell always report 0L and seek raise an exception, because we don't need tarfile to seek or tell,
	but it's insisting on tell'ing anyway.
	'''
	def __init__(self, file_):
		self.file_ = file_

	def seek(self, *args, **kwargs):
		raise NotImplementedError('No_tell_file class does not support seeking')

	def tell(self, *args, **kwargs):
		return 0

	def read(self, *args, **kwargs):
		self.file_.read(*args, **kwargs)

	def write(self, *args, **kwargs):
		self.file_.write(*args, **kwargs)

class Dynamically_generated_file:
	chars = '0123456789abcdef'

	def __init__(self):
		self.length = int(random.random() * 2**20)
		sys.stderr.write('Intending a length of %d\n' % self.length)
		self.position = 0
		self.currently_have = ''
		self.charno = 0

	def read(self, length_desired):
		# inhale
		while len(self.currently_have) < length_desired:
			chunk_length = int(random.random() * 2**17)
			if self.position + chunk_length > self.length:
				chunk_length = self.length - self.position
			if chunk_length == 0:
				# we've inhaled all we're gonna (supposed to) get already
				break
			self.currently_have += chunk_length * self.chars[self.charno]
			self.charno = (self.charno + 1) % len(self.chars)
		# advance the "file" pointer and dole out
		result = self.currently_have[:length_desired]
		self.currently_have = self.currently_have[length_desired:]
		return result

class rawio:
	# this class is a simple wrapper for os.open, os.read, os.write and os.close, that should in turn allow us to wrap
	# these os.* routines with bufsock.  Alternatively, we should also be able to wrap a python file object with bufsock,
	# but then you end up with two layers of buffering, each with slightly different functionality
	def __init__(self, filename=None, mode=7*64 + 7*8 + 7, perms = 6*64 + 6*8 + 6, handle=None):
		assert (filename != None) + (handle != None) == 1
		self.perms = perms
		if filename:
			self.filename = filename
			if mode == 'r':
				self.mode = os.O_RDONLY
			elif mode == 'w':
				self.mode = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
			elif mode == 'rw':
				self.mode = os.O_RDRW | os.O_CREAT
			else:
				raise ValueError('Invalid mode: %s' % mode)
			self.file_descriptor = os.open(filename, self.mode, perms)
		elif handle:
			self.file_descriptor = handle

	open = __init__

	def read(self, length):
		return os.read(self.file_descriptor, length)

	def write(self, data):
		if hasattr(data, 'encode'):
			return os.write(self.file_descriptor, data.encode('latin-1'))
		else:
			return os.write(self.file_descriptor, data)

	def close(self):
		os.close(self.file_descriptor)

	def __enter__(self):
		return self

	def __exit__(self, *_unused):
		os.close(self.file_descriptor)



string_file_1 = "stringio first.txt", 8, stringio.StringIO("one one\n")
string_file_2 = "stringio second.txt", 4, stringio.StringIO("two\n")
string_file_3 = "stringio third.txt", 2**16, stringio.StringIO('\0' * 2**16)
dyn_file_1    = "dynamic  first.txt", None, Dynamically_generated_file()

tar = tarfile.open(fileobj=No_tell_file(rawio(handle = 1)), mode="w")

mtime = time.time()
for name, length, file_ in [ string_file_1, string_file_2, string_file_3, dyn_file_1 ]:
	tarinfo = tarfile.TarInfo(name)
	if length == None:
		tarinfo.size = file_.length
	else:
		tarinfo.size = length
	tarinfo.mtime = mtime
	# add more attributes as needed
	tar.addfile(tarinfo, file_)

tar.close()