"""
Implementation of Termbox Python API in tdl.

See README.md for details.
"""

import tdl

"""
Implementation status:
 [ ] tdl.init() needs a window, made 132x60
 [ ] Termbox.close() is not implemented, does nothing
 [ ] poll_event needs review, because it does not
     completely follows the original logic
 [ ] peek is stubbed, but not implemented
 [ ] not all keys/events are mapped
"""


class TermboxException(Exception):
    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return self.msg


_instance = None

# keys ----------------------------------
KEY_F1 = 0xFFFF - 0
KEY_F2 = 0xFFFF - 1
KEY_F3 = 0xFFFF - 2
KEY_F4 = 0xFFFF - 3
KEY_F5 = 0xFFFF - 4
KEY_F6 = 0xFFFF - 5
KEY_F7 = 0xFFFF - 6
KEY_F8 = 0xFFFF - 7
KEY_F9 = 0xFFFF - 8
KEY_F10 = 0xFFFF - 9
KEY_F11 = 0xFFFF - 10
KEY_F12 = 0xFFFF - 11
KEY_INSERT = 0xFFFF - 12
KEY_DELETE = 0xFFFF - 13

KEY_PGUP = 0xFFFF - 16
KEY_PGDN = 0xFFFF - 17

KEY_MOUSE_LEFT = 0xFFFF - 22
KEY_MOUSE_RIGHT = 0xFFFF - 23
KEY_MOUSE_MIDDLE = 0xFFFF - 24
KEY_MOUSE_RELEASE = 0xFFFF - 25
KEY_MOUSE_WHEEL_UP = 0xFFFF - 26
KEY_MOUSE_WHEEL_DOWN = 0xFFFF - 27

KEY_CTRL_TILDE = 0x00
KEY_CTRL_2 = 0x00
KEY_CTRL_A = 0x01
KEY_CTRL_B = 0x02
KEY_CTRL_C = 0x03
KEY_CTRL_D = 0x04
KEY_CTRL_E = 0x05
KEY_CTRL_F = 0x06
KEY_CTRL_G = 0x07
KEY_BACKSPACE = 0x08
KEY_CTRL_H = 0x08
KEY_TAB = 0x09
KEY_CTRL_I = 0x09
KEY_CTRL_J = 0x0A
KEY_CTRL_K = 0x0B
KEY_CTRL_L = 0x0C
KEY_ENTER = 0x0D
KEY_CTRL_M = 0x0D
KEY_CTRL_N = 0x0E
KEY_CTRL_O = 0x0F
KEY_CTRL_P = 0x10
KEY_CTRL_Q = 0x11
KEY_CTRL_R = 0x12
KEY_CTRL_S = 0x13
KEY_CTRL_T = 0x14
KEY_CTRL_U = 0x15
KEY_CTRL_V = 0x16
KEY_CTRL_W = 0x17
KEY_CTRL_X = 0x18
KEY_CTRL_Y = 0x19
KEY_CTRL_Z = 0x1A


# -- mapped to tdl
KEY_HOME = "HOME"
KEY_END = "END"
KEY_ARROW_UP = "UP"
KEY_ARROW_DOWN = "DOWN"
KEY_ARROW_LEFT = "LEFT"
KEY_ARROW_RIGHT = "RIGHT"
KEY_ESC = "ESCAPE"
# /--


KEY_CTRL_LSQ_BRACKET = 0x1B
KEY_CTRL_3 = 0x1B
KEY_CTRL_4 = 0x1C
KEY_CTRL_BACKSLASH = 0x1C
KEY_CTRL_5 = 0x1D
KEY_CTRL_RSQ_BRACKET = 0x1D
KEY_CTRL_6 = 0x1E
KEY_CTRL_7 = 0x1F
KEY_CTRL_SLASH = 0x1F
KEY_CTRL_UNDERSCORE = 0x1F
KEY_SPACE = 0x20
KEY_BACKSPACE2 = 0x7F
KEY_CTRL_8 = 0x7F

MOD_ALT = 0x01

# attributes ----------------------

# -- mapped to tdl
DEFAULT = Ellipsis

BLACK = 0x000000
RED = 0xFF0000
GREEN = 0x00FF00
YELLOW = 0xFFFF00
BLUE = 0x0000FF
MAGENTA = 0xFF00FF
CYAN = 0x00FFFF
WHITE = 0xFFFFFF
# /--

BOLD = 0x10
UNDERLINE = 0x20
REVERSE = 0x40

# misc ----------------------------

