"""

#AUTHORS
*** Moses Olafenwa
*** John Olafenwa
>> January, 2018

#NOTES
Please remember to replace the string "PROGRAM_INSTALL_FULLPATH" with the full path
of your program install folder in every instance that it is used in the code below.
You may use relative purpose for debugging, but please note that you should keep and
maintain an absolute path for the "model_path" and "index_file_path" as once a picture
is selected in the UI for prediction, the program execution folder will migrate
to the folder of the selected picture, making the model file and index file
unreadable by the program.

"""

# Import WxPython, os and Thread classes
import wx
import wx.adv
from wx.lib.wordwrap import wordwrap
import os
from wx.lib.pubsub import pub, setupkwargs
import threading







# Function to initiate image prediction in a separate thread and accepts the image path, as sent from the dialog
class actionThreadSqueezeNet(threading.Thread):
    def __init__(self, path, model_path, index_file_path, main_ui):
        threading.Thread.__init__(self)
        self.path = path
        self.model_path = model_path
        self.main_ui = main_ui
        self.index_file_path = index_file_path


    def run(self):
        wx.CallAfter(pub.sendMessage, "report101", message="Prediction process started. \nLoading........")  # Sending report to the Text Box
        import SqueezenetPrediction
        result = SqueezenetPrediction.predict(self.path, self.model_path, self.index_file_path, self.main_ui) # This calls the SqueezeNet Prediction Class
        wx.CallAfter(pub.sendMessage, "report101", message=result) # A call to update the report Text Box after image recognition



