# File Name: implementation.py
# Modified Date: April 16, 2017
# Description: This class does functionalities such as validation check, calculation and
#              it also interacts with users by displaying a result message as a popup window.
# Method Level Comment: Python Convention (inside function)
__author__ = "Inyoung Choung"

import tkinter
from tkinter import messagebox
from impl_interface import ImplInterface

# Impl class implements ImplInterface.
class Impl(ImplInterface):
    # Here are the global variables that can be accessed throughout the class.
    global itemtype, brandN1, brandN2
    global price1, price2
    global weight1, weightT1, weight2, weightT2
    global discount1, discountT1, discount2, discountT2
    global newWeightForSecondItem
    global isEmpty
    global isFinalized
    global result
    global list
    global path

    def __init__(self, itemtype, brandN1, brandN2, price1, price2, weight1, weightT1,
                 weight2, weightT2, discount1, discountT1, discount2, discountT2):
        """
        This method is a constructor that initializes parameters.
        :param itemtype:
        :param brandN1:
        :param brandN2:
        :param price1:
        :param price2:
        :param weight1:
        :param weightT1:
        :param weight2:
        :param weightT2:
        :param discount1:
        :param discountT1:
        :param discount2:
        :param discountT2:
        """
        # initialize values to the class variables.
        self.itemtype = itemtype
        self.brandN1 = brandN1
        self.brandN2 = brandN2
        self.price1 = price1
        self.price2 = price2
        self.weight1 = weight1
        self.weightT1 = weightT1
        self.weight2 = weight2
        self.weightT2 = weightT2
        self.discount1 = discount1
        self.discountT1 = discountT1
        self.discount2 = discount2
        self.discountT2 = discountT2

        # calls this function to validate any empty fields from users.
        self.validate_emptyfields()

        # after users values are accepted, do calculation based on the weight types of the two items.
        if self.weightT1 != self.weightT2:
            # if the weight types are different,
            # the second items should convert to the first item weight type.
            self.newWeightForSecondItem = self.convert(self.weightT1, self.weightT2, self.weight2)

        else:
            # both items have the same weight type.
            self.newWeightForSecondItem = self.weight2

        # do calculation on which item is cheaper.
        self.calculate()


    def validate_emptyfields(self):
        """
        This function checks any null values.
        :return:
        """
        # if there is any empty field
        if (self.itemtype is None or self.itemtype == "" or
            self.brandN1 is None or self.brandN1 == "" or
            self.brandN2 is None or self.brandN2 == "" or
            self.price1 is None or self.price1 == "" or
            self. price2 is None or self.price2 == "" or
            self.weight1 is None or self.weight1 == "" or
            self.weightT1 is None or self.weightT1 == "" or
            self. weight2 is None or self.weight2 == "" or
            self.weightT2 is None or self.weightT2 == "" or
            self.discount1 is None or self.discount1 == "" or
            self.discountT1 is None or self.discountT1 == "" or
            self.discount2 is None or self.discount2 == "" or
            self.discountT2 is None or self.discountT2 == ""):
                # if there is any empty field, initialize value; true to the variable.
                self.isEmpty = True

                # if there is any empty field, prompt a message to user.
                self.display_message(None)
        else:
            # if there is no any empty field, initialize value; false to the variable.
            self.isEmpty = False


    def convert(self, weightT1, weightT2, weight2):
        """
        This function converts the second item weight type to the first weight item type.
        :param weightT1:
        :param weightT2:
        :param weight2:
        :return: self.weight2
        """
        if weightT1 == "lb":
            if weightT2 == "kg":
                self.weight2 = float(weight2) * 2.20462
            elif weightT2 == "g":
                self.weight2 = float(weight2) * 0.002205
        elif weightT1 == "kg":
            if weightT2 == "lb":
                self.weight2 = float(weight2) * 0.453592
            elif weightT2 == "g":
                self.weight2 = float(weight2) * 0.001
        elif weightT1 == "g":
            if weightT2 == "kg":
                self.weight2 = float(weight2) * 1000
            elif weightT2 == "lb":
                self.weight2 = float(weight2) * 453.592

        # returns the converted second weight based on the first item weight type.
        return self.weight2


    def calculate(self):
        """
        This function calculates to see which item is cheaper by the unit weight.
        :return: self.result
        """
        # checks which discount type each first item has and calculates the price to the final price.
        if self.discountT1 == "percentage":
            finalPrice1 = float(float(self.price1) - (float(self.price1) * (float(self.discount1) * float(0.01))) / float(self.weight1))
        elif self.discountT1 == "dollar":
            finalPrice1 = float((float(self.price1) - float(self.discount1)) / float(self.weight1))

        # checks which discount type each second item has and calculates the price to the final price.
        if self.discountT2 == "percentage":
            finalPrice2 = float((float(self.price2) - (float(self.price2) * (float(self.discount2) * float(0.01)))) / float(self.newWeightForSecondItem))
        elif self.discountT2 == "dollar":
            finalPrice2 = float((float(self.price2) - (float(self.discount2))) / float(self.newWeightForSecondItem))

        # compare the final price of the two items.
        if finalPrice1 > finalPrice2:
            priceDiff = float(round(finalPrice1 - finalPrice2, 3))
            print(self.brandN1 + str(" is cheaper by "), priceDiff, str(" cents per unit weight "))
            self.result = str(self.brandN1 + str(" is cheaper by ") + str(priceDiff) + str(" cents per unit weight "))
        elif finalPrice1 < finalPrice2:
            priceDiff = float(round(finalPrice2 - finalPrice1, 3))
            print(self.brandN2 + str(" is cheaper by "), priceDiff, str(" cents per unit weight "))
            self.result = str(self.brandN2 + str(" is cheaper by ") + str(priceDiff) + str(" cents per unit weight "))
        else:
            self.result = str("The price is equal so get anything!")

        # once the calculation is done, initialize value; True to the variable.
        self.isFinalized = True

        # calls this function to display the popup message to the user.
        self.display_message(None)
        return self.result


    def get_result(self):
        """
        This function returns String; result.
        :return: self.result
        """
        return self.result


    def display_message(self, message):
        """
        This function is to check which message is to display to user based on boolean variables.
        :param message:
        :return: message
        """
        if (message is None):
            if self.isEmpty is True:
                # hide main window
                root = tkinter.Tk()
                root.withdraw()

                # show the error message to the user after validation.
                messagebox.showerror("Alert - Empty fields", "Please fill out all the forms.")

                # set False to isEmpty variable.
                self.isEmpty = False

            elif self.isFinalized is True:
                # hide main window.
                root = tkinter.Tk()
                root.withdraw()

                # show the result message to the user.
                messagebox.showinfo("Calculation Result", self.result)
        else:
            # hide main window.
            root = tkinter.Tk()
            root.withdraw()

            # This is for the pytest purpose and display the message that is passed from the parameter.
            messagebox.showinfo("Calculation Result", message)
            return message



    def sort(self, list):
        """
        This function gets list, sort it and returns that list.
        :param list:
        :return: list
        """
        # sort the list in the alphabetical order.
        list.sort()

        # print out the string and the list to console.
        print("This is a sorted brand_name list by the alphabetical order.")
        print(list)

        return list


    def writefile(self, path, stuff_to_print):
        """
        TFile I/O - prints a sorted list of grocery items.
        :param path:
        :param stuff_to_print:
        :return:
        """
        if path is None :
            # set a specific path
            self.path = "C:\In-young Choung\Computer Programming\Self Programming Files\grocerycalc\itemlist.txt"

            # write a file if it doesn't exist
            f = open(self.path, "w+")

            # write each item of the grocery list inside the opened file
            for i in stuff_to_print :
                f.write(i)
        else :
            self.path = path

            # write a file if it doesn't exist
            f = open(self.path, "w+")

            # write some texts inside the opened file
            f.write(stuff_to_print)


    def readfile(self):
        """
        read file that is created and pass filepath as a parameter.
        :return:
        """
        # open the file in the defined path and "r" read it
        f = open(self.path, "r")

        # mode is matched with 'r' (read)
        if f.mode == 'r':
            # use read function to read the entire file.
            contents = f.read()

            # print the file content in the console
            print("This is a list from reading a file.")

            # print the file content by one letter at a time
            for i in contents:
                print(i)
        else :
            print("wrong mode in readfile function.")


