#!python

from pydispatch import dispatcher
from twisted.internet import reactor

from phxd.client import HLClient
from phxd.client.signals import *
from phxd.constants import *

import curses


class CursesStdIO:
    def fileno(self):
        return 0

    def doRead(self):
        pass

    def logPrefix(self):
        return 'phx'


class ClientWrapper:

    def __init__(self, screen):
        self.client = HLClient()
        self.client.nickname = "phxd.client"
        self.screen = screen
        dispatcher.connect(self._connected, signal=client_connected)
        dispatcher.connect(self._disconnected, signal=client_disconnected)
        dispatcher.connect(self._chat, signal=chat_received)

    def _connected(self):
        self.screen.addLine('*** connected')
        self.client.sendLogin("guest", "").addCallback(self._login)

    def _disconnected(self):
        pass

    def _login(self, packet):
        self.screen.addLine('*** login successful')

    def _chat(self, packet):
        line = packet.getString(DATA_STRING, None)
        if line is not None:
            self.screen.addLine(line.replace('\r', '').replace('\n', ''))

    def chat(self, line):
        self.client.sendChat(line)

    def do_quit(self, args):
        reactor.stop()

    def do_connect(self, args):
        host = args[0]
        port = 5500
        if ':' in args[0]:
            (host, port) = args[0].split(':')
            port = int(port)
        reactor.connectTCP(host, port, self.client)

    def do_nick(self, args):
        nick = args[0].strip()
        self.screen.addLine('*** set nick to %s' % nick)
        self.client.sendChange(nick)

    def do_msg(self, args):
        uid = int(args[0])
        msg = ' '.join(args[1:])
        self.client.sendMessage(msg, uid)


class Screen (CursesStdIO):

    def __init__(self, stdscr):
        self.client = ClientWrapper(self)
        self.input = ""
        self.status = "[phx]"
        self.lines = []
        self.stdscr = stdscr
        self.rows, self.cols = self.stdscr.getmaxyx()
        self.stdscr.nodelay(1)
        try:
            curses.start_color()
            curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
            curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLUE)
        except:
            pass

    def connectionLost(self, reason):
        pass

    def redisplay(self):
        self.rows, self.cols = self.stdscr.getmaxyx()
        self.stdscr.clear()
        k = 0
        index = len(self.lines) - 1
        while k < (self.rows - 3) and index >= 0:
            line = self.lines[index]
            if len(line) >= self.cols:
                parts = []
                parts.append(line[0:self.cols - 1])
                line = line[self.cols - 1:]
                while len(line) >= (self.cols - 17):
                    parts.append(' ' * 16 + line[0:self.cols - 17])
                    line = line[self.cols - 17:]
                if len(line) > 0:
                    parts.append(' ' * 16 + line)
                for i in reversed(range(len(parts))):
                    if k < (self.rows - 3):
                        self.stdscr.addstr(self.rows - 3 - k, 0, parts[i])
                        k = k + 1
            else:
                self.stdscr.addstr(self.rows - 3 - k, 0, line)
                k = k + 1
            index = index - 1
        ws = ' ' * (self.cols - len(self.status))
        self.stdscr.addstr(self.rows - 2, 0, self.status + ws, curses.A_BOLD | curses.color_pair(2))
        self.stdscr.move(self.rows - 1, self.cols - 1)
        self.stdscr.refresh()

    def addLine(self, line):
        self.lines.append(line)
        self.redisplay()

    def handleInput(self, line):
        if line.startswith('/'):
            parts = line.strip().split()
            cmd = parts[0][1:]
            args = parts[1:]
            if hasattr(self.client, 'do_%s' % cmd):
                func = getattr(self.client, 'do_%s' % cmd)
                func(args)
                return
        self.client.chat(line)

    def doRead(self):
        curses.noecho()
        ch = self.stdscr.getch()
        if ch == curses.KEY_BACKSPACE:
            self.input = self.input[:-1]
        elif ch in (curses.KEY_ENTER, 10):
            self.handleInput(self.input)
            self.input = ""
        elif ch <= 255:
            self.input = self.input + chr(ch)
        if len(self.input) < self.cols:
            self.stdscr.addstr(self.rows - 1, 0, self.input)
            self.stdscr.move(self.rows - 1, len(self.input))
            self.stdscr.refresh()


def main(stdscr):
    screen = Screen(stdscr)
    stdscr.refresh()
    reactor.addReader(screen)
    reactor.run()


if __name__ == "__main__":
    curses.wrapper(main)
