import curses, argparse, sys, time
import scanner, sql_client

data = {
    "current": 0,
    "registry": []
}

def get_current() -> int:
    """Get current row selected from data collection"""
    return data["current"]
def set_current(new: int):
    """Set current row selected to data collection"""
    data["current"] = new

def get_registry() -> list:
    """Get registry from data collection"""
    return data["registry"]
def set_registry(new):
    """Set registry to data collection"""
    data["registry"] = new

def delete_at(string: str, position: int) -> str:
    """Auxiliary script to delete character at a given position for a given string"""
    return string[:position] + string[position+1:]

def change_name_window(screen:curses.window):
    """Window to change name for chosen registry row"""
    registry = get_registry()
    current_registry = registry[get_current()]
    current_name = registry[get_current()]["host"]
    #Temporarily show cursor
    curses.curs_set(True)
    while True:
        screen.clear()
        cat = f"Changing name for {current_registry["host"]} (ip {current_registry["ip"]})\n"
        cat += f"To: {current_name}"
        screen.addstr(cat)
        key = screen.getkey()
        if key in ["\n", " "]:
            registry[get_current()]["host"] = current_name
            set_registry(registry)
            screen.clear()
            sql_client.update_registry(
                current_registry["id"],
                current_registry["ip"],
                current_name,
                current_registry["last_seen"]
            )
            curses.curs_set(False)
            break
        elif bytes(key, "utf-8") == b"\x08": #Detect control+delete
            current_name = ""
        elif bytes(key, "utf-8") == b'\x1b': #Escape
            curses.curs_set(False)
            break
        elif key == "KEY_BACKSPACE":
            current_name = delete_at(current_name,len(current_name)-1)
        #Register all non-characters to avoid writing it into name
        #Comment if you want to debug what key is being registered
        elif "KEY_" in key or len(key)>1:
            pass
        elif key.isascii():
            current_name += key #+ str(bytes(key, "utf-8"))

def delete(screen:curses.window):
    """Window to delete selected registry row"""
    registry = get_registry() 
    while get_current()>len(registry)-1:
        set_current(get_current()-1)
    current_registry = registry[get_current()]
    max_y, max_x = screen.getmaxyx()
    screen.addstr(max_y-2,0,f"Are you sure you want to delete registry?\n")
    screen.addstr(max_y-1,0,"Press y to accept, any other key to cancel")
    screen.refresh()

    key = screen.getkey()
    if key in ["y", "Y"]:
        sql_client.delete_registry(current_registry["id"])
        time.sleep(0.1)
        set_registry(sql_client.get_registry())
    else:
        return

def reload(screen:curses.window):
    """Rescan lan and store to db, reload data"""
    screen.clear()
    screen.addstr("Scanning please wait")
    screen.refresh()
    scanner.scan_store_db()
    set_registry(sql_client.get_registry())

def main(screen:curses.window):
    """Main TUI window"""

    
    def handle_input(key:int, screen:curses.window):
        if key == curses.KEY_DOWN:
            set_current(min(get_current()+1,len(registry)-1))
        elif key == curses.KEY_UP:
            set_current(max(get_current()-1,0))
        elif key == curses.KEY_PPAGE:
            set_current(max(get_current()-5,0))
        elif key == curses.KEY_NPAGE:
            set_current(min(get_current()+5,len(registry)-1))
        elif key == curses.KEY_END:
            set_current(len(registry)-1)
        elif key == 262: #Beggining key
            set_current(0)
        elif key in [10, 32]: #Space or enter
            change_name_window(screen)
        elif key == ord("r"):
            reload(screen)
        elif key == ord("d"):
            delete(screen)
        elif key == ord("x") or key == 27: #27 is ESCAPE key
            sys.exit()

    max_y, max_x = screen.getmaxyx()
    pad = curses.newpad(1000, max_x-10)

    curses.use_default_colors() #Use terminal colors, thus transparent background
    # Clear screen
    screen.clear()
    #Enable keypad
    screen.keypad(True)

    curses.curs_set(False)
    curses.noecho()
    curses.cbreak()

    pad_height = 2000 #Remember that if number of registry rows exceed this it will error
    pad_width = max_x - 10
    top_padding = 1
    bottom_padding = 3
    visible_height = max_y - bottom_padding
    max_y, max_x = screen.getmaxyx()

    #Main loop
    try:
        
        # Create a large pad
        pad = curses.newpad(pad_height, pad_width)

        while True:
            registry = get_registry()
            # Clear and show instructions
            screen.clear()
            screen.addstr("Welcome to lanwatch: r to rescan, enter to edit hostname, d to delete.")
            screen.refresh() #THIS refresh here is very important to show the pad, it has to be run before pad.refresh
            pad.clear()
            #Make sure there are rows to print
            if len(registry) == 0:
                screen.addstr("No registries found in db, re-scan LAN by pressing r")
                screen.refresh()
            else:
                for i in range(len(registry)):
                    element = registry[i]
                    cat = f"{element["ip"]} ({element["host"]}) - {element["last_seen"]}\n"
                    if i == get_current():
                        cat = "> " + cat
                    pad.addstr(i, 0, cat)
            
                # Show pad portion
                pad.refresh(get_current()-2, 0, top_padding, 2, visible_height, pad_width)
            #Get input
            key = screen.getch()
            handle_input(key, screen)

            #For debug print input
            #screen.addstr(max_y-1,0,f"{key}")
            #screen.refresh()
            #curses.delay_output(100)

    except KeyboardInterrupt:
        sys.exit()


def start_tui():
    """Start principal terminal UI for lanwatch"""
    data["registry"] = sql_client.get_registry()
    #screen = curses.initscr()
    curses.wrapper(main)

def handle_parsing() -> argparse.Namespace:
    """Generate parsing options for script"""
    parser = argparse.ArgumentParser()
    parser.add_argument('--tui', help="Default option (not needed), run terminal based ui", action='store_true')
    parser.add_argument("--background", help="Run scanning in background and store into db", action='store_true')
    parser.add_argument("--clear-db", help="Clear data in database", action='store_true')
    return parser.parse_args()

if __name__ == "__main__":
    args :argparse.Namespace = handle_parsing()
    sql_client.generate_db()
    if args.background:
        try:
            while True:
                scanner.scan_store_db()
                time.sleep(61)
        except KeyboardInterrupt:
            sys.exit()
    elif args.clear_db:
        sql_client.clear_registry_table()
        print("Cleared database")
    else:
        start_tui()

