#!/usr/bin/python
# Main module, handles scheduling and networking
import sys
if sys.version_info[:2] < [2,4]: print "Need 2.4"; sys.exit(1)
import cmd,event,textwrap,traceback as tb
from core import *
from socket import *
from util import *

sc = 0

colors = {
    'D': "1;30", 'd': "0;30", 'R': "1;31", 'r': "0;31",
    'G': "1;32", 'g': "0;32", 'Y': "1;33", 'y': "0;33",
    'B': "1;34", 'b': "0;34", 'M': "1;35", 'm': "0;35",
    'C': "1;36", 'c': "0;36", 'W': "1;37", 'x': "0" }

# replace colors given by the regular expr in sendl
def crepl(m):
    x = m.group(1)
    return (x == '{' and x) or (x in colors and "\x1b[%sm" % colors[x]) or ""

# Player connection class
class Conn(ac.dispatcher):
    din = ""
    out = ""
    had_in = 0
    p = 0

    def __init__(S, s):
        ac.dispatcher.__init__(S, s)
        S.ui = LoginUI(S)

    def sendl(S, t=""): # colorize and send with newline
        S.write(t+NL)

    def write(S, t): # colorize and send
        S.out += re.sub(r'\{([^#])',crepl,t)

    def handle_error(S):
        tb.print_exc()
        sys.exit(1)

    # write buffers, with a prompt
    def handle_write(S):
        if not S._fileno: return
        if S.had_in: S.write(S.ui.prompt()); S.had_in = 0
        elif S.out:
            S.out = NL+S.out # prepend newline
            S.write(S.ui.prompt())
        S.out = S.out[S.send(S.out):]

    def handle_close(S):
        p = S.p
        if p and p.loc:
            Act(p).room("$SN leaves the game.")
            if p.foe:
                p.unfight()
                p.rpc = p.hpc = 1  # penalty
            p.save()
            p.goto(0)  # remove from world
        S.close()

    def handle_read(S): S.din += S.recv(999)

    # check for lines
    def feed(S):
        # get one line, kill telnet negotiation, don't handle IAC IAC, etc
        x = re.split("\r\n|\r|\n",re.sub("\xff..","",S.din),1)
        S.din = x[-1]
        if len(x) > 1:
            S.had_in = 1
            S.ui.do(''.join([c for c in x[0] if c in printable]))

    def writeable(S): return S.had_in or S.out

# Listening socket handler
class Serv(ac.dispatcher):
    p = 0    # dummy
    din = "" # dummy

    def handle_accept(S):
        c = Conn(S.accept()[0])

# decorator to help with UI prompt states
def xpro(p):
    def d(f): f.pro = p; return f
    return d

class UI(object):
    pro = "> "
    def sendl(S, t): S.c.sendl(t)
    prompt = lambda S: S.pro

    def sw(S, st): # switch to state, setting prompt
        S.do = getattr(S, st)
        S.pro = S.do.pro

# Game UI, passes off to in-game command handler
class PlayUI(UI):
    def __init__(S, c, p):
        from world import start
        S.c = c
        p.c, c.p = c, p
        Act(p).world("{m>> {G$SN enters the realm.{x")
        p.goto(start)

    def do(S, l): cmd.do(S.c.p, l)

    def prompt(S):
        p = S.c.p
        return Template(S.c.p.pro).safe_substitute(hpc = p.hpc,hpm = p.hpm,
rpc = p.rpc,rpm = p.rpm,cash = p.cash,nl = NL)

# Create a new account
class NewUI(UI):
    pro = "New password: "
    def __init__(S, c, n):
        S.c = c
        S.p = Player(n)
        c.sendl("{b>> {BCreating a new character.{x")

    @xpro("Confirm new:  ")
    def ver(S, l):
        if l == S.p.passwd: S.c.ui = SexUI(S.c, S.p)
        else: S.sendl("{RPasswords don't match.{x"); S.sw("ask")

    @xpro("New password: ")
    def ask(S, l):
        if l: S.p.passwd = l; S.sw("ver")

    do = ask  # start here

# Uh, sex means gender here ;)
class SexUI(UI):
    pro = "Sex ({CM{x/{MF{x/{gN{x): "

    def __init__(S, c, p):
        S.c, S.p = c, p

    def do(S,l):
        if l in 'Mm': S.p.sex = 1
        if l in 'Ff': S.p.sex = 2
        # anything else (blank included) is intrepreted as 'N', for the lazy

        S.c.ui = PlayUI(S.c, S.p)
        S.p.sendl("\r\n{GWelcome to the game!{x\r\n")
        S.p.do("help intro")

# Prompts for name and password
class LoginUI(UI):
    pro = "Name: "
    def __init__(S,c):
        S.c = c
        c.sendl("{RWelcome to {g$$ Biz MUD{g $${R!{x")

    def name(S,l):
        l = fcap(l)
        if not l.isalpha(): return S.sendl("Invalid name")

        p = pload(l)
        if p: S.p = p; S.sw("login")
        else: S.c.ui = NewUI(S.c,l)

    @xpro("Password: ")
    def login(S,l):
        if l == S.p.passwd:
            S.c.ui = PlayUI(S.c, S.p)
            for p in cplayers():
                if p.name == S.p.name and p != S.p: p.c.close()  # kill old
        else: S.sendl("Invalid password")

    do = name   # start with this

# String editor
class EditUI(UI):
    pro = ""

    # ls = list of lines
    # cb will be called when string is saved (not aborted)
    # oldui will be restored when done editing

    def __init__(S,c,t,cb):
        S.c = c
        S.ls = t.split(NL)
        S.cb = cb
        S.oldui = S.c.ui
        c.sendl("{gType . on a blank line to finish or .h for extras.{x")
        c.sendl("  "+t)

    def do(S,l):
        x = 0
        if not l: S.ls += [l]
        if l[0] == ".": # end or dot commands
            if l == ".":
                S.c.ui = S.oldui; S.cb(NL.join(S.ls)); return
            else: x = l[1]

        if x == "s": S.c.sendl("  "+NL.join(S.ls)) # show
        if x == "c": S.ls = []           # clear
        if x == "q": S.c.ui = S.oldui # abort edit
        if x == "w": S.ls = textwrap.wrap("\n".join(S.ls), 77)
        if x == "d":
            if S.ls: del S.ls[-1]          # delete last line
            else: S.sendl("Nothing to delete.")
        if x == "h": S.sendl("{gDot commands:{x\r\n"
".s  show\r\n.c  clear\r\n.d  delete last line\r\n.w  wrap\r\n"
".q  abort\r\n.   done")

        if not x: S.ls += textwrap.wrap(l, 77) # append line

def pulse():
    ac.loop(0.05,1,count=1)

    # handle input: one line per connection
    for c in ac.socket_map.values():
        if c.din: c.feed()

    sc.enter(0.1,-1,pulse,())

if __name__ == "__main__":
    v = Serv()
    v.create_socket(AF_INET, SOCK_STREAM)
    v.set_reuse_addr()
    v.bind(("",8500))
    v.listen(5)
    print "Running"

    # sched will call the main "loop" every 0.1 seconds or so
    # not very efficient (why doesn't sched use heapq?), but it's simple
    sc = event.sc
    sc.enter(0.1,-1,pulse,())
    event.init()
    sc.run()