"""
WelcomeScreen: A simple library providing a customizable welcome screen fading over an LCD 
Copyright (C) 2024  Benjamin Burkhardt

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

from time import sleep


class WelcomeScreen:
    # __init__() - the constructor
    # lcd: an object of I2C_LCD (from the PCF8574T library - https://git.privacynerd.de/BlueFox/micropython-libraries/src/branch/main/PCF8574T)
    # ---
    # interrupt_pins: a list containing machine.Pin objects exclusively (but if you give these you can still turn interrupting off
    # via a parameter in the show method (see below!)
    # ---
    # subtitle: the text shown below the cycling text (e.g. the device's name, ...) (Lorem ipsum. by default)
    # ---
    # starting_msg: the text shown while cycling (default: Starting...)
    # ---
    # started_msg: the text shown while cycling (default: Started!)
    def __init__(self, lcd, interrupt_pins=None, subtitle="Lorem ipsum.", starting_msg="Starting...", started_msg="Started!"):
        self.lcd = lcd
        self.columns = self.lcd.num_columns
        self.lines = self.lcd.num_lines
        
        self.interrupt_pins = interrupt_pins
        
        self.subtitle = subtitle
        self.starting_msg = starting_msg
        self.started_msg = started_msg


    # show() - Display the actual message
    # ---
    # cycles says how often the Starting text goes through
    # ---
    # wait_after_cycles is a number in seconds (can be float) defining how long to wait before returning OR
    # if fading out is activated, the time to wait between end of the cycling animation and the fade out animation
    # ---
    # fade_down is a dict with following keys:
    # - "enabled"      - REQUIRED - wether fading is enabled (default: true)
    # - "wait_between" - OPTIONAL - the time in seconds to wait between each line fade
    # - "wait_after"   - OPTIONAL - the time in seconds to wait after the fade out
    # ---
    # interruptable influences wether the program can be interrupted by pins in the self.interrupt_pins list
    # if this list is empty, even when interruptable is set to true, nothing will be able to interrupt!
    def show(self, cycles=1, wait_after_cycles=1, fade_down={"enabled": True}, interruptable=True):  
        if cycles < 1: cycles = 1
        padding = " " * self.columns  # as much spaces as padding as one display line is long
        padding_hyphen = "-" * self.columns  # as much hyphens as padding as one display line is long
                
        # mechanism for centering on a 4*20 display
        y_offset = 0
        if self.lines == 4:
            y_offset = 1
            # also clear the top and bottom with ----
            self.lcd.move_to(0,0)
            self.lcd.putstr(padding_hyphen)
            self.lcd.move_to(0,4)
            self.lcd.putstr(padding_hyphen)
        
        # get the current pin values (only if there are pins specified) (when something changes, the interrupt happens and the cycle stops)
        break_flag = False
        if self.interrupt_pins:
            pin_values = []
            for p in self.interrupt_pins:
                pin_values.append(p.value())
        # cycle the text 'cycles' times and listen for changes on interrupt pins (if any given)
        for i in range(cycles):
            line1 = padding + self.starting_msg + padding
            line2 = self.subtitle.center(self.columns)
            for i in range(self.columns + len(self.starting_msg)):
                self.lcd.move_to(0,y_offset)
                self.lcd.putstr(line1[0:self.columns])
                self.lcd.move_to(0,y_offset+1)
                self.lcd.putstr(line2[0:self.columns])
                line1 = line1[1:]
                if self.interrupt_pins:
                    for i, p in enumerate(self.interrupt_pins):
                        if pin_values[i] != p.value():
                            break_flag = True
                if break_flag:
                    break
            if break_flag:
                break
        
        self.lcd.move_to(0,y_offset)
        self.lcd.putstr(self.started_msg.center(16))
        
        sleep(wait_after_cycles)
        
        # now fade down if enabled via the params
        if fade_down["enabled"]:
            # get all the waiting times
            if "wait_between" in fade_down.keys():
                wait_between = fade_down["wait_between"]
            else:
                wait_between = 0.1
            if "wait_after" in fade_down.keys():
                wait_after = fade_down["wait_after"]
            else:
                wait_after = 0.3
            if self.lines == 4:
                old_display = padding_hyphen + self.started_msg.center(16) + self.subtitle.center(self.columns) + padding_hyphen
            else:
                old_display = self.started_msg.center(16) + self.subtitle.center(self.columns)
            self.lcd.move_to(0,0)  # move to the start of the lcd
            while old_display != "":
                old_display = old_display[:-self.columns]
                lines_before = " " * (self.columns*self.lines-len(old_display))
                self.lcd.putstr(lines_before + old_display)
                sleep(wait_between)
            sleep(wait_after)