# Python packages
import threading
import os
import platform
import logging
import time
from datetime import datetime

# External packages (installed via pip)
import wx
import wx.lib.scrolledpanel
import wx.richtext
import wx.lib.newevent
import wx.aui
import wx.adv
import cv2
import numpy as np

# Own packages in src
from .__init__ import __version__
from .__init__ import __author__

# using logging module
logger = logging.getLogger('webcam')
logger.setLevel(logging.DEBUG)

# create file handler
fh = logging.FileHandler('webcam.log')
fh.setLevel(logging.INFO)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s.%(msecs)03d [%(levelname)s]: %(message)s',
                              "%Y-%m-%d %H:%M:%S")
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# add the handlers to the logger
logger.addHandler(fh)
logger.addHandler(ch)

# GUI properties
_WINDOW_NAME = "wxWebcam"
_WEB_PAGE = "https://discuss.wxpython.org/t/howto-deploy-and-distribute-a-wxpython-app-with-briefcase/35508"


class Program(threading.Thread):
    """
    this class covers the frame grabber implementation with opencv
    """

    evt_initialisation_finished, EVT_INITIALISATION_FINISHED = wx.lib.newevent.NewEvent()
    evt_update_frame, EVT_UPDATE_FRAME = wx.lib.newevent.NewEvent()
    evt_update_acquisition_info, EVT_UPDATE_ACQUISITION_INFO = wx.lib.newevent.NewEvent()

    def __init__(self, parent=None):
        threading.Thread.__init__(self)
        """
        :return: None
        """

        self.__parent = parent

        # must be daemon. always.
        self.daemon = True

        # properties of program which can be accessed
        # camera
        self.cam = None

        # image width/height
        self.image_width = None
        self.image_height = None

        # holding the current image
        self.image = None

        # saving path of image results
        self.__std_results_folder = os.path.join(os.path.expanduser("~"), 'webcam_results')
        self.results_folder = self.__std_results_folder

        # seconds per Frame
        self.seconds_per_frame = 0.0

        # Event to stop thread
        self.__stop_event = threading.Event()

    def __initialize(self) -> bool:
        """
        __initializes camera
        :return bool: True if camera is successful initialized, else false
        """

        if self.cam is None:

            self.cam = cv2.VideoCapture()
            if platform.system().upper().find('windows'):
                self.cam.open(0, cv2.CAP_DSHOW)
            else:
                self.cam.open(0)

            if self.cam.isOpened():
                # try to set highest resolution
                self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1900)
                self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 1200)
                logger.info("Webcam have been found.")
                logger.info("Webcam resolution: " +
                            str(self.cam.get(cv2.CAP_PROP_FRAME_WIDTH)) + "x" +
                            str(self.cam.get(cv2.CAP_PROP_FRAME_HEIGHT)))
            else:
                logger.error("No webcam has been found.")

        # Image information
        status = False
        if self.cam is not None:
            status, image = self.cam.read()
            if status:
                # image shape
                self.image_width = image.shape[1]
                self.image_height = image.shape[0]

        return status

    def run(self) -> None:
        """
        main process of the program with catching the images and process it
        :return None:
        """

        is_initialized = self.__initialize()
        event = self.evt_initialisation_finished(attr1=is_initialized)
        wx.PostEvent(self.__parent, event)

        timing_start = time.time()

        try:
            while not self.stopped():

                ret, image = self.cam.read()

                if ret is False:
                    image = None

                if image is not None:
                    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

                    self.image = np.copy(image)

                    self.seconds_per_frame = (time.time() - timing_start + np.finfo(float).eps)
                    wx.PostEvent(self.__parent,
                                 self.evt_update_frame(attr1=self.__to_3_d(self.image)))

                    wx.PostEvent(self.__parent,
                                 self.evt_update_acquisition_info(attr1=self.seconds_per_frame))
                    timing_start = time.time()

        except Exception as e:
            logger.error(e, exc_info=True)

        self.cam.release()

    def save_phase_image_result(self) -> str:
        """
        Saving the phase_image_result of sps class with a watermark logo.

        :return folder_file_name: full path with name of the saved image
        """
        if self.results_folder == self.__std_results_folder:
            # create folder structure
            os.makedirs(self.results_folder, exist_ok=True)

        folder = self.results_folder
        image = np.copy(self.image)

        file_name = '.png'

        # image or result:
        kind = 'photo_'

        # save image with
        date_time = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3]

        folder_file_name = os.path.join(folder, kind + date_time + file_name)
        cv2.imwrite(folder_file_name, cv2.cvtColor(image, cv2.COLOR_RGB2BGR))

        return folder_file_name

    def stop(self):
        self.__stop_event.set()

    def stopped(self):
        return self.__stop_event.is_set()

    @staticmethod
    def __to_3_d(frame: np.ndarray) -> wx.Bitmap:
        """
        converts a 2D or 3D numpy array into wx.Bitmap
        :param frame: 2D or 3D numpy array
        :return wx_bitmap:
        """

        # to 3D array if necessary
        if frame.ndim != 3:
            frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2RGB)

        return frame


