# text10 plain text editor for card10
# Written by LuKaRo, public@lrose.de
# License: cc-by-nc-sa 4.0

import display
import utime
import buttons
import color
import simple_menu
import os

class MainMenu(simple_menu.Menu):
    color_1 = color.CAMPGREEN
    color_2 = color.CAMPGREEN_DARK

    def on_select(self, name, index):
        global text
        global menucountdown
        global insert_mode
        print("{!r} was selected!".format(name))
        if name == "Load":
            FileMenu("/", "read").run()
        if name == "Save":
            FileMenu("/", "write").run()
        if name == "New File":
            text = ""
            menucountdown = MAX_MENU_COUNTDOWN
            main()
        if name == "Insert":
            insert_mode = True
            menucountdown = MAX_MENU_COUNTDOWN
            main()
        if name == "Replace":
            insert_mode = False
            menucountdown = MAX_MENU_COUNTDOWN
            main()

class FileMenu(simple_menu.Menu):
    color_1 = color.CAMPGREEN
    color_2 = color.CAMPGREEN_DARK
    path = "/"
    access_type = "read"

    def __init__(self, path, access_type):
        filelist = os.listdir(path)
        filelist.append("..")
        filelist.sort()
        super().__init__(filelist)
        self.path = path
        self.access_type = access_type

    def on_select(self, name, index):
        global text
        global menucountdown
        print("{!r} was selected!".format(name))
        if name == "..":
            oldpath = self.path.split("/")
            newpath = "/".join(oldpath[:-1])
        else:
            newpath = self.path + "/" + name
        try:
            FileMenu(newpath, self.access_type).run()
        except:
            print("Couldn't load file menu for " + newpath)
            if self.access_type == "read":
                try:
                    with open(newpath, "r") as file:
                        text = file.read()
                    menucountdown = MAX_MENU_COUNTDOWN
                    main()
                except Exception as e:
                    print(e)
                    draw_error(disp, "Can't read file: " + newpath)
            elif self.access_type == "write":
                try:
                    with open(newpath, "w") as file:
                        file.write(text)
                    draw_info(disp, "Saved!")
                    utime.sleep(1)
                    menucountdown = MAX_MENU_COUNTDOWN
                    main()
                except Exception as e:
                    print(e)
                    draw_error(disp, "Can't save file: " + newpath)

alphabet = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,-_0123456789:;!\"#$%&'()*+/<=>?[\]^{|}~"

DISPLAY_SIZE = 44
MAX_MENU_COUNTDOWN = 5
menucountdown = 0
blink = False
text = ""
disp = display.Display()
insert_mode = False

def str_replace(string, pos, char, insert_mode = False):
    return (string[:pos] if insert_mode else string[:pos - 1]) + char + string[pos:]

def draw_menu(disp, text = "", pos = 0, alphabet_idx = -1):
    global blink
    global menucountdown
    global insert_mode
    disp.clear()

    if menucountdown == 0:
        MainMenu(["Load", "Save", "New File", "Replace" if insert_mode else "Insert"]).run()
    elif (menucountdown < MAX_MENU_COUNTDOWN):
        disp.print(" Going to    Menu in       " + str(menucountdown))
    else:
        if blink == True:
            if alphabet_idx < 0:
                text = str_replace(text, pos, "_", insert_mode);
            else:
                text = str_replace(text, pos, alphabet[alphabet_idx], insert_mode)
            blink = False
        else:
            blink = True
            if (insert_mode):
                text = str_replace(text, pos, " ", insert_mode);
        textlength = len(text)
        if textlength > DISPLAY_SIZE:
            # If Text is larger than display, display cursor in the middle if possible.
            chars_right_of_cursor = textlength - pos
            # At Maximum half the screen of characters right the cursor
            if (chars_right_of_cursor > DISPLAY_SIZE / 2):
                chars_right_of_cursor = int(DISPLAY_SIZE / 2)
            # Fill screen with characters left of cursor
            chars_left_of_cursor = DISPLAY_SIZE - chars_right_of_cursor - 1
            if (chars_left_of_cursor > pos):
                # If not enough characters left of cursor available, use as many as possible
                chars_left_of_cursor = pos
                # Fill Screen with characters right of cursor then
                chars_right_of_cursor = DISPLAY_SIZE - chars_left_of_cursor - 1
                if (chars_right_of_cursor > textlength - pos):
                    # If also not enough characters right of cursor available, use as many as possible
                    chars_right_of_cursor = textlength - pos
            # Trim text accordingly
            text = text[pos - chars_left_of_cursor:pos + chars_right_of_cursor]
            
        disp.print(text)
    
    disp.update()

def draw_error(disp, error):
    disp.clear()
    disp.print(error)
    disp.update()

def draw_info(disp, error):
    disp.clear()
    disp.print(error)
    disp.update()

def draw_welcome(disp):
    disp.clear()
    disp.print("   text10      v2        loading   by LuKaRo ")
    disp.update()
    utime.sleep_ms(500)
    
def main():
    global menucountdown
    global text
    global insert_mode
    draw_welcome(disp)
    pos = 1
    length = 0
    alphabet_idx = -1
    press_and_hold = False;
    event = 0
    oldev = 0

    while True:
        event = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT)
        # If the button pressed stays the same during two subsequent iterations, user most likely kept the button pressed.
        if oldev == event and event != 0:
            press_and_hold = True;
        else:
            press_and_hold = False;
            oldev = event;

        if event == (buttons.TOP_RIGHT | buttons.BOTTOM_LEFT):
            menucountdown -= 1 if menucountdown > 0 else 0
        
        elif menucountdown == MAX_MENU_COUNTDOWN:
            if event == buttons.TOP_RIGHT:
                # Scroll down
                alphabet_idx += 1
                alphabet_idx %= len(alphabet)

            elif event == buttons.BOTTOM_LEFT:
                # Scroll up
                alphabet_idx = -1
                pos = pos - 1 if pos > 0 else 0

            elif event == buttons.BOTTOM_RIGHT:
                # If no char is selected and we're at the and of the string
                if alphabet_idx >= 0:
                    text = str_replace(text, pos, alphabet[alphabet_idx], insert_mode)
                    alphabet_idx = -1
                
                pos += 1
            
        elif menucountdown != 0:
            menucountdown += 1

        print(pos)
        draw_menu(disp, text, pos, alphabet_idx)

        # Only wait for next iteration if user didn't keep the button pressed.
        if press_and_hold:
            utime.sleep_ms(30)
        else:
            utime.sleep_ms(150)

if __name__ == "__main__":
    main()
