##  @package ParamFrame
#   Display of camera parameters, e.g. iso, white balance, record/standby, etc.
#
#   @author     Mark Zvilius
#   @date       10-Feb-2020
#   @copyright  Virtual Reality Technology Underwater Limited (VRTUL)
#
import sys
import tkinter as tk
from tkinter import ttk
from tkinter import font

##
#   Create and run the tk-based UI.
#
#   The root window fills the whole screen ("fullscreen" attribute). A frame is created
#   for the video display, but that is purely to reserve screen area for it. Omxplayer
#   does its own thing.
#
#   With the "fullscreen" setting there is no title bar with a close icon to stop
#   the UI. To provide a way of exiting during testing, the Esc key and a mouse-click
#   will close the UI.
#
#   @param  root        Root window
#   @return             Parameter frame.
#   
def run_ui(root):
    SCREEN_HEIGHT = 480
    VIDEO_HEIGHT = 0
    PARAMS_HEIGHT = SCREEN_HEIGHT - VIDEO_HEIGHT

    root.attributes('-fullscreen', True)
    root.configure(background='black')

    video_frame = ttk.Frame(master=root, width=568, height=VIDEO_HEIGHT)
    param_frame = ParamFrame(master=root, width=800, height=PARAMS_HEIGHT)
    ttk.Style().configure('TFrame', background='black')
#    video_frame.grid(row=0, column=0)
    param_frame.grid(row=0, column=0)

    root.bind('<Escape>', lambda ev: root.destroy())
    root.bind('<Button-1>', lambda ev: root.destroy())
    return param_frame

##
#   Display of camera parameters, some of which can be modified by the user.
#
#   Implemented within a ttk frame, using the Grid geometry manager.
#   Designed for 1920x540 area, assuming a 960x540 video window is using the top
#   half of the 1920x1080 screen.
#
#   Example:
#   @code
#       root = tk.Tk()
#       param_display = ParamFrame(root, width=1920, height=540)
#   @endcode
#
class ParamFrame(ttk.Frame):
    ##
    #   Constructor.
    #
    #   @param  self        Object pointer.
    #   @param  master      Containing window.
    #   @param  **kw        Keyword arguments forwarded to ttk.Frame.
    #   
    def __init__(self, master=None, **kw):
        super().__init__(master, **kw)
        self.grid_propagate(False)      # use fixed size window specified by caller
        self.label_font = font.Font(family='DejaVu Sans', size=24)
        self.create_styles()
        self.create_widgets()
        self.layout()

    ##
    #   Create styles used by widgets in the display.
    #
    #   @param  self        Object pointer.
    #
    def create_styles(self):
        HIGHLIGHT_BACKGROUND = '#2020c0'
        s = ttk.Style()
        s.configure('TLabel', background='black', foreground='white', font=self.label_font)
        s.configure('Highlight.TLabel', background=HIGHLIGHT_BACKGROUND)
        s.configure('Standby.TLabel', background='green')
        s.configure('Recording.TLabel', background='red')
        s.configure('Error.TLabel', background='yellow', foreground='black')
        s.configure('NoError.TLabel')

    ##
    #   Create widgets.
    #
    #   @param  self        Object pointer.
    #
    def create_widgets(self):
        self.camera_label = ttk.Label(self)
        self.update_camera('--')
        
        self.wb_label = ttk.Label(self)
        self.update_wb('9999')
        
        self.shutter_label = ttk.Label(self)
        self.update_shutter('1/9999')

        self.fstop_label = ttk.Label(self)
        self.update_fstop('9.9')

        self.iso_label = ttk.Label(self)
        self.update_iso('99999')

        self.enc_label = ttk.Label(self)
        self.update_encoder('XxxXxx YYY ZZ')

        self.fps_label = ttk.Label(self)
        self.update_fps('99.77')

        self.status_label = ttk.Label(self, text='FOOBAR', style='Standby.TLabel')
        self.message_label = ttk.Label(self, text='foobar', style='Error.TLabel')
#        self.rectime_label = ttk.Label(self, text='99:59', style='Time.TLabel')
#        self.camtemp_label = ttk.Label(self, text='99 \N{DEGREE SIGN}C')

    ##
    #   Lay out the widgets in the frame.
    #
    #   @param  self        Object pointer.
    #
    def layout(self):
        # Padding for labels.
        PAD = 10 #10               # padding inside label
        LOFS0 = 0 #300-PAD         # external left padding for column 0
        LOFS1 = 0 #400-PAD         # external left padding for column 1

        # Set minimum sizes for rows and columns.
        self.columnconfigure((0,1), minsize=400)

        # Place the labels in the frame: 6 rows, 2 columns.
        # Row 0: camera id
