# * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
#  *
#  *
#  *  Redistribution and use in source and binary forms, with or without
#  *  modification, are permitted provided that the following conditions
#  *  are met:
#  *
#  *    Redistributions of source code must retain the above copyright
#  *    notice, this list of conditions and the following disclaimer.
#  *
#  *    Redistributions in binary form must reproduce the above copyright
#  *    notice, this list of conditions and the following disclaimer in the
#  *    documentation and/or other materials provided with the
#  *    distribution.
#  *
#  *    Neither the name of Texas Instruments Incorporated nor the names of
#  *    its contributors may be used to endorse or promote products derived
#  *    from this software without specific prior written permission.
#  *
#  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#  *
# */


import webbrowser
import os, sys, subprocess
from os import path
from tkinter.filedialog import asksaveasfilename
from tkinter.filedialog import askopenfilename
from MT_Commands import *
from Output_Info import *
from Start_Callback import *
import global_vars


def erase(self):
    # Clear the content within the Application
    from Input_Info import input_settings
    from Read_Info import read_settings
    from Write_Info import write_settings
    from Output_Info import output_settings
    input_settings(self, False)
    write_settings(self)
    output_settings(self)
    read_settings(self)
    self.file_menu.entryconfig("Start", state=DISABLED)
    self.view_menu.entryconfig("Read File", state=DISABLED)
    return


def url_callback(url):
    # Open the URL in a browser
    webbrowser.open_new(url)


def open_config_file(self):
    # Opens the Configuration File
    if sys.platform == 'win32':
        os.startfile(global_vars.id_file)
    else:
        opener = "open" if sys.platform == 'darwin' else "xdg-open"
        subprocess.call([opener, global_vars.id_file])
    return


def open_read_file(self):
    # Open the Read File
    if sys.platform == 'win32':
        os.startfile(global_vars.content_file)
    else:
        opener = "open" if sys.platform == 'darwin' else "xdg-open"
        subprocess.call([opener, global_vars.content_file])
    return


def clear_frame(self):
    # Clears the content within a frame
    for widget in self.winfo_children():
        widget.destroy()
    self.pack_forget()


def log_output(self):
    # Export the content of the output display into a file
    files = [('Text Document', '.txt')]
    name = asksaveasfilename(filetypes=files, defaultextension=".txt")
    if name != "":
        f = open(name, 'w+')
        for x in range(self.Lb3.size()):
            f.write(''.join(self.Lb3.get(x))+'\n')
        f.close()
    return


def forming_network(self):
    error = False
    if self.v.get() == 1:  # Reading
        if self.Lb1.curselection() is not ():
            port = self.Lb1.get(ACTIVE)
        else:
            error = True
    if self.v.get() == 2:  # Writing
        if self.Lb2.curselection() is not ():
            port = self.Lb2.get(ACTIVE)
        else:
            error = True
    if self.v.get() == 3:  # Cloning
        if self.Lb2.curselection() is not ():
            port = self.Lb2.get(ACTIVE)
        else:
            error = True

    if not error:
        if sys.platform.startswith('darwin'):
            port = "/dev/tty.usbmodemL" + port
        try:
            ser = serial.Serial(port=port, baudrate=115200, parity=serial.PARITY_NONE,
                                stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1)
        except IOError:
            # Erase the content from the output display
            self.Lb3.delete(0, 'end')
            self.Lb3.insert('end', port + " is no longer accessible.")
            self.Lb3.yview(END)
        app_cnf_bdb_start_commissioning(self, ser, True)
        ser.close()
    else:
        self.Lb3.insert('end', 'Please select a COM Port')
        self.Lb3.yview(END)
    return


def reset_device(self):
    error = False
    if self.v.get() == 1:  # Reading
        if self.Lb1.curselection() is not ():
            port = self.Lb1.get(ACTIVE)
        else:
            error = True
    if self.v.get() == 2:  # Writing
        if self.Lb2.curselection() is not ():
            port = self.Lb2.get(ACTIVE)
        else:
            error = True
    if self.v.get() == 3:  # Cloning
        if self.Lb2.curselection() is not ():
            port = self.Lb2.get(ACTIVE)
        else:
            error = True

    if not error:
        if sys.platform.startswith('darwin'):
            port = "/dev/tty.usbmodemL" + port
        try:
            ser = serial.Serial(port=port, baudrate=115200, parity=serial.PARITY_NONE,
                                stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=5)
        except IOError:
            # Erase the content from the output display
            self.Lb3.delete(0, 'end')
            self.Lb3.insert('end', port + " is no longer accessible.")
            self.Lb3.yview(END)

        ser.flushInput()
        sys_reset(ser)
        ser.flushInput()
        zdo_startup_from_app(self, ser)
        ser.flushInput()
        ser.flushOutput()
        ser.close()
    else:
        self.Lb3.insert('end', 'Please select a COM Port')
        self.Lb3.yview(END)
    return


