#!/usr/bin/python
##############################################################################
# pitonella client                                  Author: M. M. Caldarelli #
#                                                                            #
# This is free software. Do whatever you want with it.                       #
##############################################################################
import readline, select, string, sys, telnetlib, termios, TERMIOS, os

HOST = 'ping.science.unitn.it'                              # default bbs host
PORT = 4000                                                 #         bbs port

# Classe per la comunicazione con il server.
class BBS:
    cmd_server = []
    ret_server= []

    def __init__(self, host, port):
	self.tn = telnetlib.Telnet(host, port)

    def esci(self, msg):
	self.write('QUIT')
	print '\n' + msg
	self.tn.close()
	reset_term()
	sys.exit('Bye bye.');

    def write(self, cmd):
	self.tn.write(cmd + '\n')

    def write_ret(self, cmd):
	self.write(cmd)
	return self.read()

    def read(self, timeout = 1000000000):
	while not self.ret_server[:]:
	    self.input(0, timeout)
	return self.ret_server.pop(0)

    def input(self, esegui, timeout=1000000000):
	while (1):
	    str = self.tn.read_until('\n', timeout)
	    timeout = 0
	    if len(str) == 0:
		return ''
	    if (str[0] == '8') | (str[0] == '9'):
		self.cmd_server.append(str)
		if esegui:
		    self.elabora_cmd_server()
	    else:
		self.ret_server.append(str)
		return

    def inkey(self):
	while 1:
	    socklist = [sys.stdin] + [self.tn]
	    ins, outs, exs = select.select(socklist,[],[])
	    if sys.stdin in ins:
		cmd = sys.stdin.read(1)
		if len(cmd) != 0:
		    return cmd[0]
	    elif self.tn in ins:
		self.input(1)

    def elabora_cmd_server(self):
	while self.cmd_server[:]:
	    esegui_comando(self.cmd_server.pop(0))

def esegui_comando(cmd):
    if cmd[:3] == '910':                               # Notifica Login
	print '\r%-79s\r*** %s si e` appena collegato.' % ('', cmd[5:-1])
	print_prompt()
    elif cmd[:3] == '911':                             # Notifica Logout
	print '\r%-79s\r*** %s e` uscito dalla bbs.' % ('', cmd[5:-1])
	print_prompt()
    elif cmd[:3] == '901':                             # HEADER X
	rec = string.split(cmd[4:-1], '|')
	x.append(('\r%-79s\r*** Express Message da ' % '') + rec[0] + ' alle '\
	+ rec[1] + ':' + rec[2])
    elif cmd[:3] == '902':                             # TEXT X, BROADCAST
	x.append('> ' + cmd[4:-1])
    elif cmd[:3] == '903':                             # X END
	for riga in x[:]:
	    print x.pop(0)
	print_prompt()

#############################################################################
# Funzioni di inizializzazione/reset terminale
def init_term():
    global term_orig
    term_orig = termios.tcgetattr(sys.stdin.fileno())
    new = termios.tcgetattr(sys.stdin.fileno())
    new[3] = new[3] & ~(TERMIOS.ICANON|TERMIOS.ECHO)
    termios.tcsetattr(sys.stdin.fileno(), TERMIOS.TCSADRAIN, new)

def reset_term():
    termios.tcsetattr(sys.stdin.fileno(), TERMIOS.TCSADRAIN, term_orig)

# Input di una stringa
def str_input(prompt):
    reset_term()
    str = raw_input(prompt)
    init_term()
    return str

# Legge testo di nrighe max da utente.
def text_input(nrighe):
    text = []
    for i in range(0, nrighe):
	str = str_input('>')
	if len(str):
	    text.append(str)
	else:
	    return text
    return text

def text_send(txt):
    for riga in txt[:]:
	bbs.write('TEXT ' + txt.pop(0))

# Stampa il prompt.
def print_prompt():
    if status == 0:
	print '\n' + room + ' ',
    elif status == 1:
	print '\n' + msg_prompt,
    sys.stdout.flush()

