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

import os
import time
import json
import random
import argparse
import multiprocessing
import threading
from datetime import datetime
from playsound import playsound
from getkey import getkey, keys

COLOR_TEMPLATE = '\u001b[38;5;{}m'
RESET_COLOR = '\u001b[0m'

# location = os.path.dirname(os.path.realpath(__file__))
# sound_dir = location+'/sounds'

sound_dir = os.path.expanduser('~')+'/.local/sounds'

def initialize():
    # location = os.path.dirname(os.path.realpath(__file__))
    # sound_dir = location+'/sounds'
    home_dir = os.path.expanduser('~')
    if not os.path.exists(home_dir+'/piclock.json'):
        os.chdir(home_dir)
        sounds = os.listdir(sound_dir)
        sound_map = {}
        for i in range(len(sounds)):
            sound_map[i] = sounds[i]
        settings = {}
        settings["files"] = sound_map
        settings["timer_alert"] = random.randint(0,len(sounds)-1)
        settings["alarm_alert"] = random.randint(0,len(sounds)-1)
        os.chdir(home_dir)
        with open('piclock.json','w') as file:
            json.dump(settings,file)
        return settings
    else:
        os.chdir(home_dir)
        with open('piclock.json','r') as file:
            data = json.load(file)
        return data

settings=initialize()
timer_alert_file=settings['files'][str(settings['timer_alert'])]
alarm_alert_file=settings['files'][str(settings['alarm_alert'])]

def colorize(text,colorvalue):
    return COLOR_TEMPLATE.format(colorvalue)+text+RESET_COLOR

def playaudio_wrapper(soundfile):
    while True:
        playsound(soundfile)

class Timer:
    def __init__(self,hours,minutes,seconds,custom_message="Timer has run out!"):
        self.hours = hours
        self.minutes = minutes
        self.seconds = seconds
        self.custom_message=custom_message

        #adjust values based on their unit
        while self.minutes>59 or self.seconds>59:
            while self.minutes>59:
                self.hours+=1
                self.minutes-=60

            while self.seconds>59:
                self.minutes+=1
                self.seconds-=60

    def start(self):
        while self.seconds!=0 or self.minutes!=0 or self.hours!=0:
            self.show()
            time.sleep(1)
            self.seconds -=1
            self.adjust()
        print()
        self.alert()
           
    def adjust(self):
        if self.seconds<0:
            if self.minutes>0:
                self.seconds = 59
                self.minutes -=1
            elif self.minutes==0:
                if self.hours>0:
                    self.hours-=1
                    self.minutes=59
                    self.seconds=59

    def show(self):
        end='\r'
        seconds=self.seconds
        minutes=self.minutes
        hours=self.hours
        #add 0 to single didgit numbers
        if seconds<10:
            seconds='0'+str(seconds)
        if minutes<10:
            minutes='0'+str(minutes)
        if (self.hours+1)==1000:
            hours='0'+str(hours)
        if (self.hours+1)==100:
            hours='0'+str(hours)
        if self.hours<10:
            hours='0'+str(hours)
 
        progress = f'{hours}:{minutes}:{seconds}'        
        print("\tTimer in progress : "+colorize(progress,87),end=end)

    def alert(self):
        stopper = ''
        print(ascii_alert)
        print('\t     '+colorize(self.custom_message,118))
        print('\t     '+colorize('PRESS ENTER TO STOP',42))
        os.chdir(sound_dir)
        p=multiprocessing.Process(target=playaudio_wrapper,args=(timer_alert_file,))
        p.start()
        while stopper!=keys.ENTER:
            stopper=getkey()
        p.terminate()