def write_options(self):
    # If writing from a file -> Disable the Write Settings and ask to upload the file
    if self.w.get() == '1':
        for x in self.fr_write_back.winfo_children():
            x.config(state=DISABLED)
        files = [('Text Document', '.txt')]
        write_file = askopenfilename(filetypes=files, defaultextension=".txt")

        if path.exists(write_file):
            global_vars.content_file = write_file
            self.write_file_label.config(state=NORMAL)
            self.write_file_label.grid()
    # If not writing from a file -> Enable the Write Settings
    else:
        for x in self.fr_write_back.winfo_children():
            x.config(state=NORMAL)
        self.write_file_label.config(state=DISABLED)
        self.write_file_label.grid_remove()
    return


def device_scan(self):
    # Check what the available ports are
    ports = serial_ports()

    if len(ports) != 0:
        # Form a NWK
        self.tools_menu.entryconfig("Form Network", state=NORMAL)
        # Reset Device
        self.tools_menu.entryconfig("Reset", state=NORMAL)
    else:
        # Form a NWK
        self.tools_menu.entryconfig("Form Network", state=DISABLED)
        # Reset Device
        self.tools_menu.entryconfig("Reset", state=DISABLED)

    # Enable Listbox
    self.Lb1.config(state=tk.NORMAL)
    self.Lb2.config(state=tk.NORMAL)

    # Check if any of the items are active
    read_port = [self.Lb1.get(idx) for idx in self.Lb1.curselection()]
    write_port = [self.Lb2.get(idx) for idx in self.Lb2.curselection()]

    # Clear the Listbox of current COM Ports
    self.Lb1.delete(0, END)
    self.Lb2.delete(0, END)

    # Re-write the available COM Ports
    for x in range(len(ports)):
        self.Lb1.insert(x + 1, ports[x])
        self.Lb2.insert(x + 1, ports[x])

    # Enable or Disable based on application
    if self.v.get() == 0:
        self.Lb1.config(state=tk.DISABLED)
        self.Lb2.config(state=tk.DISABLED)
    if self.v.get() == 1:  # Only Read
        self.Lb1.config(state=tk.NORMAL)
        self.Lb2.config(state=tk.DISABLED)
        items = self.Lb1.get(0, END)
        if len(read_port) != 0:
            for x in range(len(ports)):
                if read_port[0] == ports[x]:
                    self.Lb1.activate(items.index(read_port[0]))
                    self.Lb1.selection_set(items.index(read_port[0]))
    if self.v.get() == 2:  # Only Write
        self.Lb2.config(state=tk.NORMAL)
        self.Lb1.config(state=tk.DISABLED)
        items = self.Lb2.get(0, END)
        if len(write_port) != 0:
            for x in range(len(ports)):
                if write_port[0] == ports[x]:
                    self.Lb2.activate(items.index(write_port[0]))
                    self.Lb2.selection_set(items.index(write_port[0]))
    if self.v.get() == 3:  # Do both Read and Write
        self.Lb1.config(state=tk.NORMAL)
        self.Lb2.config(state=tk.NORMAL)
        items_1 = self.Lb1.get(0, END)
        items_2 = self.Lb2.get(0, END)
        if len(read_port) != 0 and len(write_port) != 0:
            for x in range(len(ports)):
                if read_port[0] == ports[x]:
                    self.Lb1.activate(items_1.index(read_port[0]))
                    self.Lb1.selection_set(items_1.index(read_port[0]))
                if write_port[0] == ports[x]:
                    self.Lb2.activate(items_2.index(write_port[0]))
                    self.Lb2.selection_set(items_2.index(write_port[0]))
    self.Lb3.insert(END, 'Scan Complete')
    self.Lb3.yview(END)
    return