# Stampa un testo in arrivo dal server.
def print_text():
    while 1:
	str = bbs.read()
	if str == "000\n":
	    return
	print str[4:-1]

############################################################################
# Modifica il doing
def doing():
    str = str_input("Doing: ")
    bbs.write_ret('EDNG 1|' + str)

# GOTO COMMANDS
def goto_output():
    global room, barflag
    str = bbs.read()
    if str[0] != '2':
	print '*** Errore.\n'
	return
    rec = string.split(str[4:], '|')
    room = rec[0] + rec[1]            # Nuovo Room Prompt
    print "%s - %d nuovi su %d messaggi." % (room, int(rec[3]), int(rec[2]))
    if int(rec[3]):
	barflag = 1         # Ci sono nuovi messaggi, <SPACE> --> <n>ew
    else:
	barflag = 0         # <SPACE> --> <g>oto

def goto():
    print 'Goto next room.'
    bbs.write('GOTO 0||')
    goto_output()

def jump():
    newroom = str_input('Jump to : ')
    bbs.write('GOTO 0|%s|' % newroom)
    goto_output()

def skip():
    print 'Skip.'
    bbs.write('GOTO 1||')
    goto_output()
    
def abandon():
    print 'Abbandona la room.'
    bbs.write('GOTO 2||')
    goto_output()

# Lista dei comandi disponibili.
def help():
    print """Help.\n
 <a> abandon room      <g> goto next room            <n> read new messages
 <d> edit doing        <h> this help                 <Q> quit the bbs
 <e> enter post        <j> jump to a specific room   <s> skip room
 <f> read forward      <k> list known rooms          <w> who is online
 <x> eXpress message   < > spacescan"""

# Lista delle room conosciute con/senza messaggi nuovi.
def known_rooms():
    print 'Known Rooms.'
    if bbs.write_ret('RKRL 1')[0] != '2':
	return
    print '\n   Rooms con messaggi da leggere:'
    print_rooms()
    if bbs.write_ret('RKRL 2')[0] != '2':
	return
    print '\n   Non ci sono messaggi nuovi in:'
    print_rooms()

def print_rooms():
    i = 0;
    str = bbs.read()
    while str != "000\n":
	rec = string.split(str[4:], '|')
	print '%3d. %-20s' % (int(rec[1]), rec[0]),
	i = i+1
	if i % 3 == 0:
	    print
	str = bbs.read()
    if i % 3 != 0:
	print

# Procedura di login (NB: solo per utenti registrati o Ospite)
def login():
    bbs.write_ret('INFO')
    bbs.write('LBAN 0')                    # Chiede al server il login banner
    print_text()                           # e lo stampa
    print """
Inserire il nome che si vuole utilizzare presso la bbs oppure 'Ospite' 
nel caso si voglia solo dare un\'occhiata ('Esci' chiude la connessione).
"""
    user = str_input("Nome    : ")
    if (user == 'Off') | (user == 'Esci') | (len(user) == 0):
	bbs.esci('Torna presto!')
    if bbs.write_ret('USER ' + user)[:3] != '223':
	bbs.esci('Solo utenti validati possono usare questo client per ora!')
    password = raw_input("Password: ")
    print
    if bbs.write_ret('USR1 ' + user + '|' + password)[0] != '2':
	bbs.esci('*** Password errata.')
    bbs.write_ret('CHEK')
    return 1

# Invia post in room corrente. (NB: no mail e msg anonimi per ora!)
def post():
    print 'Enter post.'
    cmd = bbs.write_ret('PSTB |0|0|')
    if cmd[0] != '2':
	print ' *** Errore.'
	return
    rec = string.split(cmd[4:], '|')
    if (int(rec[0]) & 16):                 # Nella room sono ammessi i Subject
	subject = str_input('Subject :')
    else:
	subject = ''
    txt = text_input(100)
    text_send(txt)
    if bbs.write_ret('PSTE ' + subject + '|0|0|0|')[0] != '2':
	print ' *** Errore.'
    else:
	print 'Post inviato.'