class Mainapp(wx.Frame):

    prediction_ongoing = False  # To check and avoid attempt to initiate precdictions before another is done

    def __init__(self, parent):
        super(Mainapp, self).__init__(parent)

        self.squeezenet_model_loaded = False  # Value to make checks, as the image prediction Model should loaded only once
        self.model_collection_squeezenet = []  # An Array to hold the Loaded model for use for subsequent calls for image prediction
        self.magic_collection = []  # A collection to hold the containing UI objects and its children

        pub.subscribe(self.reportPrediction, "report101" ) # Subscribe the function to the call coming from the thread

        # Set the User interface properties
        self.Action()
        self.SetBackgroundColour("green")
        self.SetSize((1000,600))
        self.SetTitle("SqueezeNet Playground")

        icon =  wx.Icon("PROGRAM_INSTALL_FULLPATH\\playground.png")
        self.SetIcon(icon)

        self.Centre()
        self.Show()





    def Action(self):


        # Set a background for the UI
        ui_bitmap = wx.Image("PROGRAM_INSTALL_FULLPATH\\background.jpg", wx.BITMAP_TYPE_ANY).ConvertToBitmap()
        self.under = wx.StaticBitmap(self, -1, ui_bitmap, (0, 0))

        # Image Banner
        banner_bitmap1 = wx.Bitmap("PROGRAM_INSTALL_FULLPATH\\SqueezeNet.png")
        banner_image1 = banner_bitmap1.ConvertToImage()
        banner_image1 = banner_image1.Scale(285, 200, wx.IMAGE_QUALITY_HIGH)
        banner_bitmap2 = banner_image1.ConvertToBitmap()
        ui_banner_bitmap = wx.StaticBitmap(self, -1, banner_bitmap2 ,(0,2) )





        # The Left menu below the banner, which contains the File dialog button, report text box and about button
        left_menu = wx.StaticBox(self.under, -1, "" )
        left_menu.SetSize((285,370))
        left_menu.SetPosition((0,200))
        left_menu.SetBackgroundColour(wx.WHITE)


        # The right region to display the picture selected from the dialog.
        right_region = wx.StaticBox(self, -1, "Picture")
        right_region.SetSize((650,510))
        right_region.SetBackgroundColour(wx.WHITE)
        right_region.SetPosition((300, 10))
        self.magic_collection.append(right_region) # Adding the right region object to the UI object array

        # The report text box, to report prediction results
        report_box = wx.TextCtrl(left_menu, -1, "1. Click the Button Above \n \n"
                                                   "2.Select your picture. \n \n"
                                                   "3. Wait till the Program studies the picture and display the result here",
                                  size= (285, 200), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER, pos=(5, 80) )
        font = wx.Font(10, wx.DECORATIVE, wx.NORMAL, wx.NORMAL)
        report_box.SetFont(font)

        self.magic_collection.append(report_box) # Adding the report text box to the UI objects array


        # A Sample picture in the right region
        modification_bitmap1 = wx.Bitmap("PROGRAM_INSTALL_FULLPATH\\sample.jpg")
        modification_image1 = modification_bitmap1.ConvertToImage()
        modification_image1 = modification_image1.Scale(650, 490, wx.IMAGE_QUALITY_HIGH)
        modification_bitmap2 = modification_image1.ConvertToBitmap()
        report_bitmap = wx.StaticBitmap(right_region, -1, modification_bitmap2, (0, 20) )
        self.magic_collection.append(report_bitmap)

        # Text label
        image_dialog_label = wx.StaticText(left_menu, -1, "Select Picture")
        image_dialog_label.SetPosition((20, 20))
        image_dialog_label.SetForegroundColour(wx.BLUE)
        image_dialog_label.SetBackgroundColour(wx.WHITE)

        # The button to call the file dialog function
        dialog_button = wx.Button(left_menu, -1, "Click to Select")
        dialog_button.SetPosition((20, 40))
        dialog_button.SetSize((120, 30))
        dialog_button_bitmap = wx.Bitmap("PROGRAM_INSTALL_FULLPATH\\select-file.png")
        dialog_button_bitmap_pressed = wx.Bitmap("PROGRAM_INSTALL_FULLPATH\\select-file-hover.png")
        dialog_button.SetBitmap(dialog_button_bitmap, wx.LEFT)
        dialog_button.SetBitmapPressed(dialog_button_bitmap_pressed)
        self.Bind(wx.EVT_BUTTON, self.launchFileDialog, dialog_button )

        # The button to call the About SqueezeNet dialog
        about_squeezenet = wx.Button(left_menu, -1, "About SqueezeNet")
        about_squeezenet.SetPosition((20, 285))
        about_squeezenet.SetSize((200, 30))
        about_squeezenet_bitmap = wx.Bitmap("PROGRAM_INSTALL_FULLPATH\\info.png")
        about_squeezenet.SetBitmap(about_squeezenet_bitmap, wx.LEFT)
        self.Bind(wx.EVT_BUTTON, self.aboutSqueezenet, about_squeezenet)

        # The button to call the About dialog
        about_button = wx.Button(left_menu, -1, "About SqueezeNet Playground")
        about_button.SetPosition((20, 320))
        about_button.SetSize((200, 30))
        about_button_bitmap = wx.Bitmap("PROGRAM_INSTALL_FULLPATH\\info.png")
        about_button.SetBitmap(about_button_bitmap, wx.LEFT)
        self.Bind(wx.EVT_BUTTON, self.aboutApplication, about_button)




    def launchFileDialog(self, evt):
        # defining wildcard for suppported picture formats
        wildcard = "JPEG (*.jpg)|*.jpg|" \
                   "PNG (*.png)|*.png|" \
                   "GIF (*.gif)|*.gif"
        # defining the dialog object
        dialog = wx.FileDialog(self, message="Select Picture", defaultDir=os.getcwd(), defaultFile="",
                               wildcard=wildcard,
                               style=wx.FD_OPEN | wx.FD_MULTIPLE | wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST | wx.FD_PREVIEW)
        # Function to retrieve file dialog response and return the full path of the first image (it is a multi-file selection dialog)
        if dialog.ShowModal() == wx.ID_OK:
            self.magic_collection[1].SetValue(
                "You have selected a Picture. It will now be processed!, Please wait! \nLoading.....")
            paths = dialog.GetPaths()

            # This adds the selected picture to the Right region. Right region object is retrieved from UI object array
            modification_bitmap1 = wx.Bitmap(paths[0])
            modification_image1 = modification_bitmap1.ConvertToImage()
            modification_image1 = modification_image1.Scale(650, 490, wx.IMAGE_QUALITY_HIGH)
            modification_bitmap2 = modification_image1.ConvertToBitmap()
            report_bitmap = wx.StaticBitmap(self.magic_collection[0], -1, modification_bitmap2, (0, 20))

            self.processPicture(paths[0],
                                "PROGRAM_INSTALL_FULLPATH\\squeezenet_weights_tf_dim_ordering_tf_kernels.h5",
                                "PROGRAM_INSTALL_FULLPATH\\imagenet_class_index.json")

    def processPicture(self, path, model_path, index_file_path):
        # Report text box is updated with a message
        self.magic_collection[1].SetValue(
            "Picture is currently being processed. May take a few minutes. Wait for the result! \nLoading.....")
        # Thread to initiate image prediction is called
        action = actionThreadSqueezeNet(path, model_path, index_file_path, self)
        action.start()

    def reportPrediction(self, message):
        # This adds the prediction report into the report text box
        self.magic_collection[1].SetValue(message)


    # The About Application Dialog Function
    def aboutApplication(self, evt):
        about_details = wx.adv.AboutDialogInfo()
        about_details.Name = "SqueezeNet Playground"
        about_details.Version = "1.0"
        about_details.Description = wordwrap("        SqueezeNet Playground is a software that enables users to perform average "
                                             "recognition and classification on pictures on computer systems. Powered by "
                                             "the Convolutional Neural Network Architecture, SqueezeNet model, trained on the ImageNet "
                                             "dataset which comprises of 1000 different objects in its 1.2 million pictures "
                                             "collection, this software can recognize on average most everyday objects based on "
                                             "the capability of the SqueezeNet + ImageNet model shipped with it. \n "
                                             "        This software is part of a series of programs that is meant to let "
                                             "non-programmers and average computer users to experience Artificial Intelligence "
                                             "in which machines and software programs can identify picture/objects in pictures. \n"
                                             "        These series of Artificial Intelligence playgrounds is built by Specpal "
                                             "with Moses Olafenwa as its Chief programmer and John Olafenwa as the Technical Adviser.  \n"
                                             "        This program is free for anyone to use for both commercial and non-commercial purposes.  "
                                             " We do not guarantee the accuracy or consistency of this program and we shall not be "
                                             "responsible for any consequence or damage to your computer system that may arise in the "
                                             " use of this program. \n"
                                             "        You can reach to Moses Olafenwa via an email to \"guymodscientist@gmail.com\", or John Olafenwa via an email to \"johnolafenwa@gmail.com\" . ", 500, wx.ClientDC(self))
        about_details.Copyright = "Specpal"
        about_details.SetWebSite("http://www.specpal.science", "Specpal's Official Website")

        about_dialog = wx.adv.AboutBox(about_details)

    # About SqueezeNet dialog function
    def aboutSqueezenet(self, evt):
        about_details = wx.adv.AboutDialogInfo()
        about_details.Name = "SqueezeNet"
        about_details.Version = ""
        about_details.Description = wordwrap(
            "     SqueezeNet is a Deep Neural Network architecture developed to be relatively small"
            ", require less computational power for training, require less server-to-server communication"
            " during distributed training and have a viable low-end devices deployment.", 300, wx.ClientDC(self))
        about_details.Copyright = ""
        about_details.SetWebSite("https://github.com/DeepScale/SqueezeNet", "SqueezeNet GitHub page")
        about_details.SetDevelopers(["Forrest N. Iandola", "Song Han", "Matthew W. Moskewicz", "Khalid Ashraf", "William J. Dally", "Kurt Keutzer"])

        about_dialog = wx.adv.AboutBox(about_details)








def Main():
    app = wx.App()
    Mainapp(None)
    app.MainLoop()

if __name__=="__main__":
    Main()