#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Built-in modules
import os
import json
import readline
import threading 
import time
import sys

# Require install
from playsound import playsound

# Global Variables
timer = None
data = None
work_interval = None

script_dir = os.path.dirname(__file__)

class pomotimer(threading.Timer):
    def __init__(self, timeout, callback):
        self.timer = threading.Timer(timeout, callback)

        self.start_time = None
        self.pause_time = None

        # Used for creating a new timer upon renewal
        self.timeout = timeout
        self.callback = callback
    
    def start(self):
        self.start_time = time.time()
        self.timer.start()
        
    def pause(self):
        self.pause_time = time.time()
        self.timer.cancel()
        return self.remaining()
        
    def resume(self):
        self.timeout = self.remaining()
        self.timer = threading.Timer(self.timeout, self.callback)
        self.start_time = time.time()
        self.timer.start()
        return self.remaining() 
    
    def remaining(self):
        if self.pause_time is None or self.pause_time < self.start_time:
            return self.timeout - (time.time() - self.start_time)
        return self.timeout - (self.pause_time - self.start_time)
    
    def cancel(self):
        self.timer.cancel()
        
# Main Function
def main():
    global timer, data, work_interval, this_dir, this_filename
    
    print("\n"+colors.BOLD + "Hello, welcome to..." + colors.RESET)
    print_file("banner.txt" , colors.RED)
    print(color_text(colors.GREEN,"POM-OH-PIE")+": The Terminal Pomodoro Timer")
    print("♡")
    print(colors.RESET+"Enter `"+color_text(colors.CYAN,"help")+"` for a list of commands.")
    

    readline.parse_and_bind('tab: complete')
    readline.parse_and_bind('set editing-mode vi')

    # while loop repeats looking for commands as input
    while True:
        cmd = input().strip().lower()
        # Start Timer from "beginning" 
        if cmd.startswith("start"):
            if timer == None:
                args = cmd.split(' ')
                DATA_PATH = get_path("preferences.json")
                data = json.load(open(DATA_PATH, "r"))
                print("Starting Pomodoro for a...")
                print(color_text(colors.BLUE,str(data['work'])+" minute work period and a "+str(data['break'])+" minute break"))
                if len(args) == 1 and data['auto']:
                    work_interval = False
                elif len(args) == 1 and not data['auto']:
                    print("Auto has been turned off. Please specify `"+color_text(colors.CYAN,"start work")+"` or `"+color_text(colors.CYAN,"start break")+"`.", end='')
                    print(" Or turn auto back on with `"+color_text(colors.CYAN, "auto on")+"`.")
                    continue
                elif args[1] == "work":
                    work_interval = False
                elif args[1] == "break":
                    work_interval = True
                else:
                    print("Did you mean `"+color_text(colors.CYAN, "start")+"`, `"+color_text(colors.CYAN, "start work")+"` or `"+color_text(colors.CYAN, "start break")+"`?")
                    continue
                next_interval()
            else:
                print("Did you mean `"+color_text(colors.CYAN, "resume")+"`?")

        elif cmd == "end" or cmd == "stop":
            if timer != None:
                timer.cancel()
                timer = None
                print(color_text(colors.BLUE, "Timer has been ended."))
            else:
                print(color_text(colors.BOLD, "Start a timer first!"))
            
        # Quit entire program
        elif cmd == "quit":
            print(color_text(colors.BOLD,"See you later!"))
            if timer != None:
                timer.cancel()
            return

        # Pause timer
        elif cmd == "pause":
            if timer != None:
                print("Timer has been "+color_text(colors.RED, "paused")+". There are "+str(round(timer.pause()/60, 1))+" minutes left.")
            else:
                print(color_text(colors.BOLD, "Start a timer first!"))
        # Resume timer
        elif cmd == "resume":
            if timer != None:
                print("Timer has been "+color_text(colors.GREEN, "resumed")+". There are "+str(round(timer.resume()/60, 1))+" minutes left.")
            else:
                print(color_text(colors.BOLD, "Start a timer first!"))
        # Remaining time until next interval switch
        elif cmd == "remaining":
            if timer != None:
                print("There are "+color_text(colors.MAGENTA, str(round(timer.remaining()/60, 1)))+" minutes of ", end='')
                if work_interval:
                    print("your work period remaining.")
                else:
                    print("your break period left.")
            else:
                print(color_text(colors.BOLD,"Start a timer first!"))
            
        # Use OS module to clear terminal
        elif cmd == "clear":
            os.system('cls' if os.name == 'nt' else 'clear')

        #TODO: Switch intervals automatically vs manualling `start work` and `start break`
        elif cmd.startswith("auto"):
            if cmd == "auto on":
                set_pref("auto", True)
                print("Now automatically looping working time and breaks.")
            elif cmd == "auto off":
                set_pref("auto", False)
                print("Now working time and breaks must now be started manually.")
            else:
                print("Did you mean `"+color_text(colors.CYAN, "auto on")+"` or `"+color_text(colors.CYAN, "auto off"+"`?")+"`.")

        # set work or break period
        elif cmd.startswith("set ") and len(cmd.split(" ")) <= 3:
            arg = cmd.split(" ")
            if arg[1] == "work" and len(cmd.split(" ")) == 3:
                set_pref(arg[1], float(arg[2]))
                print("Work period updated to "+color_text(colors.MAGENTA, str(float(arg[2])))+" minutes.")
            elif arg[1] == "break" and len(cmd.split(" ")) == 3:
                set_pref(arg[1], float(arg[2]))
                print("Break period updated to "+color_text(colors.MAGENTA, str(float(arg[2])))+" minutes.")
            else:
                print("Did you mean `"+color_text(colors.CYAN, "set work [number of mins]")+"` or `"+color_text(colors.CYAN, "set break [number of mins]")+"`?")

        elif cmd == "mute":
            set_pref("mute", True)
            print("Pomopy has been muted. Enter `"+color_text(colors.CYAN, "unmute")+"` to unmute.")
            
        elif cmd == "unmute":
            set_pref("mute", False)
            print("Pomopy has been unmuted. Enter `"+color_text(colors.CYAN, "mute")+"` to mute.")
            
        # Print help    
        elif cmd == "help":
            print_help()
        # Print about text file
        elif cmd == "about":
            print(colors.UNDERLINE+"About:"+colors.RESET)
            print_file("about.txt", "")

        # Fun features
        elif cmd == "tomato":
            print_file("tomato.txt", colors.RED)
        elif cmd.startswith("hack"):
            args = cmd.split(' ')
            if len(args)>1 and args[1] == "save":
                new_txt = input("Input your troll text here: ")
                set_pref("troll", new_txt)
                print("Troll text saved.")
            else:
                print_troll()
                
        # Edgecase       
        else:
            print("Sorry we don't recognize that command :(")

