'''
Destructive Functions for "collapsing" Sequences into single levels

>>> from mcf.utils import collapse

>>> collapse.test([[[1],[2,3]],[[]],[4],5,[6]])

[1, 2, 3, 4, 5, 6] # note that is the same root list

>>> collapse.collapse2([[[1],[2,3]],[[]],(4,()),(5,),[6]])

[1, 2, 3, 4, 5, 6] # note is the same root list
'''
import copy, types, sys
from types import ListType, TupleType # this now only supports the obsolete stuff...

def hyperCollapse( inlist, allowedmap, type=type, list=list, itype=types.InstanceType, maxint= sys.maxint):
	'''
	Destructively flatten a mixed hierarchy to a single level.
	Non-recursive, many speedups and obfuscations by Tim Peters :)
	'''
	try:
		# for every possible index
		for ind in xrange( maxint):
			# while that index currently holds a list
			expandable = 1
			while expandable:
				expandable = 0
				if allowedmap.has_key( type(inlist[ind]) ):
					# expand that list into the index (and subsequent indicies)
					inlist[ind:ind+1] = list( inlist[ind])
					expandable = 1
				
				# alternately you could iterate through checking for isinstance on all possible
				# classes, but that would be very slow
				elif type( inlist[ind] ) is itype and allowedmap.has_key( inlist[ind].__class__ ):
					# here figure out some way to generically expand that doesn't risk
					# infinite loops...
					templist = []
					for x in inlist[ind]:
						templist.append( x)
					inlist[ind:ind+1] = templist
					expandable = 1
	except IndexError:
		pass
	return inlist
	

def collapse(inlist, type=type, ltype=types.ListType, maxint= sys.maxint):
	'''
	Destructively flatten a list hierarchy to a single level. 
	Non-recursive, and (as far as I can see, doesn't have any
	glaring loopholes).
	Further speedups and obfuscations by Tim Peters :)
	'''
	try:
		# for every possible index
		for ind in xrange( maxint):
			# while that index currently holds a list
			while type(inlist[ind]) is ltype:
				# expand that list into the index (and subsequent indicies)
				inlist[ind:ind+1] = inlist[ind]
			#ind = ind+1
	except IndexError:
		pass
	return inlist

def collapse_safe(inlist):
	'''
	As collapse, but works on a copy of the inlist
	'''
	return collapse( inlist[:] )

def collapse2(inlist, ltype=(types.ListType, types.TupleType), type=type, maxint= sys.maxint ):
	'''
	Destructively flatten a list hierarchy to a single level.
	Will expand tuple children as well, but will fail if the
	top level element is not a list.
	Non-recursive, and (as far as I can see, doesn't have any
	glaring loopholes).
	'''
	ind = 0
	try:
		while 1:
			while type(inlist[ind]) in ltype:
				try:
					inlist[ind:ind+1] = inlist[ind]
				except TypeError:
					inlist[ind:ind+1] = list(inlist[ind])
			ind = ind+1
	except IndexError:
		pass
	return inlist

def collapse2_safe(inlist):
	'''
	As collapse2, but works on a copy of the inlist
	'''
	return collapse( list(inlist) )
	
def old_buggy_collapse(inlist):
	'''Always return a one-level list of all the non-list elements in listin,
	rewritten to be non-recursive 96-12-28  Note that the new versions work
	on the original list, not a copy of the original.'''
	if type(inlist)==TupleType:
		inlist = list(inlist)
	elif type(inlist)!=ListType:
		return [inlist]
	x = 0
	while 1:
		try:
			y = inlist[x]
			if type(y) == ListType:
				ylen = len(y)
				if ylen == 1:
					inlist[x] = y[0]
					if type(inlist[x]) == ListType:
						x = x - 1 # need to collapse that list...
				elif ylen == 0:
					del(inlist[x])
					x = x-1 # list has been shortened
				else:
					inlist[x:x+1]=y
			x = x+1
		except IndexError:
			break
	return inlist


def old_buggy_collapse2(inlist):
	'''As collapse, but also collapse tuples, rewritten 96-12-28 to be non-recursive'''
	if type(inlist)==TupleType:
		inlist = list(inlist)
	elif type(inlist)!=ListType:
		return [inlist]
	x = 0
	while 1:
		try:
			y = inlist[x]
			if type(y) in [ListType, TupleType]:
				ylen = len(y)
				if ylen == 1:
					inlist[x] = y[0]
					if type(inlist[x]) in [ListType,TupleType]:
						x = x-1 #(to deal with that element)
				elif ylen == 0:
					del(inlist[x])
					x = x-1 # list has been shortened, will raise exception with tuples...
				else:
					inlist[x:x+1]=list(y)
			x = x+1
		except IndexError:
			break
	return inlist


def oldest_buggy_collapse(listin):
	'Always return a one-level list of all the non-list elements in listin'
	if type(listin) == ListType:
		return reduce(lambda x,y: x+y, map(collapse, listin), [])
	else: return [listin]

def oldest_buggy_collapse2(seqin):

	if type(seqin) in [ListType, TupleType]:
		return reduce(lambda x,y: x+y, map(collapse2, seqin), [])
	else:
		return [seqin]