HIDE_CURSOR = -1
INPUT_CURRENT = 0
INPUT_ESC = 1
INPUT_ALT = 2
OUTPUT_CURRENT = 0
OUTPUT_NORMAL = 1
OUTPUT_256 = 2
OUTPUT_216 = 3
OUTPUT_GRAYSCALE = 4


# -- mapped to tdl
EVENT_KEY = "KEYDOWN"
# /--
EVENT_RESIZE = 2
EVENT_MOUSE = 3


class Event:
    """Aggregate for Termbox Event structure"""

    type = None
    ch = None
    key = None
    mod = None
    width = None
    height = None
    mousex = None
    mousey = None

    def gettuple(self):
        return (self.type, self.ch, self.key, self.mod, self.width, self.height, self.mousex, self.mousey)


class Termbox:
    def __init__(self, width=132, height=60):
        global _instance
        if _instance:
            raise TermboxException("It is possible to create only one instance of Termbox")

        try:
            self.console = tdl.init(width, height)
        except tdl.TDLException as e:
            raise TermboxException(e)

        self.e = Event()  # cache for event data

        _instance = self

    def __del__(self):
        self.close()

    def __exit__(self, *args):  # t, value, traceback):
        self.close()

    def __enter__(self):
        return self

    def close(self):
        global _instance
        # tb_shutdown()
        _instance = None
        # TBD, does nothing

    def present(self):
        """Sync state of the internal cell buffer with the terminal."""
        tdl.flush()

    def change_cell(self, x, y, ch, fg, bg):
        """Change cell in position (x;y)."""
        self.console.draw_char(x, y, ch, fg, bg)

    def width(self):
        """Returns width of the terminal screen."""
        return self.console.width

    def height(self):
        """Return height of the terminal screen."""
        return self.console.height

    def clear(self):
        """Clear the internal cell buffer."""
        self.console.clear()

    def set_cursor(self, x, y):
        """Set cursor position to (x;y).

        Set both arguments to HIDE_CURSOR or use 'hide_cursor' function to
        hide it.
        """
        tb_set_cursor(x, y)

    def hide_cursor(self):
        """Hide cursor."""
        tb_set_cursor(-1, -1)

    def select_input_mode(self, mode):
        """Select preferred input mode: INPUT_ESC or INPUT_ALT.

        INPUT_CURRENT returns the selected mode without changing anything.
        """
        return int(tb_select_input_mode(mode))

    def select_output_mode(self, mode):
        """Select preferred output mode: one of OUTPUT_* constants.

        OUTPUT_CURRENT returns the selected mode without changing anything.
        """
        return int(tb_select_output_mode(mode))

    def peek_event(self, timeout=0):
        """Wait for an event up to 'timeout' milliseconds and return it.

        Returns None if there was no event and timeout is expired.
        Returns a tuple otherwise: (type, unicode character, key, mod,
        width, height, mousex, mousey).
        """
        """
        cdef tb_event e
        with self._poll_lock:
            with nogil:
                result = tb_peek_event(&e, timeout)
        assert(result >= 0)
        if result == 0:
            return None
        if e.ch:
            uch = unichr(e.ch)
        else:
            uch = None
        """
        pass  # return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y)

    def poll_event(self):
        """Wait for an event and return it.

        Returns a tuple: (type, unicode character, key, mod, width, height,
        mousex, mousey).
        """
        """
        cdef tb_event e
        with self._poll_lock:
            with nogil:
                result = tb_poll_event(&e)
        assert(result >= 0)
        if e.ch:
            uch = unichr(e.ch)
        else:
            uch = None
        """
        for e in tdl.event.get():
            # [ ] not all events are passed thru
            self.e.type = e.type
            if e.type == "KEYDOWN":
                self.e.key = e.key
                return self.e.gettuple()

        # return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y)

"""
Implementation of Termbox Python API in tdl.

See README.md for details.
"""

import tdl

"""
Implementation status:
 [ ] tdl.init() needs a window, made 132x60
 [ ] Termbox.close() is not implemented, does nothing
 [ ] poll_event needs review, because it does not
     completely follows the original logic
 [ ] peek is stubbed, but not implemented
 [ ] not all keys/events are mapped
"""


class TermboxException(Exception):
    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return self.msg


_instance = None

# keys ----------------------------------
KEY_F1 = 0xFFFF - 0
KEY_F2 = 0xFFFF - 1
KEY_F3 = 0xFFFF - 2
KEY_F4 = 0xFFFF - 3
KEY_F5 = 0xFFFF - 4
KEY_F6 = 0xFFFF - 5
KEY_F7 = 0xFFFF - 6
KEY_F8 = 0xFFFF - 7
KEY_F9 = 0xFFFF - 8
KEY_F10 = 0xFFFF - 9
KEY_F11 = 0xFFFF - 10
KEY_F12 = 0xFFFF - 11
KEY_INSERT = 0xFFFF - 12
KEY_DELETE = 0xFFFF - 13

