# Version 1.0 - rewrote in AWK, then ported to Python.
# Version 1.0.1 - now removes commas in game names, and any duplicate entries.
# Version 1.0.2 - Monolith version. Utilises a single python script to generate, combine, sort and filter the TSL file. 
# Version 1.0.3 - Converted to a Class
# Version 1.1 - Updated GUI, removed dependency on text file processing, autogenerate qcas.bat file
# Version 1.2 - Updated to remove non-ASCII characters from game names
# Version 1.2.1 - Updated to handle abortion on qcas.bat selection
# Version 1.2.2 - Updated to handle leading "0" on Month file for MSL and PSL
# Version 1.2.3 - Updated to fix qcas autogeneration, now prompts users if a new Game is being generated in a new month (refer to diagram).
# Version 1.2.4 - Automatically removes headers from Tab Delimited File 
# Version 1.3 - Remove blank spaces, uses OUTPUT_DIR as an output directory, some testing code, version to v1.3, automatic new TSL filename, to support xls file
# Last Modified date: 8/12/2021 = refer to https://github.com/icarus523/qcasTSLgenerator.git

import csv
import sys
import operator
import os
import re
import getpass

from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox
from datetime import datetime

VERSION = "1.3"
QCAS_BATCH_FILE_HEADER_STR = ("Echo To be run on Datafile PC\n" 
    "Echo CTRL-C to exit\nPause\n" 
    "REM **********************************************************************************************************************************\n"
    "REM ** usage epsigQCAS3_5.exe d:\\techserv\\binimage\\*.* Current Month.msl new-qcas.tsl (previously version copied & inc by 1).psl    **\n"
    "REM **********************************************************************************************************************************\n")

QCAS_BATCH_FILE_FOOTER_STR = (
"\nREM *******************************************************************************************************************************" 
"\nREM ** usage epsigQCAS3_5.exe d:\\techserv\\binimage\\*.* Next Month.msl new-qcas.tsl (previously version copied & inc by 1).psl    **" 
"\nREM **                                                                                                                           **" 
"\nREM ** NOTE for NEW MONTH if files do not exist they are automatically generated                                                 **" 
"\nREM *******************************************************************************************************************************\n\n")

QCAS_DIRECTORY = "G:\OLGR-TECHSERV\MISC\BINIMAGE\qcas"
#QCAS_DIRECTORY = "." # Testing only

OUTPUT_DIR = 'output'

