#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright 2006 Harri Pitkänen (hatapitk@iki.fi)
# Program to create (Finnish) anagrams
# This program requires Python and python-enchant.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

import sys
import locale
import enchant
import getopt
import codecs
import time

# Computes lexicographically next permutation of elements in list clist.
# Returns the permutation or None, if clist already has its elements in
# lexicographically decreasing order. This algorithm is explained in
# Edsger W. Djikstra: A Discipline of Programming.
def next_permutation(clist):
	n = len(clist)
	if n < 2: return None
	i = n - 1
	while i > 0:
		if clist[i - 1] < clist[i]:
			j = i - 1
			k = n - 1
			while clist[k] <= clist[j]: k = k - 1
			# swap items j and k
			tmp = clist[j]
			clist[j] = clist[k]
			clist[k] = tmp
			# reverse list from i to end
			ltmp = clist[i:]
			ltmp.reverse()
			clist[i:] = ltmp
			return clist
		i = i - 1
	return None

# Calculates the number of distinct permutations for given character list
def count_permutations(clist):
	# Calculate the number of all permutations
	n = 1
	for i in range(1, len(clist) + 1): n = i * n
	
	# Remove the duplicates
	chars = list(set(clist))
	for c in chars:
		count = len(filter(lambda x: x == c, clist))
		for i in range(1, count + 1): n = n / i
	
	return n

def usage():
	print u'Usage: ' + sys.argv[0] + u' [-l language] word1 [word2 ...]'
	print u'       ' + sys.argv[0] + u' [-l language] -f file'
	print u'Options:'
	print u'     --nocheck   List all permutations'

def words_from_file(filename):
	try:
		inputfile = codecs.open(filename, "r", ENCODING)
		wordlist = []
		while True:
			rivi = inputfile.readline()
			if rivi == u'': break
			wordlist.append(rivi.strip())
		inputfile.close()
		return wordlist
	except IOError:
		sys.stderr.write("Could not read from file " + filename + "\n")
		sys.exit(1)


# Start of main program

# Set defaults
ENCODING = locale.getpreferredencoding()
langcode = "fi_FI"
words = []
nocheck = False

# Get options
try:
	(opts, args) = getopt.getopt(sys.argv[1:], "l:f:", ["help", "nocheck"])
except getopt.GetoptError:
	usage()
	sys.exit(2)
for (opt, value) in opts:
	if opt == "-l": langcode = value
	elif opt == "-f": words = words + words_from_file(value)
	elif opt == "--help":
		usage()
		sys.exit(0)
	elif opt == "--nocheck":
		nocheck = True
for word in args:
	words.append(unicode(word, ENCODING))

# At least one word should be available
if len(words) == 0:
	usage()
	sys.exit(1)

# Initialise the spellchecker
try:
	dict = enchant.Dict(langcode)
except enchant.DictNotFoundError:
	print u"No dictionary was found for language " + langcode
	sys.exit(1)
sys.stderr.write("Using language " + dict.tag + " with Enchant " + dict.provider.desc + "\n")

# Calculate the number of permutations to check
nperms = 0
for word in words: nperms = nperms + count_permutations(list(word))
sys.stderr.write("Number of permutations to check: %i\n" % nperms)

# List all anagrams
starttime = time.clock()
perms = 0
for word in words:
	clist = list(word)
	clist.sort()
	while (clist != None):
		perms = perms + 1
		wrd = u''.join(clist)
		if nocheck or dict.check(wrd):
			print wrd.encode(ENCODING)
		clist = next_permutation(clist)
time_used = time.clock() - starttime

# Show statistics
sys.stderr.write("Number of permutations tested: " + `perms` + "\n")
sys.stderr.write("Time used (seconds): " + `time_used`[:7] + "\n")
if time_used > 0:
	sys.stderr.write("Permutations per second: " + `perms/time_used`[:7] + "\n")

