import curses
import curses.ascii
import curses.textpad
from collections import namedtuple
from enum import Enum

DATA = [
    "Antony and Cleopatra",
    "Hamlet",
    "Julius Caesar",
    "King Lear",
    "Macbeth",
    "The Merchant of Venice",
    "A Midsummer Night's Dream",
    "Much Ado About Nothing",
    "Othello",
    "Romeo and Juliet",
    "The Tempest",
    "Titus Andronicus",
]

Event = namedtuple("Event", ["type", "char"])
EventType = Enum("EventType", ["BACKSPACE", "CHAR", "IGNORE", "QUIT"])


class ResultsBox:
    def __init__(self, parent, y, x, height, width):
        self.win = parent.subwin(height, width, y, x)
        self.height = height
        self.width = width

        self.win.addstr(0, 0, "<Start typing a query to display matching titles>")

    def write(self, data):
        self.win.erase()
        for line, text in enumerate(data[: self.height]):
            self.win.addstr(line, 0, text[: self.width])
        self.win.refresh()


class SearchBox:
    def __init__(self, parent, y, x, height, width):
        self.win = parent.subwin(height, width, y, x)
        self.width = width
        self.contents = ""

    @property
    def cursor_position(self):
        return self.win.getyx()[1]

    def append(self, char):
        self.contents += chr(char)
        if self.cursor_position < self.width - 1:
            self.win.echochar(char)
        self.win.cursyncup()

    def backspace(self):
        self.contents = self.contents[:-1]
        if self.cursor_position > 0:
            self.win.delch(0, self.cursor_position - 1)
            self.win.refresh()
        self.win.cursyncup()


class Window:
    def __init__(self, window):
        self.closed = False
        self.win = window
        self.search_box = SearchBox(self.win, y=4, x=2, height=1, width=30)
        self.results_box = ResultsBox(self.win, y=8, x=2, height=10, width=50)

        self.win.addstr(1, 2, "ebk-find v0.1.0")
        curses.textpad.rectangle(self.win, 3, 1, 5, 32)
        curses.textpad.rectangle(self.win, 7, 1, 18, 52)
        self.win.addstr(20, 2, "Type Ctrl-C to quit")

        self.win.move(4, 2)

    def handle_next(self):
        event = self.wait_for_input()

        if event.type is EventType.QUIT:
            self.closed = True
        elif event.type is EventType.BACKSPACE:
            self.search_box.backspace()
            data = self.filter(DATA)
            self.results_box.write(data)
        elif event.type is EventType.CHAR:
            self.search_box.append(event.char)
            data = self.filter(DATA)
            self.results_box.write(data)
        else:
            assert event.type is EventType.IGNORE, event.type.name

    def wait_for_input(self):
        ch = self.win.getch()

        if curses.ascii.iscntrl(ch):
            return Event(EventType.QUIT, None)
        elif ch in {curses.KEY_BACKSPACE, curses.KEY_DC}:
            return Event(EventType.BACKSPACE, ch)
        elif curses.ascii.isprint(ch):
            return Event(EventType.CHAR, ch)
        else:
            return Event(EventType.IGNORE, ch)

    def filter(self, data):
        query = self.search_box.contents
        return [l for l in data if query.lower() in l.lower()]


def loop(stdscr):
    window = Window(stdscr)

    while True:
        window.handle_next()

        if window.closed:
            break


if __name__ == "__main__":
    try:
        curses.wrapper(loop)
    except KeyboardInterrupt:
        raise SystemExit