class Webcam(wx.Frame):
    """
    Webcam
    A small application to look at yourself and take an image!
    """

    def __init__(self, parent):

        wx.Frame.__init__(self, parent, title=_WINDOW_NAME)

        # here is the main program containing the webcam
        self.program = Program(parent=self)

        # initialize locale settings in wx python | bug in wxpython 4.1.1 in windows10
        # see: https://discuss.wxpython.org/t/wxpython4-1-1-python3-8-locale-wxassertionerror/35168/4
        self.init_locale()

        # for high resolution displays
        self.SetClientSize(self.FromDIP(wx.Size(640, 480)))

        # Set Minimum Size
        self.SetMinSize(self.FromDIP(wx.Size(640, 480)))

        # set Icon of this GUI
        self.__icon = wx.Icon(os.path.join(os.path.abspath(os.path.dirname(__file__)), "resources", "wxWebcam.ico"))
        self.SetIcon(self.__icon)

        # list of key shortcuts in this  application
        key_shortcuts = []

        # toolbar icon width
        _icon_width = _icon_height = self.FromDIP(wx.Size(24, 24)).width

        # licensing information window
        self.lcs_dlg = None

        # AUI manager
        self.manager = wx.aui.AuiManager()
        # tell Aui Manager to manage this frame
        self.manager.SetManagedWindow(self)

        # bitmap for the main panel
        self.__main_image = None

        # set main panel in this gui, this shall show the live image / resulting image
        self.main_panel = wx.Panel(self, wx.ID_ANY)
        self.main_panel.SetBackgroundStyle(wx.BG_STYLE_PAINT)
        self.main_panel.Bind(wx.EVT_PAINT, self.paint_main_panel)
        self.manager.AddPane(self.main_panel, wx.aui.AuiPaneInfo().Name("Main View").CenterPane())

        # status bar
        self.status_bar = self.CreateStatusBar()
        self.status_bar.SetFieldsCount(number=2, widths=[-1, 175])

        # menu_bar
        self.menu_bar = wx.MenuBar()

        self.view_menu = wx.Menu()
        self.help_menu = wx.Menu()
        self.SetMenuBar(self.menu_bar)

        self.menu_bar.Append(self.view_menu, "&View")
        self.menu_bar.Append(self.help_menu, "&Help")

        # View Menu
        self.enter_full_screen_tool = wx.MenuItem(self.view_menu, wx.ID_ANY,
                                                  "full screen mode\tF11",
                                                  "full screen mode\tshortcut: F11",
                                                  kind=wx.ITEM_CHECK)
        self.view_menu.Append(self.enter_full_screen_tool)
        self.view_menu.Check(self.enter_full_screen_tool.GetId(), False)
        self.Bind(wx.EVT_MENU, self.enter_full_screen, self.enter_full_screen_tool,
                  id=self.enter_full_screen_tool.GetId())
        key_shortcuts.append([wx.ACCEL_NORMAL, wx.WXK_F11, self.enter_full_screen_tool.GetId()])

        # wx.ID_INFO and wx.ID_ABOUT are standard IDs provided by wxWidgets.
        self.__help_licensing = self.help_menu.Append(wx.ID_INFO, "&Licensing Information", "Licensing Information")
        self.__help_information = self.help_menu.Append(wx.ID_ABOUT, "&About", " Information about wxWebcam")

        # aui toolbar
        self.tool_bar = wx.aui.AuiToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize)
        self.tool_bar.SetToolBitmapSize(self.FromDIP(wx.Size(24, 24)))
        self.tool_bar.SetToolPacking(5)

        reconnect_icon = wx.ArtProvider.GetBitmap(wx.ART_REDO, wx.ART_MENU)
        self.reconnect_tool = self.tool_bar.AddTool(wx.ID_ANY, reconnect_icon, wx.NullBitmap)
        self.reconnect_tool.SetShortHelp("reconnect to camera\nshortcut: R")
        self.reconnect_tool.SetLongHelp("reconnect to camera\tshortcut: R")
        self.Bind(wx.EVT_TOOL, self.reconnect, self.reconnect_tool, id=self.reconnect_tool.GetId())
        key_shortcuts.append([wx.ACCEL_NORMAL, ord('R'), self.reconnect_tool.GetId()])

        self.tool_bar.AddSeparator()

        folder_icon = wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN, wx.ART_MENU)
        self.set_saving_path_tool = self.tool_bar.AddTool(wx.ID_ANY, '&saving directory\tS', folder_icon)
        self.set_saving_path_tool.SetShortHelp("set saving directory\nshortcut: S")
        self.set_saving_path_tool.SetLongHelp("set saving directory\tshortcut: S")
        self.Bind(wx.EVT_TOOL, self.set_saving_path, self.set_saving_path_tool, id=self.set_saving_path_tool.GetId())
        key_shortcuts.append([wx.ACCEL_NORMAL, ord('S'), self.set_saving_path_tool.GetId()])

        self.tool_bar.AddSeparator()

        save_icon = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_MENU)
        self.save_image_tool = self.tool_bar.AddTool(wx.ID_ANY, "&save an image\tENTER", save_icon)
        self.save_image_tool.SetShortHelp("save an image\nshortcut: ENTER")
        self.save_image_tool.SetLongHelp("save an image \t shortcut: ENTER")
        self.Bind(wx.EVT_TOOL, self.save_image, self.save_image_tool, id=self.save_image_tool.GetId())
        key_shortcuts.append([wx.ACCEL_NORMAL, wx.WXK_RETURN, self.save_image_tool.GetId()])

        self.tool_bar.AddSeparator()

        # realize and add the toolbar to the aui manager
        self.tool_bar.Realize()
        self.manager.AddPane(self.tool_bar,
                             wx.aui.AuiPaneInfo().Name("tool_bar").Caption("Control").ToolbarPane().Top())

        # "commit" all changes made to AuiManager
        self.manager.Update()

        accel = wx.AcceleratorTable(key_shortcuts)
        self.main_panel.SetAcceleratorTable(accel)

        # Bindings of menu_bar
        self.Bind(wx.EVT_CLOSE, self.close_event)
        self.Bind(wx.EVT_MENU, self.license_event, self.__help_licensing)
        self.Bind(wx.EVT_MENU, self.info_event, self.__help_information)

        self.tool_bar.EnableTool(self.reconnect_tool.GetId(), False)
        self.tool_bar.EnableTool(self.save_image_tool.GetId(), False)
        self.tool_bar.EnableTool(self.set_saving_path_tool.GetId(), False)

        # connect to Program
        self.Bind(self.program.EVT_INITIALISATION_FINISHED, self.initialisation_finished)
        self.Bind(self.program.EVT_UPDATE_FRAME, self.update_frame)
        self.Bind(self.program.EVT_UPDATE_ACQUISITION_INFO, self.update_acquisition_info_label)

        self.program.start()
        message = "Welcome to " + _WINDOW_NAME + ". Initializing..."
        logger.info(message)
        self.SetStatusText(message)

        self.Show(True)

    @staticmethod
    def init_locale():
        """
        to fix a bug in wxPyton 4.1.1 in Python 3.8 in Windows 10
        See also: https://discuss.wxpython.org/t/wxpython4-1-1-python3-8-locale-wxassertionerror/35168/3
        :return None:
        """
        import sys
        if sys.platform.startswith('win') and sys.version_info > (3, 8):
            import locale
            locale.setlocale(locale.LC_ALL, "C")

    def reconnect(self, event: wx.lib.newevent.NewEvent) -> None:
        """
        restarts the thread of class Program which leads to a reconnection to the camera
        :return:
        """
        # waiting cursor
        self.main_panel.SetCursor(wx.Cursor(wx.CURSOR_WAIT))

        # disable ui elements
        self.tool_bar.EnableTool(self.reconnect_tool.GetId(), False)
        self.tool_bar.EnableTool(self.save_image_tool.GetId(), False)
        self.tool_bar.EnableTool(self.set_saving_path_tool.GetId(), False)

        # show status message
        message = "Reconnect to camera..."
        logger.info(message)
        self.status_bar.SetStatusText(message)

        # stop program thread
        self.program.stop()

        # wait 5 seconds
        self.program.join(5.0)
        if self.program.is_alive():
            logger.error("Could not de-initialize camera.")
            try:
                self.program.stop()
            except Exception as e:
                logger.error(e, exc_info=True)

        # Delete and initialize again
        del self.program
        self.program = Program(parent=self)

        # Bind program again
        self.Bind(self.program.EVT_INITIALISATION_FINISHED, self.initialisation_finished)
        self.Bind(self.program.EVT_UPDATE_FRAME, self.update_frame)
        self.Bind(self.program.EVT_UPDATE_ACQUISITION_INFO, self.update_acquisition_info_label)

        # open program thread again
        self.program.start()

    def initialisation_finished(self, event: wx.lib.newevent.NewEvent) -> None:
        """
        if initialisation finished, call this function to inform user
        :event: if True, initialisation was successful, else not
        :return: None
        """
        if event:
            message = "Initialisation done."
        else:
            message = "Initialisation failed - please take a look into the log file."
        logger.info(message)
        self.status_bar.SetStatusText(message)

        # set cursor back to arrow cursor
        self.main_panel.SetCursor(wx.Cursor(wx.CURSOR_ARROW))

        # enable ui elements
        self.tool_bar.EnableTool(self.reconnect_tool.GetId(), True)
        self.tool_bar.EnableTool(self.save_image_tool.GetId(), True)
        self.tool_bar.EnableTool(self.set_saving_path_tool.GetId(), True)
        self.tool_bar.Refresh()

    def set_saving_path(self, event) -> None:
        """
        sets the saving path of images
        :return: None
        """
        dlg = wx.DirDialog(self, "Choose saving directory", self.program.results_folder,
                           wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST)
        dlg.ShowModal()
        folder = dlg.GetPath()

        if folder:
            self.program.results_folder = os.path.abspath(folder)
            message = "Changed saving folder directory to: " + self.program.results_folder
            logger.info(message)
            self.status_bar.SetStatusText(message)

    def save_image(self, event: wx.lib.newevent.NewEvent) -> None:
        """
        saving an image
        :return: None
        """
        # save image
        image_name = self.program.save_phase_image_result()
        name = image_name.split(os.path.sep)
        image_name = name[-1]
        folder_name = os.path.sep.join(name[:-1])

        message = "Saved phase image " + image_name + " in path: " + folder_name
        logger.info(message)
        self.status_bar.SetStatusText(message)

    def enter_full_screen(self, event: wx.lib.newevent.NewEvent) -> None:
        """
        turn on/off Full screen Mode
        :signal: if True, button is pressed, else False
        :return: None
        """
        message = ""
        if isinstance(event.GetEventObject(), wx.Menu):
            if self.enter_full_screen_tool.IsChecked():
                self.ShowFullScreen(True, wx.FULLSCREEN_NOBORDER)
                message = "Changed to full screen mode."
            else:
                self.ShowFullScreen(False)
                message = "Changed to windowed mode."
        elif isinstance(event.GetEventObject(), wx.Panel):
            if self.enter_full_screen_tool.IsChecked():
                self.ShowFullScreen(False)
                self.view_menu.Check(self.enter_full_screen_tool.GetId(), False)
                message = "Changed to windowed mode."
            else:
                self.ShowFullScreen(True, wx.FULLSCREEN_NOBORDER)
                self.view_menu.Check(self.enter_full_screen_tool.GetId(), True)
                message = "Changed to full screen mode."

        logger.info(message)
        self.SetStatusText(message)

    def update_frame(self, event) -> None:
        """
        update central widget image_label
        :param event:
        :return None:
        """

        # get width and height of current image_label
        self.__main_image = event.attr1

        self.main_panel.Refresh()

    def paint_main_panel(self, event) -> None:
        """
        Paint event of main_panel
        :param event:
        :return:
        """

        if self.__main_image is None:
            return

        image = self.__main_image

        # get height and width of panel
        panel_size = self.main_panel.GetSize()

        # get the scaling factor - aspect ratio has to be remain
        scale_width = float(image.shape[1]) / float(panel_size.width)
        scale_height = float(image.shape[0]) / float(panel_size.height)
        scale = scale_width if scale_width > scale_height else scale_height

        new_image_width = int(image.shape[1] / scale)
        new_image_height = int(image.shape[0] / scale)
        new_image = cv2.resize(image, (new_image_width, new_image_height), interpolation=cv2.INTER_LINEAR)
        offset_width = max([0, int((panel_size.width - new_image_width) / 2)])
        offset_height = max([0, int((panel_size.height - new_image_height) / 2)])

        wx_image = wx.Image(new_image.shape[1], new_image.shape[0])
        wx_image.SetData(new_image.tobytes())
        wx_bitmap = wx_image.ConvertToBitmap()
        dc = wx.AutoBufferedPaintDC(self.main_panel)
        dc.Clear()
        dc.DrawBitmap(wx_bitmap, offset_width, offset_height)

    def update_acquisition_info_label(self, event) -> None:
        """
        updates fps_label
        :param info: list conting [seconds per frame, exposure time]
        :return None:
        """
        seconds_per_frame = event.attr1
        self.status_bar.SetStatusText("%i fps | %3.3f seconds per frame" %
                                      (int(1.0 / seconds_per_frame), seconds_per_frame), i=1)

    def license_event(self, event) -> None:
        """
        opens licensing information
        :return:
        """
        if isinstance(self.lcs_dlg, LicensingFrame):
            if self.lcs_dlg.closed:
                self.lcs_dlg = LicensingFrame(self)
                self.lcs_dlg.CenterOnScreen()
                self.lcs_dlg.Show()
            else:
                self.lcs_dlg.Raise()
        else:
            self.lcs_dlg = LicensingFrame(self)
            self.lcs_dlg.CenterOnScreen()
            self.lcs_dlg.Show()

    def info_event(self, event) -> None:
        """
        opens Information about this program:
         - version
         - build date
         - company logo
         - company contact information
        :return:
        """

        about_info = wx.adv.AboutDialogInfo()
        about_info.SetName("wxWebcam")
        about_info.SetVersion(__version__)
        text = "A small application to look at yourself and take an image!\nBundled with Briefacse!"
        about_info.SetDescription(text)
        about_info.SetCopyright("(C) 2021")
        about_info.SetWebSite(_WEB_PAGE)
        about_info.AddDeveloper(__author__)
        about_info.SetIcon(self.__icon)

        wx.adv.AboutBox(about_info)

    def close_event(self, event) -> None:
        """
        closing event of class QMainWindow / instance webcam
        :param event:
        :return None:
        """
        self.program.stop()

        # wait 5 seconds
        self.program.join(5.0)
        if self.program.is_alive():
            logger.error("Could not de-initialize camera.")
        else:
            logger.info("Application exit.")

        self.manager.UnInit()
        del self.manager
        self.Destroy()


