#!/usr/bin/env python3
"""
@author Ashaya Sharma
@date   July 11, 2015 (initial)

Algebra 1

Program that randomly generates basic algebra simplification problems involving constants and 3 variables
in a GUI and checks that user responses are correct.

MathSitter Beta

"""

import random

#tkinter is the GUI library used for project documentation found at http://effbot.org/tkinterbook/entry.htm
from tkinter import *
from tkinter import messagebox
import loaderMain

class Algebra1(object):

    """
    This function generates a random algebraic expression to solve.
    @returns string containing algebraic expression that contains between 1 and 3 variables
    and ranges from 2 terms to 7 terms. Does not support terms that are fractions

    FUTURE: add support for brackets and fractions
    """
    def generateExpression(self):

            variableList = ['x','y','z'];
            operatorList = ['-', '+'];
            expression = ""; 
            #pick random size of string between 5 and 20
            expressionSize = random.randint(5, 20);
            #put either a "-" or a variable or a number at the start
            tossUp = random.randint(1,3);
            #print("TOSS UP: "+str(tossUp));
            #put a number at the start
            if (tossUp == 1):
                    expression = expression + str(random.randint(2,9));
                    #print("here is expression "+ expression);
            #put a variable at the start
            elif (tossUp == 2):
                    expression = expression + variableList[random.randint(0,2)];
                    #print("here2 "+ expression);

            else:
                    expression = expression + '-';
                    #print("here3 "+ expression);


            #loop through string
            for x in range(1, expressionSize+1):
                    #if previous element in string was number, make next operator or variable
                    if (expression[x-1].isdigit()):
                            tossUp = random.randint(1,2);
                            if (tossUp == 1):
                                    #pick randomly between 1 and 2: if 1 put a operator otherwise a variable
                                    expression = expression + operatorList[random.randint(0,1)];
                            else:
                                    expression = expression + variableList[random.randint(0,2)];
                    #else if previous was operator, next is number or variable
                    elif (expression[x-1] == '-' or expression[x-1] == '+'):
                            tossUp = random.randint(1,2);
                            #pick randomly between 1 and 2: if 1 put a number otherwise a variable 
                            if (tossUp == 1): 
                                    expression = expression + str(random.randint(2,9));
                            else:
                                    expression = expression + variableList[random.randint(0,2)]; 
                    #else (previous was variable), next is operator
                    else:
                            expression = expression + operatorList[random.randint(0,1)];
            #at end of string check the last element, if it is an operator, add a number to the end so the expression is valid
            if (expression.endswith('-') or expression.endswith('+')):
                    expression = expression + str(random.randint(1,9));
            # TESTING PURPOSES print ("FINAL expression: " + expression);
            return expression;
    """
    @param expression to solve 
    @returns count of coefficients of each term in simplified form in the following format as a tuple: (x-coefficients, y-coefficients, z-coefficients, constant-coefficients)
    @requires the expression contains only variables named x,y,z. Does not at this moment support fractions or brackets 

    """
    def expressionSimplifier(self, currentExpression):

        # TESTING PURPOSES print("Expression in function: " + currentExpression);

        #initialize the counts for each constant
        xCount = 0;
        yCount = 0;
        zCount = 0;
        constCount = 0;

        index = 0;
        negativeFlag = 1;
        #loop intil end of string
        while(index < len(currentExpression)):
                
                #following line is for testing purposes
                #print("Expr\t"+currentExpression+" |INDEX:\t"+str(index) + " |LENGTH:\t"+ str(len(currentExpression)) + " |X\t"+str(xCount)+ " |y\t"+str(yCount)+ " |z\t"+str(zCount)+ " |cons\t"+str(constCount));
                
                #if the expression is an operator check if its negative and if so set the negative flag on
                if(currentExpression[index] == '+' or currentExpression[index] == '-'):
                        if (currentExpression[index] == '-'):
                               # print("neg") #testing purposes 
                                negativeFlag = -1;
                        else:
                                # print("pos") #testing purposes
                                negativeFlag = 1;
                        #check if the next is a number
                        if(index+1< len(currentExpression)and currentExpression[index+1].isdigit()):
                                #while its still a number, keep updating the coefficient
                                temp = 0;
                                while(index+1< len(currentExpression) and currentExpression[index+1].isdigit()):
                                        index = index + 1;
                                        #store the coefficient
                                        temp = temp*10 + int(currentExpression[index]);

                                #increment the count of the variable or constant by the coefficient depending on which variable is at the current point in string

                                if(index+1<len(currentExpression) and currentExpression[index+1]=='x'):
                                        xCount = xCount + negativeFlag*temp;
                                        index = index + 2;
                                elif(index+1<len(currentExpression) and currentExpression[index+1]=='y'):
                                        yCount = yCount + negativeFlag*temp;
                                        index = index + 2;
                                elif(index+1<len(currentExpression) and currentExpression[index+1]=='z'):
                                        zCount = zCount + negativeFlag*temp;
                                        index = index + 2;
                                else:
                                        constCount = constCount + negativeFlag*temp;
                                        index = index + 1;
                                        
                        #increment the count of the variable if it has no coefficients

                        elif(index+1<len(currentExpression) and currentExpression[index+1]=='x'):
                                xCount = xCount + negativeFlag;
                                index = index + 2;
                        elif(index+1<len(currentExpression) and currentExpression[index+1]=='y'):
                                yCount = yCount + negativeFlag;
                                index = index + 2;
                        elif(index+1<len(currentExpression) and currentExpression[index+1]=='z'):
                                zCount = zCount + negativeFlag;
                                index = index + 2;

                #if we are at a coefficient
                elif(currentExpression[index].isdigit()):
                        temp = int(currentExpression[index]);
                        #extract the coefficient
                        while(index+1< len(currentExpression) and currentExpression[index+1].isdigit()):
                                index = index + 1;
                                #store the coefficient
                                temp = temp*10 + int(currentExpression[index]);

                        if(index+1<len(currentExpression) and currentExpression[index+1]=='x'):
                                xCount = xCount + temp;
                                index = index + 2;
                        elif(index+1<len(currentExpression) and currentExpression[index+1]=='y'):
                                yCount = yCount + temp;
                                index = index + 2;
                        elif(index+1<len(currentExpression) and currentExpression[index+1]=='z'):
                                zCount = zCount + temp;
                                index = index + 2;
                        else:
                                constCount = constCount + temp;
                                index = index + 1;
                                
                #if we are at a variable at the beginning, increment the count of variable depending on which variable is in the string   
                elif(currentExpression[index] == 'x'):
                        xCount = xCount + 1;
                        index = index + 1;
                elif(currentExpression[index] == 'y'):
                        yCount = yCount + 1;
                        index = index + 1;
                elif(currentExpression[index] == 'z'):
                        zCount = zCount + 1;
                        index = index + 1;
                else:
                        index = index + 1

        #testing purposes print(str(xCount)+" "+str(yCount)+" "+str(zCount)+" "+str(constCount));
        return(xCount, yCount, zCount, constCount);



    """
    @param question expression to be simplified and user's simplified expression
    @return true if user simplified expression matches question expression and false otherwise
    """

    def checkExpressions(self, expression, userExpression):
            userSimplified = self.expressionSimplifier(userExpression);
            answerSimplified = self.expressionSimplifier(expression);

            if(userSimplified[0] == answerSimplified[0] and userSimplified[1] == answerSimplified[1] and userSimplified[2] == answerSimplified[2] and userSimplified[3] == answerSimplified[3]):
                    return True;
            else:
                    return False;

    """ Checks if user simplified their constants or not
    @param message entered by user
    @return true if user simplified message, false if they didnt
    """
    def simplifiedNumbers(self, message):
        constantCount = 0;
        #loop through message
        for x in range(0,len(message)):
            if message[x].isdigit():
                #check if its the last digit if so its a constant, add it to the temporary string
                if(x+1 == len(message)):
                    constantCount = constantCount + 1;
                elif(message[x+1] == '-' or message[x+1] == '+'): #if operator add last digit to temporary string
                    constantCount = constantCount + 1;
        #if there is more than one constant return false
        if constantCount > 1:
            return False
        else:
            return True

    """
    Updates score depending on correctness of user input and updates GUI
    Requests new expression if user is correct
    @modifies score, checkMark image, question

    """

    def incrementScore(self, event):
            self.attemptedQuestions = self.attemptedQuestions + 1
            userAnswer = self.userResponse.get()
            #trims whitespace
            userAnswer.replace(" ", "") 
            answerKey = self.computerQuestion.get()

            #check if the simplified expressions of both match and the user enters a valid expression and increment their score if they are right
            #generate a new expression for them to solve if they keep on going
            if ((self.checkExpressions(answerKey, userAnswer)) and (self.quickExpressionFilter(userAnswer) == True) and (self.simplifiedNumbers(userAnswer)) == True):
                    tempscore = int(self.score.get()) 
                    tempscore = tempscore +1
                    self.questionsCorrect = self.questionsCorrect + 1

                    #check if score is 10 and popup congratulations message
                    if (tempscore == 10):
                            messagebox.showinfo("Congratulations!", "Well Done! You mastered the module. You can keep practicing or quit");
                    
                    self.score.set(tempscore)
                    scoreString = str(tempscore)+ "/10"
                    self.scoreCount.set(scoreString)
                    self.computerQuestion.set(self.generateExpression());
                    self.checkMark.config(image = self.pictures[1])
                    #clear the answer textbox
                    self.userResponse.set("")

            #if they are incorrect but have a score greater than 5, send their score down to 5 otherwise send them back to 0
            else:
               tempscore = int(self.score.get())
               newscore = 0
               if (tempscore > 5):
                       self.score.set(5)
                       newscore = 5
               else:
                       self.score.set(0)
                       newscore = 0
                #set the score accordingly and picture to X
               scoreString=str(newscore)+"/10"
               self.scoreCount.set(scoreString)
               self.checkMark.config(image = self.pictures[2])
               self.computerQuestion.set(answerKey)

    """
    Checks that user did not enter the question 
    @param expression that user enters
    @returns false if user entered term with variable more than once (likely they entered question or not fully simplifed expression)
            and true otherwise
    """
    def quickExpressionFilter(self, expression):
            if((expression.count('z') > 1) or (expression.count('y') > 1) or (expression.count('x') > 1)):
                    return False
            else:
                    return True

    #Closes the program
    def closeGame(self):
            messagebox.showinfo("Quit", "Program closed")
            self.root.destroy()
            self.writeScoretoFile()
            m1 = loaderMain.Loader(self.username.get());
            m1.root.mainloop() 
            

    """
    Constructor sets up main GUI
    """
    def __init__(self, username):
        self.root = Tk()
        self.root.title("Basic Algebra")

        #set window to fullscreen and focus
        #w, h = self.root.winfo_screenwidth(), self.root.winfo_screenheight()
        #self.root.overrideredirect(1)
        #self.root.geometry("%dx%d+0+0" % (w, h))
        
        self.root.focus_set()
        self.username=StringVar();
        self.username.set(username)        

        self.root.configure(bg = "#4C4CFF")
        self.mainFrame = Frame(self.root, width = 250);
        self.topFrame = Frame(self.root, width = 250);

        #place widgets on screen
        self.topFrame.pack();
        self.mainFrame.pack();

        self.score = StringVar();
        self.score.set(0);
        self.scoreCount = StringVar();
        self.scoreCount.set("0/10");
        self.userResponse = StringVar();
        self.userResponse.set("")
        self.computerQuestion = StringVar();

        self.attemptedQuestions = 0;
        self.questionsCorrect = 0;
        
        #load pictures of check mark and x
        self.pictures = (PhotoImage(file = "Images/nop.gif"), PhotoImage(file = "Images/cm2.gif"), PhotoImage(file = "Images/x2.gif"));                                                                                               
        #display an initial problem
        self.computerQuestion.set(self.generateExpression());
        
        self.GameName = Label(self.topFrame, bg = "#4C4CFF", pady = 15, width = 20, text = self.username.get() + ": Algebra1", font = ('Helvetica', 12), fg = "#e5e5ff");
        self.GameName.pack(side = LEFT);
        self.butt2 = Button(self.topFrame, text = "Close", font = ('Helvetica', 9), command = self.closeGame);
        
        #paddingLabel1.pack(side = LEFT)
        self.butt2.pack(side = RIGHT);

        #set background colors
        self.mainFrame.configure(bg = "#4C4CFF");
        self.topFrame.configure(bg = "#4C4CFF");

        #Display question (takes text from generateExpression)
        self.question = Label(self.mainFrame, bg = "#4C4CFF", fg = "White", pady = 100, padx = 300, relief = GROOVE, textvariable = self.computerQuestion, font = ('Courier', 24), width = 100);
        self.question.pack();

        #creates an invisible layer which makes buttons centered (necessary on rpi for aesthetic purposes)
        self.paddingLabel2 = Label(self.mainFrame, bg = "#4C4CFF", pady = 30, padx = 30);
        self.paddingLabel2.pack();

        #entry to submit answer
        self.entry1 = Entry(self.mainFrame, borderwidth= 20, bg = "#4C4CF0", fg = "White", relief = RAISED, font = ('Helvetica', 20), textvariable = self.userResponse);
        self.entry1.pack();

        #make a variable which holds the checkmark or cross set it to the background color to start
        self.checkMark = Label(self.mainFrame, image = self.pictures[0], bg ="#4C4CFF");

        #creates an invisible layer which makes buttons centered (necessary on rpi for aesthetic purposes)
        self.paddingLabel3 = Label(self.mainFrame, width = 75, bg="#4C4CFF");
        self.button1 = Button(self.mainFrame, text = "Submit", height = 5, width = 20, font = ('Helvetica', 10));

        #let user press the submit button or enter to submit their answer
        self.button1.bind("<Button-1>", self.incrementScore)
        self.root.bind("<Return>", self.incrementScore)

        #display the user's current score
        self.currentScore = Label(self.mainFrame, bg = "#4C4CFF", textvariable = self.scoreCount, font = ('Helvetica', 20), fg = "White", padx = 20);

        #place widgets on screen
        self.paddingLabel3.pack(side = LEFT);
        self.checkMark.pack(side = LEFT);
        self.button1.pack(side = LEFT);
        self.currentScore.pack(side = LEFT);

    '''
    Function opens a text file with scores for each user and reads and then writes the updated scores from this module to the file
    Appends to file if designated user has no entry yet
    @modifies score text file
    '''
    def writeScoretoFile(self):

        #the following lines of code open a file and find the line where the designated user is on
        #if the user is not in the file, a line 0 is indicated
        
        f = open("score.txt", 'r')
        lineCount = 0;
        lineofUser = 0;
        for line in f:
            a = line
            lineCount = lineCount + 1;
            username = "";
            tempString = ""
            startOfScores = 0;
            scoreArray = []
            for x in range (0, len(a)):
                if a[x] == '\t':
                    startOfScores = x
                    break
                else:
                    username = username + a[x]
            if (username == self.username.get()):
                lineofUser = lineCount

        #appends to file if user is not in the file yet
        if(lineofUser == 0):
            f = open("score.txt", 'a')
            #this is for module 1. arrange accordingly
            f.write('\n' + self.username.get()+ '\t'+ "0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t" + str(self.questionsCorrect) +'\t' + str(self.attemptedQuestions) + '\t' + "0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t")  
            f.close()

        #if the user is already in the file, the following few lines create an array of the scores for all the modules and modifies the one
        #corresponding to this module
        else:
            f = open("score.txt", 'r').readlines()
            temp = f[lineofUser-1]
            username2 = "";
            startOfScores = 0;
            scoreArray = []
            
            for x in range (0, len(temp)):
                if temp[x] == '\t':
                    startOfScores = x