def feature_action(self):
    # Enable the Start Button upon select
    self.start_button.config(state=NORMAL)

    # Enable the Start Option in Menu Bar upon selection
    self.file_menu.entryconfig(1, state=NORMAL)

    nv_region_list = []
    nv_region_clone = []
    nv_id = []
    all_regions = False
    error = False

    from File_Check import prelim_file_check
    from File_Check import max_width_file
    if not prelim_file_check(self, global_vars.id_file):
        # Read the Configuration File
        with open(global_vars.id_file, 'r') as file:
            content = file.readlines()
            if max_width_file(self, content) != 0:
                all_regions = True
            else:
                error = True
            for line in content:
                line = ((line.strip('\n')).split(':'))
                for x in range(len(line)):
                    line[x] = line[x].strip()
                if line[0] != '' and line[0] != 'NV Region' and line[1] != '':
                    nv_region_list.append(line[0])
                    nv_id.append(line[1])
        file.close()

        # Check for Mandatory 'Cloning' Regions
        if len(nv_region_list) != 0:
            for x in range(len(nv_region_list)):
                if (nv_region_list[x])[-1] == '*':
                    nv_region_list[x] = nv_region_list[x].strip("*")
                    nv_region_clone.append(nv_region_list[x].strip("*"))
        else:
            error = True

        # Enable/ Disable features based on application
        if not error:
            port_size = self.Lb2.size()
            if port_size != 0:
                # Form a NWK
                self.tools_menu.entryconfig("Form Network", state=NORMAL)
                # Reset Device
                self.tools_menu.entryconfig("Reset", state=NORMAL)
            else:
                # Form a NWK
                self.tools_menu.entryconfig("Form Network", state=DISABLED)
                # Reset Device
                self.tools_menu.entryconfig("Reset", state=DISABLED)

        if self.v.get() == 1 and not error:  # Only Read
            # Clear any prior selections
            self.Lb1.selection_clear(0, END)
            self.Lb2.selection_clear(0, END)

            # List Boxes
            self.Lb1.config(state=tk.NORMAL)
            self.Lb2.config(state=tk.DISABLED)
            self.Lb4.config(state=NORMAL, bd=0)

            # Write from a file
            self.write_select.config(state=tk.DISABLED)
            self.write_select.deselect()
            self.write_file_label.grid_remove()

            # Write Settings
            for x in self.fr_write_back.winfo_children():
                x.config(state=DISABLED)

            # Re-Populate Read Settings
            self.Lb4.delete(0, END)
            if all_regions:
                self.Lb4.insert(END, 'All Regions')
            if len(nv_region_list) != 0:
                for x in range(len(nv_region_list)):
                    self.Lb4.insert(END, nv_region_list[x])

            self.Lb3.delete(0, 'end')
            if port_size == 0:
                self.Lb3.insert(END, "No devices detected. Please connect device and go to Tools -> Scan for Devices.")
            else:
                self.Lb3.insert(END, "Select a port and the content to read")
                self.Lb3.insert(END, 'Press [Start Process] to begin once all selections have been made.')
            self.Lb3.yview(END)

        if self.v.get() == 2 and not error:  # Only Write
            # Clear any prior selections
            self.Lb1.selection_clear(0, END)
            self.Lb2.selection_clear(0, END)

            # List Boxes
            self.Lb2.config(state=tk.NORMAL)
            self.Lb1.config(state=tk.DISABLED)
            self.Lb4.config(state=DISABLED, bd=0)

            # Write from a file
            self.write_select.config(state=tk.NORMAL)
            self.write_file_label.grid_remove()

            # Write Settings
            for x in self.fr_write_back.winfo_children():
                x.config(state=NORMAL)

            # Re-Populate Read Settings
            self.Lb4.delete(0, END)
            if all_regions:
                self.Lb4.insert(END, 'All Regions')
            if len(nv_region_list) != 0:
                for x in range(len(nv_region_list)):
                    self.Lb4.insert(END, nv_region_list[x])

            self.Lb3.delete(0, 'end')
            if port_size == 0:
                self.Lb3.insert(END, "No devices detected. Please connect device and go to Tools -> Scan for Devices.")
            else:
                self.Lb3.insert(END, "* If this is the first time writing to a new ZC then start the network before")
                self.Lb3.insert(END, "  performing the procedure.")
                self.Lb3.insert(END, "To start the network go to Tools -> Form Network")
            self.Lb3.yview(END)

        if self.v.get() == 3 and not error:  # Do both Read and Write
            # Clear any prior selections
            self.Lb1.selection_clear(0, END)
            self.Lb2.selection_clear(0, END)

            # List Boxes
            self.Lb1.config(state=tk.NORMAL)
            self.Lb2.config(state=tk.NORMAL)
            self.Lb4.config(state=NORMAL, bd=0)

            # Write from a file
            self.write_select.config(state=tk.DISABLED)
            self.write_select.deselect()
            self.write_file_label.grid_remove()

            # Write Settings
            for x in self.fr_write_back.winfo_children():
                x.config(state=DISABLED)

            # Re-Populate the Read Settings
            self.Lb4.delete(0, END)
            if all_regions:
                self.Lb4.insert(END, 'All Regions')
            if len(nv_region_list) != 0:
                for x in range(len(nv_region_list)):
                    self.Lb4.insert(END, nv_region_list[x])

            # Activate the items for Cloning Process
            items = self.Lb4.get(0, END)
            for x in range(len(nv_region_clone)):
                self.Lb4.activate(items.index(nv_region_clone[x]))
                self.Lb4.selection_set(items.index(nv_region_clone[x]))
            self.Lb3.delete(0, 'end')
            if port_size == 0:
                self.Lb3.insert(END, "No devices detected. Please connect devices and go to Tools -> Scan for Devices.")
            else:
                self.Lb3.insert(END, "* If this is the first time writing to the new ZC then start the network before")
                self.Lb3.insert(END, "  performing the cloning procedure.")
                self.Lb3.insert(END, "To start the network go to Tools -> Form Network")
            self.Lb3.yview(END)

        if error:
            # Erase the content from the output display
            self.Lb3.delete(0, 'end')
            self.Lb3.insert(END, "No Content in " + os.path.basename(global_vars.id_file))
            self.Lb3.yview(END)
    return