def next_interval():
    global timer
    global data
    global work_interval
    global this_dir
    
    work_interval = not work_interval
    DATA_PATH = get_path("preferences.json")
    data = json.load(open(DATA_PATH, "r"))

    if data['auto'] == True:
        if work_interval:
            timer = pomotimer(data['work']*60, next_interval)
            print(color_text('\033[1;31m',">> Work for "+str(data['work'])+" minutes!"))
        else:
            timer = pomotimer(data['break']*60, next_interval)
            print(color_text('\033[1;32m',">> Take a break for "+str(data['break'])+" minutes!"))
            
    else:
        if work_interval:
            timer = pomotimer(data['work']*60, finished_interval)
            print(color_text('\033[1;31m',">> Work for "+str(data['work'])+" minutes!"))
        else:
            timer = pomotimer(data['break']*60, finished_interval)
            print(color_text('\033[1;32m',">> Take a break for "+str(data['break'])+" minutes!"))
            
    DATA_PATH = get_path("preferences.json")
    data = json.load(open(DATA_PATH, "r"))
    if not data['mute']:
        DATA_PATH = get_path("digital_watch_alarm.mp3")
        threading.Thread(target=playsound, args=(DATA_PATH,), daemon=True).start()
    timer.start()

def finished_interval():
    global timer
    global this_dir
    global data
    
    print(color_text(colors.BLUE, "Timer has been ended."))
    DATA_PATH = get_path("preferences.json")
    data = json.load(open(DATA_PATH, "r"))
    if not data['mute']:
        DATA_PATH = get_path("digital_watch_alarm.mp3")
        threading.Thread(target=playsound, args=(DATA_PATH,), daemon=True).start()

    timer.cancel()
    timer = None

class colors: # You may need to change color settings depending on OS
    BOLD    = "\033[;1m"
    UNDERLINE = "\033[;4m"
    REVERSE = "\033[;7m"
    RESET = "\033[0;0m"
    BLACK = '\033[30m'
    RED = '\033[31m'
    GREEN = '\033[32m'
    YELLOW = '\033[33m'
    BLUE = '\033[34m'
    MAGENTA = '\033[35m'
    CYAN  = '\033[36m'
    WHITE = '\033[37m'

# Return text with colors
def color_text(color, string):
    return(color+string+colors.RESET)

