import random
# import sys
# import time

class UserInput:
    '''
    UserInput class is defined to take correct input from user.

    '''
    def takeInput(self):
        '''
        takeInput function will only take input from user.
        
        '''
        userInput = input()
        return userInput

    def numberChecker(self,number):
        '''
        numberChecker function is used for checking the input 
        is number or not by validating number parameter value.

        '''
        try:
            # if ! is input then either exit the program or go to home.
            if number == "!": 
                return number

            # if input is not number then an expection is raised 
            # and flow move to except code.
            typecastedNumber = int(number)
            return typecastedNumber

        except:
            # unless the user provides a valid number input,
            # program will recursively take input from user.
            print(" Enter a valid number :")
            return self.numberChecker(self.takeInput())

class  GuessingNumber:
    '''
    GuessingNumber class is used to generate a random number.

    '''
    def numberGenerator(self):
        '''

        numberGenerator function is used to generate a random number 
        in between 0 to 100 using random module.

        '''
        generatedNumber = random.randint(0,100)
        return generatedNumber

class CheckNumber(GuessingNumber):
    '''

    CheckNumber class is used for comparing the user input and number 
    generated by GuessingNumber class and return the result.

    '''
    def generatedNumber(self):

        '''
        As we can see that we are inheriting the GuessingNumber class
        we are using its numberGenerator function for Extream mode.

        '''
        return GuessingNumber.numberGenerator(self)

    def sameMatchNumbersCode(self, gussedNumber, generatedNumber):

        '''
        sameMatchNumbersCode is a function that used for comparing
        gussedNumber and generatedNumber.

        '''
        # if gussedNumber is equal generatedNumber then we return
        # 0 as match found and generatedNumber.
        if gussedNumber == generatedNumber:
            return 0

        # elif gussedNumber is less than generatedNumber then we 
        # return -1 to state guessed number is low.. so that user
        # can give his next input as more than that number.
        elif gussedNumber < generatedNumber:
            return -1

        # elif gussedNumber is greater than generatedNumber then
        # we return 1 to state guessed number is high.. so that 
        # user can give his next input as less than that number.
        else:
            return 1

    def matchNumber(self, gussedNumber):
        '''
        
        matchNumber is used for comparing the generated number with
        user gussed number by taking one parameter gussedNumber.

        '''
        # print(self.gussedNumber())
        # generatedNumber is 2nd constrain generated by 
        # generatedNumber() used to compare with gussedNumber.
        generatedNumber = self.generatedNumber()

        sameMatchNumbersCodeReturn = self.sameMatchNumbersCode(gussedNumber,generatedNumber)
        return sameMatchNumbersCodeReturn, generatedNumber

    def matchNumbers(self,gussedNumber,generatedNumber):
        '''
        
        matchNumbers is used for comparing the generated number with
        user gussed number by taking it as in gussedNumber parameter.

        '''

        sameMatchNumbersCodeReturn = self.sameMatchNumbersCode(gussedNumber,generatedNumber)
        return sameMatchNumbersCodeReturn

class Design:
    '''
    Design class is used to add design to the output.

    '''
    def design(self,specialCharacter):
        '''
        design function is used print characters like @, #, $, 
        by taking type of caharacter in specialCharacter parameter.

        '''
        print(specialCharacter*50)


class Message(Design):
    '''
    Message class is used to print the output message to the user
    and inherits Design calss to give a design to the message.

    '''
    def toHome(self):
        '''
        print's to show menu, if user exit the Easy or Extream mode.
        '''
        return "\n Thanks for playing. Choose another option.. "

    def bye(self):
        '''
        print bye message when user exit the program.
        '''
        return "\n Thanks for playing. Come again to play.. Bye..."

    def welcome(self):
        '''
        print's welcome message when user enters the game.
        '''
        print("\n### Welcome to the Jungle ###\n")

    def showMenu(self):
        '''
        print to show menu.
        '''
        # time.sleep(1)
        self.design("#")
        # print(" type -> ! -> to exit the game. ")
        print(" Press SHIFT +\n")
        print(" 1. Exit ")
        print(" 2. Easy ")
        print(" 3. Extream ")
        self.design("#")

    def exitChoice(self):
        '''
        print to show menu when user choose the game mode.
        '''

        self.design("#")
        print(" Press SHIFT +\n")
        print(" 1. Exit ")
        self.design("#")
        print(" Enter your number :")