# Legge un post
# restituisce 1 in caso di successo, 0 altrimenti.
def read(mode):
    global barflag, status, msg_prompt
    barflag = 0
    str = bbs.write_ret('READ %d' % mode)
    if str[0] != '2':
	status = 0
	barflag = 0
	return 0
    local, rimanenti = string.split(str[4:-1], '|')
    rec = string.split(bbs.read(), '|')
    print '\n ' + rec[1] + ', data stellare ' + rec[3] + ' - da ' + rec[2]
    if len(rec[4]):
	print ' Subject: ' + rec[4]
    print
    print_text()
    msg_prompt = '['+room+'msg #'+local+' (Da leggere: '+rimanenti+')] -> '
    status = 1
    barflag = 1
    return 1

def spacescan():
    global barflag, status
    if status == 1:
	print '\r%-79s\r' % '',
	read(10)
	return
    if barflag == 0:
	goto()
    else:
	print('Nuovi messaggi.')
	read(1)

# Comando <w>ho: lista utenti connessi.
def who():
    print "Who: Chi e` in linea.\n"
    if bbs.write_ret('HWHO')[0] != '2':
	return
    print 'Login        Nome Utente                Room              Da Host/[Doing]'
    print '----- ------------------------- -------------------- --------------------------'
    str = bbs.read()
    while str != "000\n":
	rec = string.split(str, '|')
	doing = rec[2]
	if len(rec[2]) > 0:
	    if rec[2][0] == '[':
		doing = rec[2] + " "*(25-len(rec[2])) + ']'
	print '      %-25s %-20s %-26s' % (rec[1], rec[4][:-1], doing)
	str = bbs.read()
    ospiti = int(bbs.read()[4:])
    if ospiti:
	if ospiti == 1:
	    print "\nC\'e` un ospite.\n"
	else:
	    print "\nCi sono %d ospiti.\n" % (ospiti)

# Invia un eXpress message.
def xmsg():
    print 'eXpress message.'
    dest = str_input('Destinatario: ')
    if bbs.write_ret('XMSG ' + dest)[0] != '2':
	print ' *** Errore.'
	return
    txt = text_input(5)
    text_send(txt)
    if bbs.write_ret('XEND')[0] != '2':
	print ' *** Errore.'
    else:
	print 'eXpress message inviato a ' + dest + '.'

###########################################################################
#       M A I N                                                           #
###########################################################################
barflag = 1                                  # per lo spacescan
status = 0                                   # 0 room; 1 msg; 2 pager
x = []                                       # X in arrivo

try:
    bbs = BBS(HOST, PORT)                    # Apro connessione con server
except:
    sys.exit('Cannot connect to server.')
init_term()                                  # Inizializzo il terminale
login()                                      # Procedura di login
bbs.write_ret('EDNG 1|pitonella')            # Setto il doing
bbs.write("GOTO 4||")                        # Andiamo nella Lobby)
goto_output()
noprompt = 0
while (1):                                   # main loop
    if not noprompt:
	print_prompt()
    noprompt = 0
    bbs.elabora_cmd_server()
    c = bbs.inkey()
    if status == 0:                          # Sono al room prompt
	if (c == 'Q'):
	    break
	elif (c == ' '):
	    spacescan()
	elif (c == 'a'):
	    abandon()
	elif (c == 'd'):
	    doing()
	elif (c == 'e'):
	    post()
	elif (c == 'f'):
	    status = 1
	    read(0)
	elif (c == 'g'):
	    goto()
	elif (c == 'h'):
	    help()
	elif (c == 'j'):
	    jump()
	elif (c == 'k'):
	    known_rooms()
	elif (c == 'n'):
	    status = 1
	    read(1)
	elif (c == 's'):
	    skip()
	elif (c == 'w'):
	    who()
	elif (c == 'x'):
	    xmsg()
	else:
	    noprompt = 1
    elif status == 1:                        # Sono al Message Prompt
	if (c == ' ') | (c == 'n'):
	    spacescan()
	elif (c == 's'):
	    print 'Stop.'
	    status = 0
	    barflag = 0
	else:
	    noprompt = 1

bbs.esci('Quit.\n[Disconnessione...]')
