#!/usr/bin/python
#
# Language Learner with No Name - LLNN
# V 3.0
# (c)2001 M.J.London
#
# Based on Joel Rosdahl's irclib

# TODO:
#  db for actions
#  parsing questions
#  correction
#  weighted words

import os
import string
import whrandom
import shelve
from ircbot import SingleServerIRCBot
from irclib import nm_to_n, irc_lower

class LLNN(SingleServerIRCBot):
	def __init__(self, channel, nickname, server, port=6667):
		SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname)
		self.channel = channel
		self.passwd = "somepassword"
		self.words =  [ ["Hi",[1]], ["there",[]] ] 
		self.canstart = [0]
		self.canend = [0,1]
		try:
			self.dbase = shelve.open("llnn.dat")
		except:
			print "Error opening file"
		try:
			self.words = self.dbase['words']
			self.canstart = self.dbase['canstart']
			self.canend = self.dbase['canend']
		except:
			print "Error reading stored data"	
		try:
			self.dbase.close()
		except:
			print "Error closing file"
		self.updates = 0
		self.start()

	def get_version(self):
		return "LLNN Version 3.0 (c)2001 M.London"

	def on_welcome(self, c, e):
		c.join(self.channel)

	def on_privmsg(self, c, e):
		print "<" + nm_to_n(e.source()) + "> [MSG]", e.arguments()[0]
		self.addressed(c, nm_to_n(e.source()), nm_to_n(e.source()), e.arguments()[0])

	def on_pubmsg(self, c, e):
		print "<" + nm_to_n(e.source()) + ">", e.arguments()[0]
		a = string.split(e.arguments()[0], ":", 1)
		if len(a) < 2:
			a = string.split(e.arguments()[0], ",", 1)
		if len(a) < 2:
			a = string.split(e.arguments()[0], " ", 1)
		if len(a) > 1 and irc_lower(a[0]) == irc_lower(self.connection.get_nickname()):
			self.addressed(c, e.target(), nm_to_n(e.source()), string.strip(a[1]))
		else:
			self.process_string(e.arguments()[0])
		return

	def on_ctcp(self, c, e):
		if len(e.arguments()) > 1:
			if e.arguments()[0] == "ACTION":
				print "*" + nm_to_n(e.source()), e.arguments()[1]
			else:
				print "[CTCP", e.arguments()[0] + "]", "<" + nm_to_n(e.source()) + ">", e.arguments()[1]
		else:
			print "[CTCP", e.arguments()[0] + "]", "<" + nm_to_n(e.source()) + ">"
		if e.arguments()[0] == "PING":
			if len(e.arguments()) > 1:
				response = e.arguments()[1]
			else:
				response = ""
			c.ctcp_reply(nm_to_n(e.source()), e.arguments()[0]+' '+response)
		elif e.arguments()[0] == "VERSION":
			print "[CTCP Reply]", self.get_version()
			c.ctcp_reply(nm_to_n(e.source()), e.arguments()[0]+' '+self.get_version());

	def on_invite(self, c, e):
		print nm_to_n(e.source()),"invited me to",e.arguments()[0]
		print "Joining",e.arguments()[0]
		c.join(e.arguments()[0])

	def command(self, nick, cmdstring):
		c = self.connection
		cmd = string.split(cmdstring)
		print "Command from",nick,":",cmd
		if cmd[0] == "die":
			self.save_data()
			self.die()
		elif cmd[0] == "join" and len(cmd) > 1:
			print "Joining",cmd[1]
			if len(cmd) > 2:
				c.join(cmd[1], cmd[2])
			else:
				c.join(cmd[1])
		elif cmd[0] == "part" and len(cmd) > 1:
			print "Leaving",cmd[1]
			c.part(cmd[1])
	
	def addressed(self, c, source, nick, message):
