import openmedialib
import openpluginlib
import balance
import common

def assign_prop( property, value ):
	if property is not None and property.valid( ):
		if property.is_a_int( ):
			property.set( int( value ) )
		elif property.is_a_double( ):
			property.set( float( value ) )
		elif property.is_a_wstring( ):
			property.set( str( value ) )
		elif property.is_a_wstring_list( ):
			property.set( value )
	else:
		print "invalid ", property, value

def value_prop( prop ):
	result = None
	if prop is not None and prop.valid( ):
		if prop.is_a_int( ):
			result = str( prop.value_as_int( ) )
		elif prop.is_a_double( ):
			result = str( prop.value_as_double( ) )
		elif prop.is_a_wstring( ):
			result = unicode( prop.value_as_wstring( ) )
		elif prop.is_a_wstring_list( ):
			result = prop.value_as_wstring_list( )
	return result

def find_prop( node, key ):
	if key[ 0 ] == "@":
		split = key[ 1: ].split( "." )
		name = split.pop( )
		for i in split:
			node = node.fetch_slot( int( i ) )
			if node is None: return None
	else:
		name = key
	return node.properties( ).get_property( name )

def update_prop( node, name, value ):
	if node is not None:
		prop = node.properties( ).get_property( name )
		if prop is None or not prop.valid( ):
			prop = openpluginlib.property( openpluginlib.create_key_from_string( name ) )
			node.properties( ).append( prop )
		prop.set( value )

def retrieve_uri( node ):
	uri = None
	if node is not None:
		prop = find_prop( node, "rpn_uri" )
		if prop is not None and prop.valid( ):
			uri = value_prop( prop )
		else:
			uri = node.get_uri( )
	return uri

class operator:
	def __init__( self, uri, input = None, callback = None ):
		self.uri = uri
		self.properties = []
		self.input = input
		self.callback = callback

	def __str__( self ):
		if len( self.properties ):
			return self.uri + " " + str( self.properties )
		else:
			return self.uri

	def __int__( self ):
		return int( self.uri )

	def assign( self, node ):
		for i in self.properties:
			split = i.split( "=" )
			if split[ 0 ][ 0:2 ] == "@@" or split[ 1 ][ 0:2 ] == "@@":
				update_prop( node, split[ 0 ], str( split[ 1 ] ) )
			else:
				property = find_prop( node, split[ 0 ] )
				if split[ 1 ][ 0 ] == "@":
					temp = find_prop( node, split[ 1 ] )
					value = value_prop( temp )
				else:
					value = split[ 1 ]
				if property is not None:
					assign_prop( property, value )

class input_operator( openmedialib.input_delegate ):
	def __init__( self, uri, operator ):
		openmedialib.input_delegate.__init__( self )
		self.operator = operator
		self.uri = uri
		for prop in operator.properties:
			split = prop.split( "=" )
			update_prop( self, split[ 0 ], split[ 1 ] )

	def __str__( self ):
		return str( self.uri )

	def __int__( self ):
		return int( self.uri )

	def reset( self ): 
		pass

	def get_uri( self ): 
		return self.uri

	def get_mime_type( self ):
		return unicode( "" )

	def get_frames( self ):
		return 0

	def is_seekable( self ):
		return False

	def get_video_streams( self ):
		return 0

	def get_audio_streams( self ):
		return 0

	def set_video_stream( self, index ):
		return False

	def set_audio_stream( self, index ):
		return False

	def seek( self, position, relative = False ):
		pass

	def get_position( self ):
		return 0

	def fetch( self ):
		return None

	def push( self, frame ):
		return False

	def refresh_cache( self, frame ):
		pass

	def reuse( self ):
		return False