class StartGame:
    '''
    StartGame class defins the Easy and Extream mode working.

    '''
    msg = Message()
    dg = Design()

    def checkUserInput(self, generatedInput, this_msg):
        '''
        to check when user want to exit the game mode or 
        program and the given input is valid or not.

        '''
        ui = UserInput()

        inputNumber = ui.takeInput()

        # if user want to exist the program.
        if inputNumber == "!":
            # exit() # shows dialogue box to kill
            self.dg.design("@")
            if generatedInput == 0:
                print(this_msg+\
                self.msg.toHome()+"\n"+("@"*50))
                return inputNumber
            print(this_msg+str(generatedInput)+\
                self.msg.toHome()+"\n"+("@"*50))
            return inputNumber
        
        userInput = ui.numberChecker(inputNumber)
        # print(isinstance(userInput,int))

        # if user want to exit the game mode.
        if userInput == "!":
            # exit() # shows dialogue box to kill
            self.dg.design("@")
            if generatedInput == 0:
                print(this_msg+\
                self.msg.toHome()+"\n"+("@"*50))
                return userInput
            print(this_msg+str(generatedInput)+\
                self.msg.toHome()+"\n"+("@"*50))
            return userInput

        return userInput

    def sameCode(self, output):
        '''
        to print the message based on status of this guess number.
        '''
        # if number is matched.
        if output == 0:
            self.dg.design("*")
            print(" You Made It!" )
            print(self.msg.toHome())
            self.dg.design("*")
            return "home"

        # elif the gussed number is less.
        elif output == -1:
            self.dg.design("*")
            print(" Your Guess is Low... ")
            self.dg.design("*")

        # elif the gussed number is more
        else:
            self.dg.design("*")
            print(" Your Guess is High... ")
            self.dg.design("*")

    def Easy(self):
        '''
        to run Easy mode program.
        '''
        cn = CheckNumber()
        gn = GuessingNumber()

        # generate the number only once.
        generatedInput = gn.numberGenerator()
        # print(generatedInput)
        gusse_msg = " Gussed number was "

        while(True):         
            
            # to check if user input is "!" or a valid number. 
            userInput = self.checkUserInput(generatedInput, gusse_msg)

            # if user exit the Easy mode.
            if userInput == "!":
                return "home"

            # to compare the user input and generated number.
            output = cn.matchNumbers(userInput,generatedInput)

            # if user input matches generated number.
            ifyoumadeit = self.sameCode(output)
            if ifyoumadeit == "home":
                return "home"

    def Extream(self):
        '''
        to run Extream mode program.
        '''
        cn = CheckNumber()

        Flag = 1
        generatedInput = 0
        gusse_msg = " Gussed number was "
        nothing_msg = " Nothing Gussed Yet! "
        
        while(True):  

            # if user want to exit as soon as he/she enters..
            if Flag == 1:
                Flag =  0
                userInput = self.checkUserInput(0,nothing_msg)

            # to check if user input is "!" or a valid number.  
            else:
                userInput = self.checkUserInput(generatedInput, gusse_msg)

            # if user exit the Extream mode.
            if userInput == "!":
                return "home"

            # to compare the user input and generated number.
            output, generatedInput = cn.matchNumber(userInput)

            # if user input matches generated number.
            ifyoumadeit = self.sameCode(output)
            if ifyoumadeit == "home":
                return "home"

class EndGames:
    '''
    EndGames class is starting point of program and shows menu.

    '''

    # creating objects of the classes which we have to use.
    ui = UserInput()
    msg = Message()
    dg = Design()
    s = StartGame()

    # showing welcome and menu message/design.
    msg.welcome()
    msg.showMenu()

    # running while loop which terminates when user click "!"
    # to exit the program.
    while(True):

        userChoice = ui.takeInput()

        # to exit the program.
        if userChoice == "!":
            # exit() # shows dialogue box to kill
            dg.design("@")
            exit(msg.bye()+"\n"+("@"*50))
            # sys.exit(msg.bye()+"\n"+("@"*50))

        # to start Easy mode.
        elif userChoice == "@":
            msg.exitChoice()
            homeCheck = s.Easy()
            if homeCheck == "home":
                msg.showMenu()
                continue 
            # break

        # to start Extream mode.
        elif userChoice == "#":
            msg.exitChoice()
            homeCheck = s.Extream()
            if homeCheck == "home":
                msg.showMenu()
                continue 

        # to say user to give correct input that is SHIFT 
        # + 1 or 2 or 3.
        else:
            dg.design("$")
            msg.showMenu()
            dg.design("$")
            continue
        
if __name__ == "__main__":

    # starting point.
    EndGames()