class Stopwatch:
    def __init__(self):
        self.hour=0
        self.minute=0
        self.second=0
        self.centisecond=0
        self.paused = True
        self.lapcount=1
        self.exitted = False

    def run(self):
        centisecond = 0.01
        second = 1
        clear_line=' '*50
        while True:
            self.display()
            time.sleep(centisecond)
            if not self.paused:
                self.centisecond+=.01
                self.adjust()
            else:
                #blink text every half of a second
                time.sleep(second/2)
                print(clear_line,end='\r')
                time.sleep(second/2)
            if self.exitted:
                break

    def start(self):
        t = threading.Thread(target=self.run,daemon=True)
        key = ''
        print(ascii_stopwatch)
        print('\t   (SPACE) to START/STOP\n\t   (r) to RESET\n\t   (l) to SPLIT LAP \n\t   (ENTER) to EXIT')
        print()
        t.start()
        while key!=keys.ENTER:
            key=getkey()
            if key==keys.LATIN_SMALL_LETTER_L:
                self.lap()
            elif key==keys.LATIN_SMALL_LETTER_R:
                self.reset()
            elif key==keys.SPACE:
                self.startorstop()
            elif key==keys.ENTER:
                self.exitted=True
                break
        print('\n')
            

    def adjust(self):
        if self.centisecond>1:
            if self.second<59:
                self.second+=1
                self.centisecond=0
            else:
                if self.minute<59:
                    self.minute+=1
                    self.second=0
                    self.centisecond=0
                else:
                    self.hour+=1
                    self.minute=0
                    self.second=0
                    self.centisecond=0

    def display(self):
        hours = self.hour
        minutes = self.minute
        seconds = self.second
        centiseconds = self.centisecond

        centiseconds = '{0:.0f}'.format(centiseconds*100)
    
        if seconds<10:
            seconds = '0'+str(seconds)
        if minutes<10:
            minutes = '0'+str(minutes)
        if (self.hour+1)==1000:
            hours='0'+str(hours)
        if (self.hour+1)==100:
            hours='0'+str(hours)
        if self.hour<10:
            hours='0'+str(hours)

        progress_str = f'Stopwatch {colorize(str(self.lapcount),255)}: {colorize(str(hours),118)}:{colorize(str(minutes),118)}:{colorize(str(seconds),118)}:{colorize(str(centiseconds),45)}'
        print('\t  '+progress_str,end='\r')

    def reset(self):
        self.hour=0
        self.minute=0
        self.second=0
        self.centisecond=0

    def startorstop(self):
        if self.paused:
            self.paused=False
        else:
            self.paused=True

    def lap(self):
        self.lapcount+=1
        self.reset()
        print()


ascii_stopwatch='''
\t   ┏━━┳┓╋╋╋╋╋╋╋╋╋╋┏┓╋╋┏┓
\t   ┃━━┫┗┳━┳━┳┳┳┳━┓┃┗┳━┫┗┓
\t   ┣━━┃┏┫╋┃╋┃┃┃┃╋┗┫┏┫━┫┃┃
\t   ┗━━┻━┻━┫┏┻━━┻━━┻━┻━┻┻┛
\t   ╋╋╋╋╋╋╋┗┛
'''

ascii_alert='''
\t    ░░█▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
\t    ██▀▀▀██▀▀▀▀▀▀██▀▀▀██
\t    █▒▒▒▒▒█▒▀▀▀▀▒█▒▒▒▒▒█
\t    █▒▒▒▒▒█▒████▒█▒▒▒▒▒█
\t    ██▄▄▄██▄▄▄▄▄▄██▄▄▄██
'''
ascii_main='''
\t   ░░░░░░░▄█▄▄▄█▄
\t   ▄▀░░░░▄▌─▄─▄─▐▄░░░░▀▄
\t   █▄▄█░░▀▌─▀─▀─▐▀░░█▄▄█
\t   ░▐▌░░░░▀▀███▀▀░░░░▐▌
\t   ████░▄█████████▄░████
'''
clear_line='\t                    '
class Alarm:
    def __init__(self,hour,minute,period,custom_message='Waky waky, eggs n\' baky!'):
        self.hour=hour
        self.minute=minute
        self.period=period
        self.custom_message=custom_message

    def start(self,verbose=True):
        current_time = ''
        hour = self.hour
        minute = self.minute
        if self.period=='pm' and self.hour!=12:
            hour = 12 + hour
        elif self.hour==12 and self.period=='am':
            hour = 12 + hour
        #add 0 to single didgit numbers
        if hour<10:
            hour = '0'+str(hour)
        if minute<10:
            minute = '0'+str(minute)

        alarm_time = f'{hour}:{minute}:00'
        print('\tPress ctrl+c to cancel alarm. Starting. . .\n')
        try:
            while current_time!=alarm_time:
                current_time = datetime.now().strftime("%H:%M:%S")
                if verbose:
                    print(f'\tAlarm set for {COLOR_TEMPLATE.format(51)}{alarm_time}{RESET_COLOR}, current time: {COLOR_TEMPLATE.format(208)}{current_time}{RESET_COLOR}',end='\r')
                time.sleep(1)
            print()
            self.alert()
        except KeyboardInterrupt:
            print("\n\n\tAlarm has been cancelled. ")
        
        
    def alert(self):
        stopper=''
        print(ascii_alert)
        print('\t   '+colorize(self.custom_message,118))
        print('\t   '+colorize('PRESS ENTER TO STOP',42))
        os.chdir(sound_dir)
        p=multiprocessing.Process(target=playaudio_wrapper,args=(alarm_alert_file,))
        p.start()
        while stopper!=keys.ENTER:
            stopper=getkey()
        p.terminate()