# Set preferences with pref type and value
def set_pref(pref_type, value):
    global this_dir
    DATA_PATH = get_path("preferences.json")
    
    file = open(DATA_PATH, "r")
    prefs = json.load(file)
    prefs[pref_type] = value
    json.dump(prefs, open(DATA_PATH, "w"))

# Print file with colored text
def print_file(file_name, text_color):
    global this_dir
    DATA_PATH = get_path(file_name)
    
    f = open(DATA_PATH, 'r')
    file_contents = f.read()
    print(text_color + file_contents + colors.RESET)
    f.close()

def print_troll():
    global this_dir
    
    print(colors.GREEN, end = '')
    
    DATA_PATH = get_path("troll.txt")
    f = open(DATA_PATH, 'r')
    file_contents = f.read()
    f.close()
    for char in file_contents:
        print(char, end = '')
        time.sleep(0.0005)
    items = range(32)
    l = len(items)
    # Initial call to print 0% progress
    printProgressBar(0, l, prefix = 'Progress:', suffix = 'Complete', length = 50)
    for i in items:
        # Do stuff...
        time.sleep(0.1)
        # Update Progress Bar
        printProgressBar(i + 1, l, prefix = 'Progress:', suffix = 'Complete', length = 50)
        
    DATA_PATH = get_path("preferences.json")
    data = json.load(open(DATA_PATH, "r"))
    print("HACKING COMPLETE")
    print(colors.RESET+data['troll'])

def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
    """
    Call in a loop to create terminal progress bar
    @params:
        iteration   - Required  : current iteration (Int)
        total       - Required  : total iterations (Int)
        prefix      - Optional  : prefix string (Str)
        suffix      - Optional  : suffix string (Str)
        decimals    - Optional  : positive number of decimals in percent complete (Int)
        length      - Optional  : character length of bar (Int)
        fill        - Optional  : bar fill character (Str)
        printEnd    - Optional  : end character (e.g. "\r", "\r\n") (Str)
    """
    percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
    filledLength = int(length * iteration // total)
    bar = fill * filledLength + '-' * (length - filledLength)
    print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
    # Print New Line on Complete
    if iteration == total: 
        print()

# Output for the help command
def print_help():
    print(colors.UNDERLINE+"Available Commands:"+colors.RESET)
    print("Make sure your volume is unmuted to hear when the timer goes off!\n")
    
    print(color_text(colors.GREEN, "Basic Commands:"))
    generate_cmd(colors.CYAN,"start","Begin the timer from start of work period")
    generate_cmd(colors.CYAN, "stop", "End Pomodoro timer")
    generate_cmd(colors.CYAN, "remaining", "Remaining time left until next interval")
    generate_cmd(colors.CYAN, "pause", "Pause the timer temporarily until resume")
    generate_cmd(colors.CYAN, "resume", "Continue from where you left off")
    generate_cmd(colors.CYAN, "clear", "Clear the terminal")
    generate_cmd(colors.CYAN, "quit", "Quit the program")

    print(color_text(colors.GREEN, "\nCustomize Settings:"))
    generate_cmd(colors.CYAN, "mute", "Mute the alarm between every interval.")
    generate_cmd(colors.CYAN, "unmute", "Default setting: Alarm rings between each interval.")
    generate_cmd(colors.CYAN, "set work [number of mins]", "Change length of work period")
    generate_cmd(colors.CYAN, "set break [number of mins]", "Change length of break period")
    generate_cmd(colors.CYAN, "auto off", "Manually switch between working and breaks")
    print("➪ if enabled, must use `"+color_text(colors.CYAN, "start work")+"` or `"+color_text(colors.CYAN, "start break")+"`")
    generate_cmd(colors.CYAN, "auto on", "Default setting: automatically switchs between work and break periods in succession")
    
    print(color_text(colors.GREEN, "\nMore Info:"))
    generate_cmd(colors.CYAN, "about", "Purpose of the program, author, credits, and more")
    generate_cmd(colors.CYAN, "tomato", "Recieve a mysterious tomato")

# Generate the format for each command in the help output
def generate_cmd(color, cmd_name, desc):
    print("`"+color_text(color, cmd_name)+"` - "+desc)

def get_path(file_name):
    global script_dir
    file_dirs = [
        os.path.join(script_dir, "..", ".."),
        script_dir,
        sys.prefix,
        "."
    ]
    for _dir in file_dirs:
        data_file = os.path.join(_dir, file_name)
        if os.path.exists(data_file):
            return data_file
    sys.exit("ERROR: File not found. Send in an issue at https://github.com/sophiezhng/pomopy/issues")

if __name__ == "__main__":
    main()
