=begin rdoc
Bridge: Helper module to enable communication between Rails interface, Plain lemmatizer
and Blender via TCP sockets.

Author:: Stephanie Schuldes (schuldes@cl.uni-heidelberg.de)
Project:: iCookWare
Copyright:: iCookWare Team 2005 (Nicola Kaiser, Ana Kovatcheva, Olga Mordvinova, Stephanie Schuldes)
Embedded Documentation Tool:: rdoc
=end

require 'socket'
require 'icw_blender'
require 'timeout'

module ICW
	class Bridge
	
		# Initialize a new instance of Bridge.
		# +port+:: number of port for communication with server, by default 23000
		def initialize(port=23000)
			begin
				@client = TCPSocket::new('ella.cl.uni-heidelberg.de', port)
				#@client = TCPSocket::new('localhost', port)
			rescue Errno::ECONNREFUSED => e
				$stderr.puts "Caught #{e.class}: #{e}"
				$stderr.puts "Please ensure the server is running (icw_lemmatizer.rb) and try again."
				exit(1)
			end
			
			@rawIngreds = Array.new
			@ingreds = Array.new
			
			@blenderQuery = {}
			
			@fridge = Array.new
			@recs = Array.new
			@clusters = Array.new
		end
		
		# Marshal the Array containing the unprocessed ingredients and send it to server.
		def putRequest
			data = Marshal.dump(@rawIngreds)
			puts "CLIENT: Sending...", @rawIngreds
			@client.puts(data)
		end
		
		# Receive processed data from server and unmarshal it. Ensure the client connection
		# is closed at the end.
		def getProcessedIngreds
			begin
				timeout(15) do
					puts "CLIENT: Receiving data from server..."
					lemmaData = String.new
					begin
						# receive as long as there is still data to read
						while((data = @client.recv(4096)) != "\n")
							puts "data comin"
							lemmaData.concat(data)
						end
					rescue SystemCallError, IOError
						$stderr.print "Read from stream failed: " + $!
					end
					
					@ingreds.clear
					@ingreds = Marshal.load(lemmaData)
					puts @ingreds
				end	
			rescue TimeoutError => e
				$stderr.puts "Caught #{e.class}: #{e}"
				exit(1)
			end
			
			ensure
				puts "CLIENT: Bye!"
				@client.close
		end
		
		# Testing method.
		# +fridge+:: String with ingredients (comma separated)
		def generateRequestFromString(fridge)
			@rawIngreds.clear
			fridge.split(',').each do |ingr|
				@rawIngreds.push ingr.strip
			end
		end
		
		# Generate request from common recipe representation format.
		# +recs+:: recipe data, format: [{title => "", category => [], yields => "", ingreds => [[ing, amount, unit]], data => "rezepttext", keywords=> []},...]
		def generateRequestFromRecs(recs)
			@recs = recs
			@rawIngreds.clear
			@recs.each do | recHash |				# for every recipe hash
				recIngreds = recHash["ingreds"]
				recIngreds.each do | ingred |		# for every ingredient description
					@rawIngreds.push(ingred[0])		# extract ingredient name
				end
			end
		end
		
		# Generate request from clustered ingredients.
		# +clusters+:: Array of ingredient clusters (Array of Array of Strings)
		def generateRequestFromClusters(clusters)
			@clusters = clusters
			@rawIngreds.clear
			@clusters.each do | cluster |
				cluster.each do | ingred |
					@rawIngreds.push(ingred)
				end
			end
		end		
		
		# Call Blender with lemmatized query ingreds and return recipe suggestions to Rails.
		# +recipeFile+:: marshalled recipes
		# +clusterFile+:: marshalled clusters
		def callBlender(recipeFile, clusterFile)
			@blenderQuery['fridge']=@fridge
			@blenderQuery['pers']=4
			puts recipeFile, clusterFile
			myBlender = ICW::Blender.new(@blenderQuery, recipeFile, clusterFile)
			my_recs = myBlender.matchQuery
			puts "********** Empfehlung ***********", my_recs.inspect
			
			# return my_recs to Rails
			return my_recs
			
		end
		
		# Create a new (lemmatized) version of the user query
		def substituteFridgeIngreds
			@rawIngreds.each do | old |
				new = (@ingreds.shift).chomp
				print "new: ", new, "\n"
				if(new == nil)
					# proceed to next ingred
					puts "new is nil, no use for that!"
				else
					print "old: ", old, "\n"
					if((new.length < old.length) && (new[0..3].eql?(old[0..3])))
						if(old.include?('_') || old.include?(' '))	# remove following null element in ingred list (lemmatizer bug)
							print "Uh oh, underscore... ", old, "\n"
							puts "removing nil"
							@ingreds.shift
							print "@ingreds[0]: ", @ingreds[0], "\n"
						end
						@fridge.push(new)
						print "Now we have a better one: ", new, "\n\n"
					else
						print "No use changing -- skip\n\n"
						@fridge.push(old)
					end
				end
			end
			puts @fridge.inspect
		end

		# Create a new version of the recipe database by substituting every raw ingredient
		# in the common recipe representation format by the appropriate lemma and marshal it.
		# +newRecsFilename+:: Name of the file in which the marshalled recipe data will be stored
		def substituteRecIngreds(newRecsFilename)
			@recs.each do | recHash |
				recIngreds = recHash["ingreds"]
				recIngreds.each do | ingred |
					new = (@ingreds.shift).chomp
					print "new: ", new, "\n"
					if(new == nil)
						# proceed to next ingred
						puts "new is nil, no use for that!"
					else
						old = ingred[0]
						print "old: ", old, "\n"
						if((new.length < old.length) && (new[0..3].eql?(old[0..3])))
							if(old.include?('_') || old.include?(' '))	# remove following null element in ingred list (lemmatizer bug)
								print "Uh oh, underscore... ", old, "\n"
								puts "removing nil"
								@ingreds.shift
								print "@ingreds[0]: ", @ingreds[0], "\n"
							end
							ingred.shift			# remove old name of ingred
							ingred.unshift(new)		# insert new name (i.e. lemma)
							print "Now we have a better one: ", ingred[0], "\n\n"
						else
							print "No use changing -- skip\n\n"
						end
					end
				end
			end
			puts @recs.inspect
			save(@recs, newRecsFilename)
		end
		
		# Create a new version of the cluster database by substituting every raw ingredient
		# in the cluster representation by the appropriate lemma, and marshal it.
		# +newClusterFilename+:: Name of the file in which the marshalled clusters will be stored
		def substituteClusterIngreds(newClusterFilename)
			lemmaClusters = Array.new
			puts "lemmaClusters = Array.new"
			@clusters.each do | cluster |
				lemmaCluster = Array.new
				puts "--> new lemmaCluster"
				cluster.each do | ingred |
					new = (@ingreds.shift).chomp
					print "new: ", new, "\n"
					if(new == nil)
						# proceed to next ingred
						puts "new is nil, no use for that!"
					else
						print "old: ", ingred, "\n"
						#charsToCheck = (ingred.length)-3
						#print "charsToCheck = ", charsToCheck, "\n"
						print "new.length: ", new.length, "\n", "ingred.length: ", ingred.length, "\n"
						if((new.length < ingred.length) && (new[0..3].eql?(ingred[0..3])))
							puts "different length"
							if(ingred.include?("_"))	# remove following null element in ingred list (lemmatizer bug)
								print "Uh oh, underscore... ", ingred, "\n"
								print "@ingreds[0]: ", @ingreds[0], "\n"
								puts "removing nil"
								@ingreds.shift
								print "@ingreds[0]: ", @ingreds[0], "\n"
							end
							lemmaCluster.push(new)
							print "Now we have a better one: ", new, "\n\n"
						else
							print "No use changing -- skip\n\n"
							lemmaCluster.push(ingred)
						end
					end
				end
				lemmaClusters.push(lemmaCluster)
			end
			print "--> old clusters: ", @clusters.inspect, "\n"
			print "--> lemmaClusters: ", lemmaClusters.inspect, "\n"
			
			save(lemmaClusters, newClusterFilename)
		end
		
		# Helper method for saving marshalled data
		# +obj+:: Object to be saved
		# +filename+:: where Object will be saved
		def save(obj, filename)
			f = open(filename, 'w')
			Marshal.dump(obj, f)
			f.close
		end

		
	end