#        self.camera_label.configure(padding=PAD)
#        self.camera_label.grid(row=0, column=0, columnspan=2)

        # Row 1: iso, wb
        self.iso_label.configure(padding=PAD)
        self.iso_label.grid(row=1, column=0, padx=(LOFS0,0), sticky='w')
        self.wb_label.configure(padding=PAD)
        self.wb_label.grid(row=1, column=1, padx=(LOFS1,0), sticky='e')

        # Row 2: shutter, fps
        self.shutter_label.configure(padding=PAD)
        self.shutter_label.grid(row=2, column=0, padx=(LOFS0,0), sticky='w')
        self.fps_label.configure(padding=PAD)
        self.fps_label.grid(row=2, column=1, padx=(LOFS1,0), sticky='e')

        # Row 3: f-stop, enc
        self.fstop_label.configure(padding=PAD)
        self.fstop_label.grid(row=3, column=0, padx=(LOFS0,0), sticky='w')
        self.enc_label.configure(padding=PAD)
        self.enc_label.grid(row=3, column=1, padx=(LOFS1,0), sticky='e')

        # Row 4: status
        self.status_label.configure(padding=(20,30))  #20,30
        self.status_label.grid(row=4, column=0, columnspan=2)

        # Row 5: message
        self.message_label.grid(row=5, column=0, columnspan=2, sticky='we')
        self.rowconfigure(5, minsize=100)

    ##
    #   Update white-balance field on display.
    #    
    #   @param  self        Object pointer.
    #   @param  new_value   White balance setting.
    #
    def update_wb(self, new_value):
        self.wb_label.configure(text = f'WB {new_value}')

    ##
    #   Update ISO field on display.
    #    
    #   @param  self        Object pointer.
    #   @param  new_value   ISO setting.
    #
    def update_iso(self, new_value):
        self.iso_label.configure(text = f'ISO {new_value}')

    ##
    #   Update shutter-speed field on display.
    #    
    #   @param  self        Object pointer.
    #   @param  new_value   Shutter speed setting.
    #
    def update_shutter(self, new_value):
        self.shutter_label.configure(text = f'SHUTTER {new_value}')

    ##
    #   Update video encoder field on display.
    #    
    #   The encoder value may have spaces represented as '%20'.
    #   This method replaces those strings with ' '.
    #
    #   @param  self        Object pointer.
    #   @param  new_value   Video encoder setting.
    #
    def update_encoder(self, new_value):
        encoder = new_value.replace('%20',' ')
        self.enc_label.configure(text = f'{encoder}')


    ##
    #   Update f-stop (iris) field on display.
    #    
    #   @param  self        Object pointer.
    #   @param  new_value   F-stop setting.
    #
    def update_fstop(self, new_value):
        self.fstop_label.configure(text = f'F {new_value}')

    ##
    #   Update frames/sec on display.
    #
    #   @param  self        Object pointer.
    #   @param  new_value   Frames/sec setting.
    #
    def update_fps(self, new_value):
        self.fps_label.configure(text = f'FPS {new_value}')

    ##
    #   Update standby/recording on display.
    #
    #   @param  self        Object pointer.
    #   @param  new_value   True -> recording, False -> standby.
    #
    def update_recording(self, is_recording):
        if is_recording:
            self.status_label.configure(text='RECORDING', style='Recording.TLabel')
        else:
            self.status_label.configure(text='STANDBY', style='Standby.TLabel')

    ##
    #   Update camera ID on display.
    #
    #   @param  self        Object pointer.
    #   @param  new_value   ID of camera on video display.
    #
    def update_camera(self, camera_id):
        self.camera_label.configure(text = f'Cam {camera_id}')

    ##
    #   Set error message on display.
    #
    #   @param  self        Object pointer.
    #   @param  msg         Error message.
    #
    def update_errmsg(self, msg):
        self.message_label.configure(text=msg, style='Error.TLabel')

    ##
    #   Clear error message on display.
    #
    #   @param  self        Object pointer.
    #
    def clear_errmsg(self):
        self.message_label.configure(text=' ', style='NoError.TLabel')


def test_update1(frame):
    frame.update_camera(0)
    frame.update_wb('6500')
    frame.update_iso('16000')
    frame.update_shutter('1/200')
    frame.update_encoder('ProRes%20422%20LT')
    frame.update_fstop('2.8')
    frame.update_fps('59.97')
    frame.update_recording(True)
    frame.update_errmsg('ERROR: cam99 foobar')
    frame.master.after(3000, test_update2, frame)

def test_update2(frame):
    frame.update_recording(False)
    frame.clear_errmsg()
    frame.master.after(3000, test_update1, frame)

if __name__ == '__main__':
    root = tk.Tk()
    root.configure(background='#661111')
    frame = run_ui(root)

    root.after(3000, test_update1, frame)
    frame.mainloop()
