import pymatrices, pycomparator, cbctools, os

do_print_cluto = True

class CLUTO:
	def __init__(self, pathToCLUTO):
		self.path_to_cluto = pathToCLUTO
		self.temp_path = os.path.join(os.path.curdir, "tmp")
		self.temp_matrix_name = "cluto_output.mat"
		self.clmethod = "agglo"
		self.sim = "cos"
		self.crfun="upgma"
		self.treefilename = "./tmp/matrix.tree"
		self.no_of_groups = 10
		self.similarity_treshold = 0.2
	
	def run_cluto(self, rows, matrix):
		# rows -> ids or labels
		self.rows = rows
		self.matrix = matrix
		self.cmp = pycomparator.PreliminaryCosineComparision(matrix, False, 0.1, 1, False)
		# write rows to temp matrix
		print "Writing rows"
		self.__write_rows()
		# execute CLUTO
		print "Cluto"
		self.__execute_cluto(len(self.rows))
		# read CLUTO output
		print "Parsing output"
		self.__read_cluto_output()
		# select groups from CLUTO output
		print "Selecting best cluster"
		best = self.select_groups()

		# free mem:
		#self.__free_mem()

		return best

	def __execute_cluto(self, no_of_rows):

		cluto_exe = os.path.join(self.path_to_cluto, "vcluster")

		cmd_str = "%(cluto_exe)s -clmethod=%(clmethod)s -sim=%(sim)s "\
				"-crfun=%(crfun)s -treefile=%(treefile)s %(matrixname)s %(noofgroups)s " \
				% {"cluto_exe" : cluto_exe, "clmethod" : self.clmethod,
						"sim": self.sim, "crfun": self.crfun,
						"matrixname": os.path.join(self.temp_path, self.temp_matrix_name),
						"treefile": self.treefilename, "noofgroups":self.no_of_groups}
		os.system(cmd_str)

	def select_groups(self):
		out = []
		avg = 0.0

		roots = self.index[-1]
		for n in roots:
			out.extend(self.select_groups_inner(n) )

		self.groups  = out

		if do_print_cluto:
			for z,sim in self.groups:
				if self.index.has_key(z) == False and self.parray[z] != -1:
					print  self.rows[z] 
				else:
					all = self.__get_children(z)
					l = []
					for x in all:
						l.append( self.rows[x]  )
					print ("%s (%s):"%(z,sim)),
					for x in l: print x,
					print

		best = -1
		best_sim = 0
		for z,sim in self.groups:
			if sim > best_sim and sim != 1:
				best_sim = sim
				best = z

		print "Best: %s, |c|avgsim(c) = %s" %(best, best_sim)

#		if best_sim < 1:
#			if do_print_cluto:
#				print "Best sim < 1, using most similar"