class stack:
	def __init__( self ):
		self.objects = { }
		self.stack = [ ]
		self.dict = { }
		self.dict[ "clear" ] = self.clear
		self.dict[ "swap" ] = self.swap
		self.dict[ "2swap" ] = self.swap2
		self.dict[ "rot" ] = self.rot
		self.dict[ "drop" ] = self.drop
		self.dict[ "2drop" ] = self.drop2
		self.dict[ "dup" ] = self.dup
		self.dict[ "debug" ] = self.debug
		self.dict[ "decap" ] = self.decap
		self.dict[ "clone" ] = self.clone
		self.dict[ "over" ] = self.over
		self.dict[ "render" ] = self.render
		self.dict[ "nip" ] = self.nip
		self.dict[ "tuck" ] = self.tuck
		self.dict[ "!" ] = self.assign
		self.dict[ "op!" ] = self.assign_op
		self.dict[ "decap!" ] = self.assign_decap
		self.dict[ ":" ] = self.start
		self.dict[ ";" ] = self.end
		self.dict[ "play" ] = self.play
		self.dict[ "store" ] = self.store
		self.dict[ "store!" ] = self.assign_store
		self.dict[ "stack" ] = self.show_stack
		self.dict[ "vars" ] = self.vars
		self.dict[ "functions" ] = self.show_functions
		self.dict[ "+" ] = self.add
		self.dict[ "-" ] = self.sub
		self.dict[ "*" ] = self.mult
		self.dict[ "/" ] = self.div
		self.functions = { }
		self.defining = False
		self.definition = [ ]
		self.locals = [ { } ]

	def withdraw( self, op ):
		result = None
		if op.input is None:
			uri = op.uri
			if not self.objects.has_key( uri ):
				self.objects[ uri ] = [ ]
			instances = self.objects[ uri ]
			if len( instances ) > 0:
				result = instances.pop( )
			elif uri.find( "op:" ) == 0:
				result = input_operator( unicode( uri[ 3: ] ), op )
			elif uri == "filter:wrapper":
				result = common.input_wrapper( )
			elif uri.find( "filter:" ) == 0:
				result = openmedialib.create_filter( str( uri[ 7: ] ) )
			else:
				result = openmedialib.create_input( str( uri ) )
			update_prop( result, "rpn_uri", str( uri ) )
		else:
			result = op.input
		return result

	def deposit( self, input ):
		if input is not None:
			i = 0
			while i < input.slots( ):
				self.deposit( input.fetch_slot( i ) )
				input.connect( None, i )
				i += 1
			if input.reuse( ):
				uri = retrieve_uri( input )
				update_prop( input, "rpn_uri", str( uri ) )
				self.objects[ uri ].append( input )

	def clear( self ):
		tos = self.pop( )
		while tos is not None:
			self.deposit( tos )
			tos = self.pop( )

	def swap( self ):
		tos = self.pop( )
		bos = self.pop( )
		self.push_input( tos )
		self.push_input( bos )

	def swap2( self ):
		i1 = self.pop( )
		i2 = self.pop( )
		i3 = self.pop( )
		i4 = self.pop( )
		self.push_input( i2 )
		self.push_input( i1 )
		self.push_input( i4 )
		self.push_input( i3 )

	def rot( self ):
		tos = self.pop( )
		bos = self.pop( )
		uos = self.pop( )
		self.push_input( bos )
		self.push_input( tos )
		self.push_input( uos )

	def dup( self ):
		tos = self.pop( )
		self.push_input( tos )
		self.push_input( tos )

	def drop( self ):
		tos = self.pop( )
		self.deposit( tos )

	def drop2( self ):
		self.drop( )
		self.drop( )

	def decap( self ):
		tos = self.pop( )
		if tos is not None:
			i = 0
			while i < tos.slots( ):
				self.push_input( tos.fetch_slot( i ) )
				tos.connect( None, i )
				i += 1
			self.deposit( tos )
		return tos

	def clone_node( self, node ):
		if node is not None:
			i = 0
			while i < node.slots( ):
				self.clone_node( node.fetch_slot( i ) )
				i += 1
			self.push( retrieve_uri( node ) )
			self.push_args( value_prop( find_prop( node, "rpn_props" ) ) )
			self.push_callback( node.fetch_callback( ) )

	def clone( self ):
		tos = self.pop( )
		self.push_input( tos )
		self.clone_node( tos )

	def over( self ):
		tos = self.pop( )
		bos = self.pop( )
		self.push_input( bos )
		self.push_input( tos )
		self.clone_node( bos )

	def nip( self ):
		self.swap( )
		self.drop( )

	def tuck( self ):
		self.clone( )
		tos = self.pop( )
		self.swap( )
		self.push_input( tos )

	def assign( self ):
		op = self.stack.pop( )
		tos = self.pop( )
		self.locals[ -1 ][ op.uri ] = tos

	def assign_op( self ):
		op = self.stack.pop( )
		tos = self.stack.pop( )
		self.locals[ -1 ][ op.uri ] = input_operator( tos, operator( tos ) )

	def assign_decap( self ):
		op = self.stack.pop( )
		tos = self.decap( )
		self.locals[ -1 ][ op.uri ] = tos

	def start( self ):
		self.defining = True

	def end( self ):
		if self.defining == True:
			name = self.definition.pop( 0 )
			self.functions[ name ] = [ ] + self.definition
			self.definition = [ ]
			self.defining = False
		else:
			self.debug( "Syntax error: function terminator out of place" )

	def play( self ):
		input = self.pop( )
		if input is not None:
			common.play( input )
			self.deposit( input )

	def store( self ):
		op = self.pop( )
		tos = self.pop( )
		common.play( tos, op.uri, op.operator.properties )
		self.deposit( tos )

	def assign_store( self ):
		var = self.stack.pop( )
		op = self.pop( )
		tos = self.pop( )
		common.play( tos, op.uri, op.operator.properties )
		self.deposit( tos )
		self.push( op.uri )
		self.stack.append( var )
		self.assign( )

	def add( self ):
		val1 = self.pop( )
		val0 = self.pop( )
		self.push( str( int( val0 ) + int( val1 ) ) )

	def sub( self ):
		val1 = self.pop( )
		val0 = self.pop( )
		self.push( str( int( val0 ) - int( val1 ) ) )

	def mult( self ):
		val1 = self.pop( )
		val0 = self.pop( )
		self.push( str( int( val0 ) * int( val1 ) ) )

	def div( self ):
		val1 = self.pop( )
		val0 = self.pop( )
		self.push( str( int( val0 ) / int( val1 ) ) )

	def push( self, id ):
		result = True
		if self.defining == True and id != ";":
			self.definition.append( id )
		elif id.endswith( ".oml" ):
			self.push_file( id )
		elif id in self.functions.keys( ):
			self.locals.append( { } )
			self.push_args( self.functions[ id ] )
			self.locals.pop( -1 )
		elif id in self.dict.keys( ):
			self.dict[ id ]( )
		elif id in self.locals[ -1 ].keys( ):
			self.push_input( self.locals[ -1 ][ id ] )
		elif id.find( "/sequence:" ) > 0:
			self.stack.append( operator( id ) )
		else:
			if id.find( "=" ) > 0:
				if id.find( "=op:" ) > 0:
					split = id.split( "=op:" )
					split[ 1 ] = self.locals[ -1 ][ split[ 1 ] ].uri
					id = split[ 0 ] + "=" + str( split[ 1 ] )
				self.stack[ -1 ].properties.append( id )
			elif id != "":
				self.stack.append( operator( id ) )
			else:
				result = False
		return result

	def push_file( self, file ):
		f = open( file, "r" )
		if f is not None:
			for line in f:
				self.push_args( line.strip( ).split( " " ) )
			f.close( )

	def push_callback( self, callback ):
		result = True
		if len( self.stack ) > 0:
			self.stack[ -1 ].callback = callback
		else:
			result = False
		return result

	def push_args( self, properties ):
		result = True
		if properties is not None:
			for i in properties: result |= self.push( i )
		return result

	def push_input( self, input ):
		id = retrieve_uri( input )
		self.stack.append( operator( id, input ) )
		self.push_args( value_prop( find_prop( input, "rpn_props" ) ) )
		self.push_callback( input.fetch_callback( ) )
		return True

	def pop( self ):
		result = None
		if len( self.stack ) > 0:
			op = self.stack.pop( )
			result = self.withdraw( op )
			if result is not None:
				op.assign( result )
				slot = result.slots( )
				while slot > 0:
					slot -= 1
					if result.fetch_slot( slot ) is not None:
						self.push_input( result.fetch_slot( slot ) )
					result.connect( self.pop( ), slot )
				op.assign( result )
				update_prop( result, "rpn_length", result.get_frames( ) )
				update_prop( result, "rpn_props", [] + op.properties )
				result.register( op.callback )
			else:
				result = input_operator( op.uri, op )
		return result

	def empty( self ):
		return len( self.stack ) == 0

	def debug( self, title = "stack trace" ):
		print title
		for i in self.stack:
			print str( i )
		print

	def render( self, title = "top of stack" ):
		print title
		tos = self.pop( )
		if tos is not None:
			balance.walk( tos )
			self.push_input( tos )
		else:
			print "<empty>"

	def show_stack( self, title = "current stack" ):
		print title
		tos = self.pop( )
		if tos is not None:
			balance.stack( tos )
			self.push_input( tos )
			print
		else:
			print "<empty>"

	def vars( self, title = "variables" ):
		print title
		for frame in self.locals:
			for i in frame.keys( ):
				print i + ": " + str( frame[ i ] )
			print

	def show_functions( self, title = "functions" ):
		print title
		for key in self.functions.keys( ):
			print ":", key
			for word in self.functions[ key ]:
				print word,
			print
			print ";"
			print