KEY_PGUP = 0xFFFF - 16
KEY_PGDN = 0xFFFF - 17

KEY_MOUSE_LEFT = 0xFFFF - 22
KEY_MOUSE_RIGHT = 0xFFFF - 23
KEY_MOUSE_MIDDLE = 0xFFFF - 24
KEY_MOUSE_RELEASE = 0xFFFF - 25
KEY_MOUSE_WHEEL_UP = 0xFFFF - 26
KEY_MOUSE_WHEEL_DOWN = 0xFFFF - 27

KEY_CTRL_TILDE = 0x00
KEY_CTRL_2 = 0x00
KEY_CTRL_A = 0x01
KEY_CTRL_B = 0x02
KEY_CTRL_C = 0x03
KEY_CTRL_D = 0x04
KEY_CTRL_E = 0x05
KEY_CTRL_F = 0x06
KEY_CTRL_G = 0x07
KEY_BACKSPACE = 0x08
KEY_CTRL_H = 0x08
KEY_TAB = 0x09
KEY_CTRL_I = 0x09
KEY_CTRL_J = 0x0A
KEY_CTRL_K = 0x0B
KEY_CTRL_L = 0x0C
KEY_ENTER = 0x0D
KEY_CTRL_M = 0x0D
KEY_CTRL_N = 0x0E
KEY_CTRL_O = 0x0F
KEY_CTRL_P = 0x10
KEY_CTRL_Q = 0x11
KEY_CTRL_R = 0x12
KEY_CTRL_S = 0x13
KEY_CTRL_T = 0x14
KEY_CTRL_U = 0x15
KEY_CTRL_V = 0x16
KEY_CTRL_W = 0x17
KEY_CTRL_X = 0x18
KEY_CTRL_Y = 0x19
KEY_CTRL_Z = 0x1A


# -- mapped to tdl
KEY_HOME = "HOME"
KEY_END = "END"
KEY_ARROW_UP = "UP"
KEY_ARROW_DOWN = "DOWN"
KEY_ARROW_LEFT = "LEFT"
KEY_ARROW_RIGHT = "RIGHT"
KEY_ESC = "ESCAPE"
# /--


KEY_CTRL_LSQ_BRACKET = 0x1B
KEY_CTRL_3 = 0x1B
KEY_CTRL_4 = 0x1C
KEY_CTRL_BACKSLASH = 0x1C
KEY_CTRL_5 = 0x1D
KEY_CTRL_RSQ_BRACKET = 0x1D
KEY_CTRL_6 = 0x1E
KEY_CTRL_7 = 0x1F
KEY_CTRL_SLASH = 0x1F
KEY_CTRL_UNDERSCORE = 0x1F
KEY_SPACE = 0x20
KEY_BACKSPACE2 = 0x7F
KEY_CTRL_8 = 0x7F

MOD_ALT = 0x01

# attributes ----------------------

# -- mapped to tdl
DEFAULT = Ellipsis

BLACK = 0x000000
RED = 0xFF0000
GREEN = 0x00FF00
YELLOW = 0xFFFF00
BLUE = 0x0000FF
MAGENTA = 0xFF00FF
CYAN = 0x00FFFF
WHITE = 0xFFFFFF
# /--

BOLD = 0x10
UNDERLINE = 0x20
REVERSE = 0x40

# misc ----------------------------

HIDE_CURSOR = -1
INPUT_CURRENT = 0
INPUT_ESC = 1
INPUT_ALT = 2
OUTPUT_CURRENT = 0
OUTPUT_NORMAL = 1
OUTPUT_256 = 2
OUTPUT_216 = 3
OUTPUT_GRAYSCALE = 4


# -- mapped to tdl
EVENT_KEY = "KEYDOWN"
# /--
EVENT_RESIZE = 2
EVENT_MOUSE = 3


class Event:
    """Aggregate for Termbox Event structure"""

    type = None
    ch = None
    key = None
    mod = None
    width = None
    height = None
    mousex = None
    mousey = None

    def gettuple(self):
        return (self.type, self.ch, self.key, self.mod, self.width, self.height, self.mousex, self.mousey)