end


# --- Case 1: String ---
def	test_string(bridge)
	#f="Rinderhackfleisch,Baguette,Ei,Zwiebel,Knoblauchzehen,Tomaten,Paprika,Majoran,Reis,Gouda,Milch"
	f="Rinderhackfleisch,Baguette,Paprika,Majoran,Reis,Schweinelende,Knoblauchzehe"
	#f="Gorgonzola,Farfalle,Schalotten,Sahne,Spaghetti,Tomaten,Champignons,Parmesan"

	#f="Schweinelende,Tomatenmark,Knoblauchzehe,Paprika,Pfeffer,Majoran,Basilikum"
	#f="Shrimps,Pinienkerne,Chili,Parmesan,Spaghettini,Pfeffer,Basilikum,Knoblauchzehen"
	
	bridge.generateRequestFromString(f)
end

# --- Case 2: Recipes ---
def test_recipes(bridge)
	recFile = File.open("muster_rezepte", "r")
	recData = Marshal.load(recFile)
	bridge.generateRequestFromRecs(recData)
	recFile.close
end

# --- Case 3: Clusters ---
def test_clusters(bridge)
	clusterFile = File.open("test_cluster_marshalled", "r")
	clusters = Marshal.load(clusterFile)
	bridge.generateRequestFromClusters(clusters)
	clusterFile.close
end

#myBridge = ICW::Bridge.new

#test_string(myBridge)
#test_recipes(myBridge)
#test_clusters(myBridge)

#myBridge.putRequest
#myBridge.getProcessedIngreds

#myBridge.substituteFridgeIngreds
#myBridge.substituteRecIngreds("muster_rezepte_lemmatized")
#myBridge.substituteClusterIngreds("test_cluster_lemmatized")

#myBridge.callBlender("./muster_rezepte_lemmatized", "./test_cluster_lemmatized")