class LicensingFrame(wx.Frame):
    def __init__(self, parent):

        wx.Frame.__init__(self)
        self.Create(parent, title="Licensing information", id=wx.ID_ANY)

        self.closed = False

        # for high resolution displays
        self.SetClientSize(self.FromDIP(wx.Size(640, 480)))

        # resources folder
        rsrcs_folder = os.path.join(os.path.abspath(os.path.dirname(__file__)), "resources")

        # set Icon of this GUI
        self.SetIcon(wx.Icon(os.path.join(rsrcs_folder, "wxWebcam.ico")))

        # Add a panel so it looks the correct on all platforms
        self.panel = wx.Panel(self, wx.ID_ANY)

        self.scrolled_panel = wx.lib.scrolledpanel.ScrolledPanel(self.panel, -1,
                                                                 style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)

        # Now continue with the normal construction of the dialog
        self.sizer = wx.BoxSizer(wx.VERTICAL)

        self.sizer.AddSpacer(5)
        self.wxwebcam_lic_label = wx.StaticText(self.scrolled_panel, label="wxWebcam")
        font = wx.Font(16, wx.DECORATIVE, wx.BOLD, wx.NORMAL)
        self.wxwebcam_lic_label.SetFont(font)
        self.sizer.Add(self.wxwebcam_lic_label, 0, wx.ALIGN_CENTRE | wx.ALL)
        self.sizer.AddSpacer(5)
        wxWebcam_lic = self.read_license(os.path.join(rsrcs_folder, "LICENSE_WXWEBCAM.txt"))
        self.wxwebcam_lic_edt = wx.richtext.RichTextCtrl(self.scrolled_panel,
                                                               style=wx.richtext.RE_READONLY,
                                                               value=wxWebcam_lic,
                                                               size=self.FromDIP((550, 300)))
        self.sizer.Add(self.wxwebcam_lic_edt, wx.EXPAND, wx.ALIGN_CENTRE_HORIZONTAL)
        self.sizer.AddSpacer(20)

        self.wxpython_lic_label = wx.StaticText(self.scrolled_panel, label="wxPython / wxWidgets")
        self.wxpython_lic_label.SetFont(font)
        self.sizer.Add(self.wxpython_lic_label, 0, wx.ALIGN_CENTRE | wx.ALL)
        self.sizer.AddSpacer(5)
        wx_lic = self.read_license(os.path.join(rsrcs_folder, "LICENSE_WXPYTHON.txt"))
        self.wx_lic_edt = wx.richtext.RichTextCtrl(self.scrolled_panel,
                                                     style=wx.richtext.RE_READONLY,
                                                     value=wx_lic,
                                                     size=self.FromDIP((550, 300)))
        self.sizer.Add(self.wx_lic_edt, wx.EXPAND, wx.ALIGN_CENTRE_HORIZONTAL)
        self.sizer.AddSpacer(20)

        self.beeware_lic_label = wx.StaticText(self.scrolled_panel, label="BeeWare / briefcase")
        self.beeware_lic_label.SetFont(font)
        self.sizer.Add(self.beeware_lic_label, 0, wx.ALIGN_CENTRE | wx.ALL)
        self.sizer.AddSpacer(5)
        beeware_lic = self.read_license(os.path.join(rsrcs_folder, "LICENSE_BEEWARE.txt"))
        self.beeware_lic_edt = wx.richtext.RichTextCtrl(self.scrolled_panel,
                                                     style=wx.richtext.RE_READONLY,
                                                     value=beeware_lic,
                                                     size=self.FromDIP((550, 300)))
        self.sizer.Add(self.beeware_lic_edt, wx.EXPAND, wx.ALIGN_CENTRE_HORIZONTAL)
        self.sizer.AddSpacer(20)

        self.opencv_lic_label = wx.StaticText(self.scrolled_panel, label="OpenCV")
        self.opencv_lic_label.SetFont(font)
        self.sizer.Add(self.opencv_lic_label, 0, wx.ALIGN_CENTRE | wx.ALL)
        self.sizer.AddSpacer(5)
        opencv_lic = self.read_license(os.path.join(rsrcs_folder, "LICENSE_OPENCV.txt"))
        self.opencv_lic_edt = wx.richtext.RichTextCtrl(self.scrolled_panel,
                                                     style=wx.richtext.RE_READONLY,
                                                     value=opencv_lic,
                                                     size=self.FromDIP((550, 300)))
        self.sizer.Add(self.opencv_lic_edt, wx.EXPAND, wx.ALIGN_CENTRE_HORIZONTAL)
        self.sizer.AddSpacer(20)

        self.numpy_lic_label = wx.StaticText(self.scrolled_panel, label="NumPy")
        self.numpy_lic_label.SetFont(font)
        self.sizer.Add(self.numpy_lic_label, 0, wx.ALIGN_CENTRE | wx.ALL)
        self.sizer.AddSpacer(5)
        numpy_lic = self.read_license(os.path.join(rsrcs_folder, "LICENSE_NUMPY.txt"))
        self.numpy_lic_edt = wx.richtext.RichTextCtrl(self.scrolled_panel,
                                                     style=wx.richtext.RE_READONLY,
                                                     value=numpy_lic,
                                                     size=self.FromDIP((550, 300)))
        self.sizer.Add(self.numpy_lic_edt, wx.EXPAND, wx.ALIGN_CENTRE_HORIZONTAL)
        self.sizer.AddSpacer(10)

        self.scrolled_panel.SetSizer(self.sizer)

        self.close_btn = wx.Button(self.panel, id=wx.ID_CLOSE)
        self.close_btn.Bind(wx.EVT_BUTTON, self.close_event)
        self.Bind(wx.EVT_CLOSE, self.close_event)  # custom close function

        panelSizer = wx.BoxSizer(wx.VERTICAL)
        panelSizer.AddSpacer(5)

        self.header_label = wx.StaticText(self.panel, label="Licensing Information")
        font = wx.Font(18, wx.DECORATIVE, wx.BOLD, wx.NORMAL)
        self.header_label.SetFont(font)
        panelSizer.Add(self.header_label, 0, wx.ALIGN_CENTRE)
        panelSizer.AddSpacer(5)

        panelSizer.Add(self.scrolled_panel, 1, wx.EXPAND)
        panelSizer.AddSpacer(5)
        panelSizer.Add(self.close_btn, 0, wx.ALIGN_CENTRE)
        panelSizer.AddSpacer(5)
        self.panel.SetSizer(panelSizer)

        self.scrolled_panel.SetAutoLayout(1)
        self.scrolled_panel.SetupScrolling()
        self.scrolled_panel.ScrollChildIntoView(self.wxwebcam_lic_label)

    @staticmethod
    def read_license(license_path):
        """
        reading a license file
        :param license_path:
        :return license_text:
        """
        f = open(license_path, "r")
        license_text = f.read()
        f.close()
        return license_text

    def close_event(self, event) -> None:
        """
        closing event of class QMainWindow / instance webcam
        :param event:
        :return None:
        """
        self.closed = True
        self.Destroy()
