##
## clirc -- An IRC client in Python.
## cursesscreen.py - Curses user interface.
##
## Copyleft 1997-2000 Teemu Kalvas <chery@s2.org>
##

import fcntl
import curses
import signal
import string
import struct
import sys
import TERMIOS

import curseseditline
import cursesmodebar
import curseswindow
import main
import reloadable
import resizeterm
import runloop
import user

class CursesScreen(reloadable.Reloadable):
    def __init__(self):
        self.window = curses.initscr()
        curses.raw()
        curses.noecho()
        curses.nonl()
        curses.intrflush(1)
        self.window.keypad(1)
        self.height, self.width = self.window.getmaxyx()
        self.configuration = []
        self.changed_windows = {}
        self.redraw_windows = {}
        self.active_window = None
        y = 0
        for a, h, w in [(1, self.height - 2, None),
                        (0, 1, cursesmodebar.CursesModebar(self)),
                        (0, 1, curseseditline.CursesEditline(self))]:
            subwin = self.window.subwin(h, self.width, y, 0)
            subwin.scrollok(1)
            self.configuration.append((a, h, subwin, w))
            y = y + h
        self.cursor_at = self.configuration[2][3], 0
        runloop.reading[0] = self.read_ready
        main.cleaners[self.clean] = ()
	self.post_reload()

    def post_reload(self):
	self.ensure_defaults([('marked_window', None)])
        signal.signal(signal.SIGWINCH, self.resize)

    def clean(self):
        curses.nl()
        curses.noraw()
        curses.endwin()

    def resize(self, a, b):
        ## Postpone the real handling of the resize signal until just before
        ## the main select() call to ensure that it is not done in the middle
        ## of some processing that caches or otherwise depends on the
        ## subwindows that are going to be changed.
        runloop.signal_queue.append(self.resize_event)

    def resize_event(self):
        i = 0
        while i < len(self.configuration):
            a, h, subwin, win = self.configuration[i]
            self.configuration[i] = a, h, None, win
            i = i + 1
        del subwin
        old_height = self.height
        self.height, self.width = struct.unpack(
            '4h', fcntl.ioctl(sys.stdout.fileno(), TERMIOS.TIOCGWINSZ,
                              struct.pack('4h', 0, 0, 0, 0)))[:2]
        resizeterm.resizeterm(self.height, self.width)
        change = self.height - old_height
        extra_div = len(filter(lambda x: x[0], self.configuration))
        extra, step = change / extra_div, change % extra_div
        i = 0
        y = 0
	acc = 0
        while i < len(self.configuration):
            a, h, subwin, win = self.configuration[i]
            if a:
                h = h + extra
                acc = acc + step
                if acc >= extra_div:
                    h = h + 1
                    acc = acc - extra_div
            subwin = self.window.subwin(h, self.width, y, 0)
            subwin.scrollok(1)
            self.configuration[i] = a, h, subwin, win
            i = i + 1
            y = y + h
	self.refresh()

    def refresh(self):
        for a, h, s, w in self.configuration:
            w.redraw()

    def select_window(self, win):
	i = 0
	while i < len(self.configuration):
	    a, h, s, w = self.configuration[i]
	    if w == self.active_window:
		self.configuration[i] = a, h, s, win
		break
	    i = i + 1
	self.active_window = win
	self.refresh()

    def prev_window(self):
        if self.active_window:
	    self.select_window(self.active_window.prev)

    def next_window(self):
        if self.active_window:
	    self.select_window(self.active_window.next)

    def mark_window(self):
	self.marked_window = self.active_window

    def select_marked_window(self):
	win = self.active_window.next
	while win <> self.active_window and win <> self.marked_window:
	    win = win.next
	self.select_window(win)

    def select_active_window(self):
	win = self.active_window.next
	while win <> self.active_window and not win.detect:
	    win = win.next
	self.select_window(win)

    def get_window(self, window):
        for a, h, s, w in self.configuration:
            if w == window:
                return s
        return None

    def read_ready(self, fd):
        ch = self.window.getch()
        if (ch >= 32 and ch < 127) or (ch >= 160 and ch < 256):
            key = chr(ch)
        else:
            if ch >= curses.KEY_MIN and ch <= curses.KEY_MAX:
                key = string.replace(string.replace(
                    string.lower(curses.keyname(ch)), '(', ''), ')', '')
            elif ch > 0 and ch < 27:
                key = 'ctrl_' + chr(ch + 96)
            elif ch >= 27 and ch < 32:
                key = 'ctrl_' + ['leftbracket', 'backslash', 'rightbracket',
                                 'caret', 'underscore'][ch - 27]
            elif ch == 127:
                key = 'key_backspace'
            else:
                key = 'keynum_' + repr(ch)
        user.keypress_event(user.KeyEvent(self.active_window, self, key,
                                          self.configuration[-1][3]))

    def window_changed(self, window):
        self.changed_windows[window] = ()
        runloop.writing[1] = self.write_ready

    def cursor_changed(self, window, x):
        self.cursor_at = window, x
        runloop.writing[1] = self.write_ready

    def redraw_window(self, window):
        self.redraw_windows[window] = ()
        runloop.writing[1] = self.write_ready

    def write_ready(self, fd):
        for window in self.redraw_windows.keys():
            window.redraw_event()
        self.redraw_windows = {}
        for window in self.changed_windows.keys():
            if window <> self.cursor_at[0]:
                win = self.get_window(window)
                if win:
                    win.touchwin()
                    win.refresh()
        win = self.get_window(self.cursor_at[0])
        if win:
            win.move(0, self.cursor_at[1])
            win.touchwin()
            win.refresh()
        self.changed_windows = {}
        del runloop.writing[1]

    def swap_next(self):
	b = self.active_window
	a = b.prev
	c = b.next
	d = c.next
	a.next = c
	c.prev = a
	c.next = b
	b.prev = c
	b.next = d
	d.prev = b
	self.select_window(c)

    def add_window(self, net, name, buf):
        window = curseswindow.CursesWindow(name, buf, self)
        a, height, subwin, owindow = self.configuration[0]
        self.configuration[0] = a, height, subwin, window
        self.active_window = window
        if owindow:
            window.prev = owindow
            window.next = owindow.next
            owindow.next.prev = window
            owindow.next = window
        else:
            window.next = window
            window.prev = window
        self.refresh()

    def remove_window(self, window):
	i = 0
	while i < len(self.configuration):
	    a, h, s, w = self.configuration[i]
	    if w == window:
		self.configuration[i] = a, h, s, window.next
		break
	    i = i + 1
        window.next.prev = window.prev
        window.prev.next = window.next
	if self.active_window == window:
	    self.active_window = window.next
        self.refresh()

reloadable.register_for_reload(CursesScreen)

## End. ##