#			return ([0],1)
#		else:

		c = self.__get_children(best)
		if len(c) > 0:
			return self.__get_children(best),best_sim
		else:
			print "We do not use leafs."
			return None


	def select_groups_inner(self, root):
		out = []
		avg = 0.0
		if self.index.has_key(root) == True:
			cluster = self.__get_children(root)
			sim = self.avgsim(cluster)
		#	if sim >= self.similarity_treshold:
		#		out = root
		#		avg = sim * len(cluster)
		#	else:
			l = self.index[root]
			left = l[0]
			right = l[1]
			a = self.select_groups_inner(left)
			b = self.select_groups_inner(right)
			out.extend( [ (root, sim*len(cluster) ) ] )
			print "%s=%s, len = %s" % (root, sim, len(cluster) )
			out.extend(a)
			out.extend(b)
			return out
		else:
			out = root
			avg = 0

		return [(out,avg)]

	def avgsim(self, cluster):
		sim = 0.0
		n = 0
		for r_i in xrange( len(cluster) ):
			row_i = self.rows[ cluster[r_i] ]
			#a = self.matrix.row_lexems[ self.rows_id_list[ cluster[r_i]  ]]
			for r_j in xrange(r_i+1, len(cluster) ):
				n += 1
				sim += self.cmp.Compare(row_i, self.rows[cluster[r_j]] )
				#b = self.matrix.row_lexems[ self.rows_id_list[ cluster[r_j] ] ]
				#print "counting (%s, %s) " % (a,b)
		#print "sim %s, n=%s" % (sim,n)
		return sim / n

	def __read_cluto_output(self):
		file = open(self.treefilename)
		self.parray = []
		lineNo = 0
		for line in file:
			self.parray.append(int(line.split()[0]))
			lineNo+=1

		self.index = {}
		for i in xrange( len(self.parray) ):
			pointingTo = self.parray[i]
			if self.index.has_key(pointingTo) :
				self.index[pointingTo].append(i)
			else:
				self.index[pointingTo] = [i]

		roots = self.index[-1]
		new_roots = []
		for r in roots:
			if self.index.has_key(r) == False:
				roots.remove(r)


		if True:
			print self.index
			print self.parray
			roots = self.index[-1]
			for r in roots:
				self.__print_tree(r,  0)

	def __get_children(self, node):
		out = []
		if self.index.has_key(node) == False:
			out.append(node)
		else:
			l = self.index[node]
			for next_node in l:
				out.extend( self.__get_children(next_node) )
		return out

	def __print(self, x, level):
		for i in xrange( level*2 ):
			print ' ',
		print x

	def __print_tree(self, root, level):
		if root < len(self.rows):
			self.__print( self.rows[root] , level )
			return
		l = self.index[root]

		left = l[0]
		right = l[1]

		self.__print(root, level)
		self.__print_tree(left, level+1)
		self.__print_tree(right, level+1)


	def __write_rows(self):
		# check temp dir
		self.__check_tmpdir()
		#create tmp matrix
		f = os.path.join(self.temp_path, self.temp_matrix_name)
		file = open(f, "w")
		self.nz = self.__count_nz(self.rows)
		line = "%d %d %d\n" %(len(self.rows), self.matrix.NumberOfColumns(), self.nz)
		file.write(line)
		for row_label in self.rows:
			i = self.matrix.GetRowIndex(row_label)
			if self.matrix.GetRowIndex(row_label) > self.matrix.NumberOfRows():
				file.write("\n")
				continue

			row = self.matrix.GetRow(row_label)
			vals = row.GetValues()
			keys = vals.keys()

			for k in keys:
				part = "%d %s " % (k+1, vals[k])
				file.write(part)
			file.write("\n")
		file.close()

	def __count_nz(self, rows):
		nz = 0
		for row in rows:
			if self.matrix.GetRowIndex(row) > self.matrix.NumberOfRows():
				continue
			nz += self.matrix.GetRow(row).GetValues().size()
		return nz

	def __check_tmpdir(self):
		path = os.path
		if path.exists(self.temp_path) and path.isdir(self.temp_path) == False:
			error_string = "Path %s does exists, but is not a directory" \
					% self.temp_path
			raise IOError, error_string
		if path.exists(self.temp_path) == False:
			os.mkdir(self.temp_path)
		if path.exists(self.temp_path) == False:
			error_string = "Cannot create directory %s" \
					% self.temp_path

	def __free_mem(self):
		del self.rows
		del self.matrix

class CBC:
	def __init__(self, workdir, matrix_name, kbestfilename, pathToCluto):
		self.kbestfilename = kbestfilename
		self.workdir = workdir
		self.matrix_name = matrix_name
		self.matrix = pymatrices.LoadSuperMatrix(workdir, matrix_name, False, True)
		pycomparator.mi_weight_matrix(self.matrix)
					
		self.cluto = CLUTO(pathToCluto)
	
	def cluster(self):
		self.phase1()
		self.phase2()
		self.phase3()

	def phase1(self):
		print "CBC, phase 1"
		self.kbest = cbctools.extract(self.kbestfilename)
		
	def phase2(self):
		print "CBC, phase 2"
		for word in self.kbest.keys():
			rows = self.kbest[word]
			

	def phase3(self):
		print "CBC, phase 3"

if __name__ == "__main__":
	print "CBC"