class Termbox:
    def __init__(self, width=132, height=60):
        global _instance
        if _instance:
            raise TermboxException("It is possible to create only one instance of Termbox")

        try:
            self.console = tdl.init(width, height)
        except tdl.TDLException as e:
            raise TermboxException(e)

        self.e = Event()  # cache for event data

        _instance = self

    def __del__(self):
        self.close()

    def __exit__(self, *args):  # t, value, traceback):
        self.close()

    def __enter__(self):
        return self

    def close(self):
        global _instance
        # tb_shutdown()
        _instance = None
        # TBD, does nothing

    def present(self):
        """Sync state of the internal cell buffer with the terminal."""
        tdl.flush()

    def change_cell(self, x, y, ch, fg, bg):
        """Change cell in position (x;y)."""
        self.console.draw_char(x, y, ch, fg, bg)

    def width(self):
        """Returns width of the terminal screen."""
        return self.console.width

    def height(self):
        """Return height of the terminal screen."""
        return self.console.height

    def clear(self):
        """Clear the internal cell buffer."""
        self.console.clear()

    def set_cursor(self, x, y):
        """Set cursor position to (x;y).

        Set both arguments to HIDE_CURSOR or use 'hide_cursor' function to
        hide it.
        """
        tb_set_cursor(x, y)

    def hide_cursor(self):
        """Hide cursor."""
        tb_set_cursor(-1, -1)

    def select_input_mode(self, mode):
        """Select preferred input mode: INPUT_ESC or INPUT_ALT.

        INPUT_CURRENT returns the selected mode without changing anything.
        """
        return int(tb_select_input_mode(mode))

    def select_output_mode(self, mode):
        """Select preferred output mode: one of OUTPUT_* constants.

        OUTPUT_CURRENT returns the selected mode without changing anything.
        """
        return int(tb_select_output_mode(mode))

    def peek_event(self, timeout=0):
        """Wait for an event up to 'timeout' milliseconds and return it.

        Returns None if there was no event and timeout is expired.
        Returns a tuple otherwise: (type, unicode character, key, mod,
        width, height, mousex, mousey).
        """
        """
        cdef tb_event e
        with self._poll_lock:
            with nogil:
                result = tb_peek_event(&e, timeout)
        assert(result >= 0)
        if result == 0:
            return None
        if e.ch:
            uch = unichr(e.ch)
        else:
            uch = None
        """
        pass  # return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y)

    def poll_event(self):
        """Wait for an event and return it.

        Returns a tuple: (type, unicode character, key, mod, width, height,
        mousex, mousey).
        """
        """
        cdef tb_event e
        with self._poll_lock:
            with nogil:
                result = tb_poll_event(&e)
        assert(result >= 0)
        if e.ch:
            uch = unichr(e.ch)
        else:
            uch = None
        """
        for e in tdl.event.get():
            # [ ] not all events are passed thru
            self.e.type = e.type
            if e.type == "KEYDOWN":
                self.e.key = e.key
                return self.e.gettuple()

        # return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y)

Implementation of Termbox Python API in tdl.  See README.md for details. Implementation status:  [ ] tdl.init() needs a window, made 132x60  [ ] Termbox.close() is not implemented, does nothing  [ ] poll_event needs review, because it does not      completely follows the original logic  [ ] peek is stubbed, but not implemented  [ ] not all keys/events are mapped # keys ---------------------------------- # -- mapped to tdl # /-- # attributes ---------------------- # -- mapped to tdl # /-- # misc ---------------------------- # -- mapped to tdl # /-- Aggregate for Termbox Event structure # cache for event data # t, value, traceback): # tb_shutdown() # TBD, does nothing Sync state of the internal cell buffer with the terminal. Change cell in position (x;y). Returns width of the terminal screen. Return height of the terminal screen. Clear the internal cell buffer. Set cursor position to (x;y).          Set both arguments to HIDE_CURSOR or use 'hide_cursor' function to         hide it. Hide cursor. Select preferred input mode: INPUT_ESC or INPUT_ALT.          INPUT_CURRENT returns the selected mode without changing anything. Select preferred output mode: one of OUTPUT_* constants.          OUTPUT_CURRENT returns the selected mode without changing anything. Wait for an event up to 'timeout' milliseconds and return it.          Returns None if there was no event and timeout is expired.         Returns a tuple otherwise: (type, unicode character, key, mod,         width, height, mousex, mousey). cdef tb_event e         with self._poll_lock:             with nogil:                 result = tb_peek_event(&e, timeout)         assert(result >= 0)         if result == 0:             return None         if e.ch:             uch = unichr(e.ch)         else:             uch = None # return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y) Wait for an event and return it.          Returns a tuple: (type, unicode character, key, mod, width, height,         mousex, mousey). cdef tb_event e         with self._poll_lock:             with nogil:                 result = tb_poll_event(&e)         assert(result >= 0)         if e.ch:             uch = unichr(e.ch)         else:             uch = None # [ ] not all events are passed thru # return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y)