class QCAS_batch_file():

    def __init__(self, fname, new_tsl_file, new_games_new_month):
        self.filename = fname
        self.new_tsl_file = new_tsl_file
        self.new_games_new_month = new_games_new_month

    def get_filename(self):
        return self.filename

    def format_version(self, version_number):
        if version_number < 9:
            return "0" + str(version_number)
        else:
            str(version_number)

    def replace_qcas_command(self, entrylist):
        if len(entrylist) == 2: # Must be two entries.
            with open(os.path.join(OUTPUT_DIR,'qcas_autogenerated.bat'), 'w+') as batch_file:
                batch_file.write(QCAS_BATCH_FILE_HEADER_STR)
                batch_file.write("\nREM copy error.txt error1.txt\n\n")
                batch_file.write(entrylist[0]) # First Month
                batch_file.write(QCAS_BATCH_FILE_FOOTER_STR)
                batch_file.write(entrylist[1]) # Second Month
        else:
            print("error with generated qcas command entries: Incorrect size: " + str(entrylist.size()))

        # input: psl file name, mode to update version/month/year
        # output: updated complete psl file name
    def increment_version(self, file_name_input='', mode='version'):
        outputstr = " "

        # save filetype
        ext = file_name_input.split('.')[-1]
        filename = file_name_input.split('.')[0]
        # filename, ext = os.path.splitext(file_name_input)

        fileparts = filename.split('_')
        qcas_str = fileparts[0]
        qcas_year = int(fileparts[1])
        qcas_month = int(fileparts[2])
        qcas_version = int(fileparts[3][1:]) # remove first char from string "v03"

        if mode == 'version':
            qcas_version += 1                
        elif mode == 'month':
            if qcas_month < 12:
                qcas_month += 1
            else:
                qcas_month = 1
                qcas_year += 1 # New Year
                
        elif mode == 'year':
            generated_year = int(datetime.now().year)
            qcas_year = str(generated_year)
            
        else:
            print("unknown mode: " + mode)
            sys.exit(1)

        if qcas_month < 10:
            qcas_month = "0" + str(qcas_month)
        else:
            qcas_month = str(qcas_month)

        if mode == 'version': 
            return(qcas_str + "_" +
                str(qcas_year) + "_" +
                qcas_month + "_v" + 
                self.format_version(qcas_version) + "." + ext)
        elif mode == 'month': 
            return (qcas_str + "_" +
                    str(qcas_year) + "_" +
                    qcas_month + "_v" + 
                    self.format_version(1) + "." + ext) # version always 1

    # input: msl file name, flag for new month
    # output: updated complete msl file name
    def update_msl_file(self, file_name_input, new_month):
        filename = file_name_input.split('.')[0]

        fileparts = filename.split('_')
        qcas_str = fileparts[0]
        qcas_year = int(fileparts[1])
        qcas_month = int(fileparts[2])
        # qcas_version = int(fileparts[3][1:]) # remove first char from string "v03"

        if not new_month: 
            # generated_month = int(datetime.now().month)
            generated_month = qcas_month
        else:
            generated_month = qcas_month + 1

            if generated_month > 12:
                generated_month = 1 # handle new years
                qcas_year += 1 # inc year

        if generated_month < 10:
            generated_month = "0" + str(generated_month)
        else:
            generated_month = str(generated_month)

        # qcas MSL file is always a v01
        return "qcas_" + str(qcas_year) + "_" + generated_month + "_v01.msl" 
        
    # input: none
    # output: two (2) strings in a list that is used to update the current qcas.bat file
    # 
    def read_qcas_bat_file(self):
        batch_file = list()
        outputstr_list = list()
        entries_to_change = 0
        outputstr = ""
        command_list_str = list()
        
        with open(self.filename, 'r') as batch_file:
            file = batch_file.readlines()

            # read file and build a list of command string
            # command_list_str[0] is current month
            # command_list_str[1] is next month
            while entries_to_change < 2: 
                for line in file:
                    if line.startswith("epsigQCAS3_5.exe"):
                        entries_to_change += 1
                        command_list_str.append(line) 
                        
            ## If a new game is being approved in a month.
            if self.new_games_new_month == True:
                # Current Month PSL files are not going to be used, use next month (command_list_str[1]) inc versions to be used as current month 
                # use last month PSL as Current PSL, inc Version
                # use last month MSL as Current MSL
                fields = command_list_str[1].split(' ')
                command = fields[0]
                path = fields[1]
                msl = fields[2]
                psl = fields[4]
                new_psl_file = self.increment_version(psl, mode='version') # inc PSL version
                
                outputstr = command + " " + path + " " + msl + " " + self.new_tsl_file + " " + new_psl_file
                outputstr_list.append(outputstr)
                
                # Next Month
                new_month_msl = self.update_msl_file(msl, new_month=True)                 # new msl inc
                new_month_psl = self.increment_version(psl, mode='month')                 # new psl v1
                
                outputstr = command + " " + path + " " + new_month_msl + " " + self.new_tsl_file + " " + new_month_psl
                outputstr_list.append(outputstr)
            else: 
                # Current Month MSL remains the same
                # Current Month PSL inc version
                fields1 = command_list_str[0].split(' ')
                command = fields1[0]
                path = fields1[1]
                msl = fields1[2]
                psl = fields1[4]
                current_month_psl = self.increment_version(psl, mode='version') 

                outputstr = command + " " + path + " " + msl + " " + self.new_tsl_file + " " + current_month_psl
                outputstr_list.append(outputstr)
                
                # Next Month MSL remains the same
                # Next Month PSL inc version.
                fields2 = command_list_str[1].split(' ')
                command = fields2[0]
                path = fields2[1]
                msl = fields2[2]
                psl = fields2[4]
                next_mont_psl = self.increment_version(psl, mode='version')

                outputstr = command + " " + path + " " + msl + " " + self.new_tsl_file + " " + next_mont_psl
                outputstr_list.append(outputstr)

        return outputstr_list