def getunitvalue(unit):
    #returns a number ranging from 0 to 59 for units second and minute, controlleed by arrow keys
    value = 0
    key=''
    print()
    while key!=keys.ENTER:
        if value<10:
            value_str='0'+str(value)
        else:
            value_str=str(value)
        print(f'\t\t{unit} : {value_str}',end='\r')
        key = getkey()
        if key==keys.UP:
            if value==59 and unit!='Hour':
                value=0
            else:
                value+=1
        elif key==keys.DOWN:
            if value==0:
                value=59
            else:
                value-=1
    return value

def getperiod():
    #returns am or pm
    period = 'am'
    key = ''
    print()
    while key!=keys.ENTER:
        print(f'\t\tPeriod : {period}',end='\r')
        key = getkey()
        if key==keys.UP or key==keys.DOWN:
            if period=='am':
                period='pm'
            else:
                period='am'
    return period

def gethour():
    #returns number ranging from 1 to 12
    value=1
    key=''
    print()
    while key!=keys.ENTER:
        if value<10:
            value_str='0'+str(value)
        else:
            value_str=str(value)
        print(f'\t\tHour : {value_str}',end='\r')
        key=getkey()
        if key==keys.UP:
            if value==12:
                value=1
            else:
                value+=1
        elif key==keys.DOWN:
            if value==1:
                value=12
            else:
                value-=1
    return value

def displayinteractivemenu():
    # clearscreen=''
    # if os.name=='nt':
    #     clearscreen='cls'
    # else:
    #     clearscreen='clear'
    # os.system(clearscreen)
    print(ascii_main)
    #222 251 202 80
    #64 108 158 132
    #98 202 208 214
    #12 33 115 158
    color1=98
    color2=202
    color3=208
    color4=214 
    print("\t "+colorize("'A' to set new alarm",color1))
    print("\t "+colorize("'T' to create new timer",color2))
    print("\t "+colorize("'S' to start new stopwatch",color3))
    print("\t "+colorize("'Q' or 'E' to exit: ",color4),end='')

def interactive():
    while True:
        displayinteractivemenu()
        opt = input().lower()
        if opt=='q' or opt=='e':
            break
        if opt=='a':
            alarm = create_new_alarm()
            print('\n')
            alarm.start()
        elif opt=='t':
            timer = create_new_timer()
            print('\n')
            timer.start()
        elif opt=='s':
            stopwatch = Stopwatch()
            stopwatch.start()
            print()

def create_new_timer():
    hours = getunitvalue('Hour')
    minutes = getunitvalue('Minute')
    seconds = getunitvalue('Second')
    timer = Timer(hours,minutes,seconds)
    return timer

def create_new_alarm():
    hour = gethour()
    minute = getunitvalue('Minute')
    period = getperiod()
    alarm = Alarm(hour,minute,period)
    return alarm
            

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-i","--interactive",help="Interactive mode",action="store_true")
    parser.add_argument("-t","--timer",help="Set a new timer",action="store_true")
    parser.add_argument("-a","--alarm",help="Set a new alarm",action="store_true")
    parser.add_argument("-s","--stopwatch",help="Start a new stopwatch",action="store_true")

    args = parser.parse_args()
    # interactive()
    if args.timer:
        timer = create_new_timer()
        print('\n')
        timer.start()

    if args.alarm:
        alarm = create_new_alarm()
        print('\n')
        alarm.start()

    if args.stopwatch:
        stopwatch = Stopwatch()
        stopwatch.start()


    if (not args.timer and not args.alarm and not args.stopwatch) or args.interactive:
        interactive()

if __name__ == '__main__':
    main()