#		print "Addressed by",nick,":",message
		p = string.split(message)
		if p[0] == self.passwd and len(p) > 1:
			self.command(nick, string.join(p[1:]))
		elif irc_lower(p[0]) == "speak":
			c.privmsg(source, self.create_sentence())
		elif irc_lower(p[0]) == "stats":
			out = "I know "+`len(self.words)`+" words, "+`len(self.canstart)`+" can start a sentence and "+`len(self.canend)`+" can end a sentence"
			c.privmsg(source, out)
		elif nm_to_n(source) == nick:
			self.process_string(message)
			c.privmsg(source, self.create_sentence())
		else:
			self.process_string(message)
			c.privmsg(source, self.create_sentence())


	def process_string(self, strings):
#		print "Processing :",strings
		sentences = string.splitfields(strings, '. ')
		for s in sentences:
			words = string.split(strings)
#			print "words :",words
			if self.is_word(words[0]) == -1:
				newword = [ words[0], [] ]
#				print "Initial word: Adding",newword
				self.words.append(newword)
			windex = self.is_word(words[0])
			if windex not in self.canstart:
#				print "Add to canstart"
				self.canstart.append(windex)
			for w in words:
				windex = self.is_word(w)
#				print w,"is",windex
				if windex != -1:
					if words.index(w) < len(words)-1:
#						print "is",words.index(w),"<",len(words),"for",w
						nextindex = self.is_word(words[words.index(w)+1])
						if nextindex != -1:
							if nextindex not in self.words[windex][1]:
#								print "Add reference to next word",self.words[nextindex][1],nextindex
								self.words[windex][1].append(nextindex)
#								print "Result :",self.words[nextindex]
						else:
							newword = [ words[words.index(w)+1], [] ]
#							print "New word :",newword
							self.words.append(newword)
							nextindex = self.is_word(words[words.index(w)+1])
							if nextindex not in self.words[windex][1]:
#								print "Add reference to next word",self.words[nextindex][1],nextindex
								self.words[windex][1].append(nextindex)
#								print "Result :",self.words[nextindex]
					else:
						if windex not in self.canend:
#							print "Add",w,"at",windex,"to canend"
							self.canend.append(windex)
				else:
					newword = [w,[]]
					self.words.append(newword)
		if windex not in self.canend:
#			print "Add",w,"at",windex,"to canend"
			self.canend.append(windex)
#		print self.words, self.canstart, self.canend
		self.updates = self.updates + 1
		if self.updates > 5:
			self.save_data()
			updates = 0

					
	def is_word(self, word):
		for w in self.words:
			if word in w:
				return self.words.index(w)
		return -1

	def create_sentence(self):
		word = whrandom.choice(self.canstart)
		sentence = self.words[word][0]
		while len(self.words[word][1]) > 0:
			if word in self.canend and whrandom.randint(0,5) > 4:
				break
			word = whrandom.choice(self.words[word][1])
			sentence = sentence + ' ' + self.words[word][0]
		print "<" + self.connection.get_nickname() + ">",sentence
		return sentence

	def save_data(self):
		try:
			os.unlink("llnn.dat")
		except:
			print "Error removing old file"
		try:
			self.dbase = shelve.open("llnn.dat")
		except:
			print "Error opening file"
		try:
			self.dbase['words'] = self.words
			self.dbase['canstart'] = self.canstart
			self.dbase['canend'] = self.canend
		except:
			print "Error writing data"
		try:
			self.dbase.close()
		except:
			print "Error closing file"


def main():
	import sys
	if len(sys.argv) == 1:
		server = "the-junction.net"
		port = 6667
		channel = "#18-30"
		nick = "llnn"
	else:
		if len(sys.argv) != 4:
			print "Usage: llnn server[:port] channel nick"
			sys.exit(1)
	
		s = string.split(sys.argv[1], ":", 1)
		server = s[0]
		if len(s) == 2:
			try:
				port = int(s[1])
			except ValueError:
				print "Error: Bad port"
				sys.exit(1)
		else:
			port = 6667
		channel = sys.argv[2]
		nick = sys.argv[3]

	llnnbot = LLNN(channel, nick, server, port)
	llnnbot.start()


if __name__ == "__main__":
	main()