class QCAS_TSL_Generator:
    
    # GUI constructor
    def __init__(self):
        self.root = Tk()
        self.setup_GUI()
        self.new_fname = ''

        if not os.path.isdir(OUTPUT_DIR): 
            os.mkdir(OUTPUT_DIR)

    def update_fname(self, fname): 
        # split and read year month and day. 
        s = os.path.splitext(fname)[0] 
        entries = s.split('_')
        
        # enstries = ['qcas', '2021', '12', 'v01']
        version = int(entries[3][1:])
        month = int(entries[2])
        year = int(entries[1])

        # just inc version for now. 
        version = version + 1
        output_fname = ''
        if version < 10: 
            output_fname = "qcas_"+str(year)+"_"+str(month)+"_v0"+str(version)+".tsl"
        else: 
            output_fname = "qcas_"+str(year)+"_"+str(month)+"_v"+str(version)+".tsl"

        return output_fname

    def clean_tab_delimited_file(self): 
        fname = self.tab_delimited_filename_tf.get()
        fname_cleaned = os.path.splitext(os.path.basename(fname))[0] + "_cleaned.txt" # without .txt
        with open(fname,'r') as file, open(os.path.join(OUTPUT_DIR, fname_cleaned),'w+') as outfile:
            for line in file.readlines():
                if not line.strip(): # remove blank lines
                    continue
                if line:
                    outfile.write(line)
        return fname_cleaned

        # input: button event
        # output: none
    def handleButtonPress(self, myButtonPress):
        if myButtonPress == '__tab_delimited_file__':
            tmp = filedialog.askopenfile(initialdir=QCAS_DIRECTORY, title="Select TAB Delimited file")

            if tmp:
                filename = tmp.name

                self.tab_delimited_filename_tf.delete(0, END)
                self.tab_delimited_filename_tf.insert(0, filename)
            
        elif myButtonPress == '__current_tsl_file__':
            tmp = filedialog.askopenfile(initialdir=QCAS_DIRECTORY, title="Select current TSL file")

            if tmp:
                filename2 = tmp.name

                self.current_tsl_filename_tf.delete(0, END)
                self.current_tsl_filename_tf.insert(0, filename2)
                # new TSL filename auto update
                self.v.set(self.update_fname(os.path.basename(filename2)))


        elif myButtonPress == '__start__':

            if (os.path.isfile(self.tab_delimited_filename_tf.get())) and (os.path.isfile(self.current_tsl_filename_tf.get())):
                fname, ext = os.path.splitext(os.path.basename(self.tab_delimited_filename_tf.get()))
                if ext == '.txt': 
                    # processing TAB delimited file
                    self.new_fname = self.clean_tab_delimited_file()
                    # update text field
                    # self.current_tsl_filename_tf.delete(0, END)
                    # self.tab_delimited_filename_tf.insert(0, new_fname) 

                    new_game_list = self.genNewTSLEntries()
                    concatenated_game_list = self.concatenateTSLfiles(new_game_list)               
                    sorted_game_list = self.sortFile(concatenated_game_list)
                    
                    final_new_game_list = self.sortFile(self.dropDuplicates(sorted_game_list))

                    self.write_game_list_to_file(final_new_game_list)

                    generate_qcas_batch_file = messagebox.askquestion("qcasTSLgenerator: Finished!", "Generated new TSL file: " + self.new_tsl_filename_tf.get() +
                                                      "\nDo you want to generate the qcas_autogenerated.bat file?")

                    if (generate_qcas_batch_file == 'yes'):
                        tmp = filedialog.askopenfile(initialdir=QCAS_DIRECTORY, title = "Select current qcas.bat file", filetypes = (("bat files","*.bat"),("all files","*.*")))
                        if tmp: 
                            self.current_filename = tmp.name # get filename

                            new_games_in_new_month = messagebox.askyesno("New Games in New Month?", "Is a new game being approved in a new month?")
                            print("New Games in New Month?: " + str(new_games_in_new_month))
                            batch_file = QCAS_batch_file(self.current_filename, self.new_tsl_filename_tf.get(), new_games_in_new_month)

                            # get batch_file generated entries
                            entries = batch_file.read_qcas_bat_file()

                            # replace lines
                            batch_file.replace_qcas_command(entries)
                            print("Generated new qcas.bat file: " + batch_file.get_filename())

                        print("Generated new TSL file: " + self.new_tsl_filename_tf.get())
                    else:
                        print("Generated new TSL file: " + self.new_tsl_filename_tf.get())

                    # Advise to check for removed games in TSL file
                    messagebox.showinfo("Remove Approval Withdrawn Games", "Please make sure that you search and REMOVE any APPROVAL WITHDRAWN games prior to generating the QCAS datafiles")
                elif ext == '.xls': 
                    messagebox.showinfo("Unsupported .xls", "Does not support reading .xls file at the moment, please export this file as a TAB delimited .txt file")                    
                else: 
                    messagebox.showerror("Cannot process: ", self.tab_delimited_filename_tf.get(), "Please select files first")
            else:
                messagebox.showerror("Files not Chosen!", "Please select files first")

    # input: game list
    # output: none
    def write_game_list_to_file(self, game_list):
        with open(os.path.join(OUTPUT_DIR,self.new_tsl_filename_tf.get()), 'w+') as outfile:
            for game in game_list: 
                outfile.write(game)

    # input: none
    # output: setup gui
    def setup_GUI(self):
        self.root.wm_title("qcasTSLgenerator v"+VERSION)
        self.root.resizable(0,0)
        help_text = "This script automates the generation of the QCOM Casino Datafile TSL file.\n"
        ttk.Label(self.root, justify=LEFT,
                  text = help_text).grid(row = 0, columnspan=2, padx=3, pady=3)

        # Button
        button_Choose_TAB_delimited_file = ttk.Button(self.root,
                                                      text = "Choose TAB delimited file...",
                                                      width = 30,
                                                      command = lambda: self.handleButtonPress('__tab_delimited_file__'))                                             
        button_Choose_TAB_delimited_file.grid(row=1, column=0, padx=3, pady=3, sticky='e')

        # Text Entry
        self.tab_delimited_filename_tf = ttk.Entry(self.root, width = 50)
        self.tab_delimited_filename_tf.grid(row=1, column=1)

        # Button
        button_Choose_Current_TSL_file = ttk.Button(self.root,
                                                    text = "Choose Current TSL file...",
                                                    width = 30,
                                                    command = lambda: self.handleButtonPress('__current_tsl_file__'))                                                    
        button_Choose_Current_TSL_file.grid(row=2, column=0, padx=3, pady=3, sticky='e')

        # Text Entry       
        self.current_tsl_filename_tf = ttk.Entry(self.root, width = 50)
        self.current_tsl_filename_tf.grid(row=2, column=1)

        ttk.Label(self.root, text = 'Enter new TSL filename: ').grid(row = 3, column=0, sticky='e', padx=3, pady=3)

        self.v = StringVar()    
        self.v.set("qcas_2021_12_v01.tsl")
        self.new_tsl_filename_tf = ttk.Entry(self.root, width = 50, textvariable=self.v)
        self.new_tsl_filename_tf.grid(row=3, column=1, padx=3, pady=3)

        # Button
        button_start = ttk.Button(self.root, text = "Start...",
                                  command = lambda: self.handleButtonPress('__start__'))
        button_start.grid(row=4, columnspan=2, sticky='se', padx=5, pady=5)        

        # debug hack
        if getpass.getuser() == 'aceretjr': 
            current_tsl = 'C:/Users/aceretjr/DJAG/OneDrive Work - Documents/WFH/dev/qcasTSLgenerator/test/qcas_2021_11_v01.tsl'
            self.current_tsl_filename_tf.delete(0, END)
            self.current_tsl_filename_tf.insert(0, current_tsl)

            tab_delimited_fname = 'C:/Users/aceretjr/DJAG/OneDrive Work - Documents/WFH/dev/qcasTSLgenerator/test/20211208 - Tech Services Item List.txt'
            self.tab_delimited_filename_tf.delete(0, END)
            self.tab_delimited_filename_tf.insert(0, tab_delimited_fname)

            self.v.set(self.update_fname(os.path.basename(current_tsl))) # new tsl filename

        self.root.mainloop()

    # input: TAB delimited file, exported from MS Excel.
    # output: Filename of new TSL game entries
    def genNewTSLEntries(self):
        new_tsl_entries = list() 
        try:
            self.new_fname
            with open(os.path.join(OUTPUT_DIR, self.new_fname), 'r', encoding='utf-8') as infile:             
            #with open(self.tab_delimited_filename_tf.get(), 'r', encoding='utf-8') as infile: 
                next(infile) #ignore header
                
                input_fieldnames = ['game_name', 'manufacturer', 'approval_status', 
                    'approval_date', 'market','ssan','vid_type','binimage','bin_type']
                reader = csv.DictReader(infile, delimiter='\t', fieldnames=input_fieldnames)

                for row in reader:
                    # Remove commas in game name
                    # If you want to replace it with another symbol change the following 
                    #   line to: .replace(",","[INSERT SYMBOL HERE]")
                    cleaned_game_name = str(row['game_name']).replace(",", "")
                    
                    # remove non-ascii characters using re
                    cleaned_game_name = re.sub(r'[^\x00-\x7f]',r'', cleaned_game_name)
                
                    # Process Video Type & Append to game name
                    if row['vid_type'].lower() == 'video':
                        cleaned_game_name += "-V"
                    else :
                        cleaned_game_name += "-S"

                    # Process Binimage type
                    if row['bin_type'] == 'BIN LINK FILE':
                        my_bin_type = 'BLNK'
                    elif row['bin_type'] == 'PSA 32':
                        my_bin_type = "PS32"
                    elif row['bin_type'] == 'HMAC SHA1':
                        my_bin_type = "SHA1"
                    else:
                        # sys.exit('Unknown binimage type %s' % row['bin_type'])         
                        print('Unknown binimage type %s' % row['bin_type'])         

                    game_entry = str("%02d,%010d,%-60s,%-20s,%4s\n" % 
                        (int(row['manufacturer']), int(row['ssan']), 
                        cleaned_game_name, row['binimage'], my_bin_type))
                    new_tsl_entries.append(game_entry)
            
        except csv.Error as e: 
            sys.exit('file %s, line %d: %s' % (filename, reader.line_num, e))
        
        return new_tsl_entries

    # input: none:
    # output: list of games, one line each
    def scan_old_TSL_files(self):
        file = list()
        with open(self.current_tsl_filename_tf.get(), 'r') as old_TSL_file:
            file = old_TSL_file.readlines()

        return file    

	# input:    None
	# output:   Concatenated game list created, 
    def concatenateTSLfiles(self, new_game_list):
        old_game_list = self.scan_old_TSL_files()
        concatenated_tsl_game_list = new_game_list + old_game_list
        
        return concatenated_tsl_game_list

	# input: a sorted games list
	# output: games list with no duplicate entries. 
    def dropDuplicates(self, game_list):           
        
        return list(set(game_list))

    # input: Game List that is unordered
    # output: Game List that is ordered.
    def sortFile(self, game_list):
        #input_fieldnames = ['manufacturer', 'ssan', 'game_name', 'binimage', 'bin_type']
        #data = csv.DictReader(game_list, delimiter=',', fieldnames=input_fieldnames)
        #sortedlist = sorted(data, key=operator.itemgetter('manufacturer','game_name','ssan'))
        my_sorted_game_list = list()
        
        data = csv.reader(game_list, delimiter=',')
        sortedlist = sorted(data, key=operator.itemgetter(0,2,1))
        
        for item in sortedlist:
            line = ",".join(item) + '\n' # convert list to string. 
            my_sorted_game_list.append(line)
            
        return my_sorted_game_list
    

def main():
    app = QCAS_TSL_Generator()    
    
if __name__ == "__main__": main()
