#!/usr/bin/env python3
from tkinter import *
import pyaudio
import wave
import random

WIDTH = 2
CHANNELS = 2
RATE = 44100
FORMAT = pyaudio.paInt16

# number of seconds between each sentence
MIN_SENTENCE_WAIT = 0.8
MAX_SENTENCE_WAIT = 3

# milliseconds of each wait frame
SENTENCE_WAIT_FRAME = 150

class Recorder:
    p = None
    record_counter = 0
    current_frames = []
    paused = True

    def __callback(self, in_data, frame_count, time_info, status):
        if not self.paused:
            self.current_frames.append(in_data)

        return (in_data, pyaudio.paContinue)
    
    def __init__(self, filename_base):
        self.filename_base = filename_base
        
    def start(self):
        global WIDTH, CHANNELS, RATE

        # TODO: mark the current filename
        if self.p == None:
            self.p = pyaudio.PyAudio()        
            self.stream = self.p.open(format=self.p.get_format_from_width(WIDTH),
                    channels=CHANNELS,
                    rate=RATE,
                    input=True,
                    output=False,
                    stream_callback=self.__callback)
    
            self.stream.start_stream()
        
    def save(self):
        global CHANNELS, FORMAT, RATE
        
        if self.p == None:
            return
        
        to_save = self.current_frames
        
        wf = wave.open(self.filename_base + str(self.record_counter) + '.wav',
                       'wb')
        
        self.record_counter += 1
                
        wf.setnchannels(CHANNELS)
        wf.setsampwidth(self.p.get_sample_size(FORMAT))
        wf.setframerate(RATE)
        wf.writeframes(b''.join(to_save))
        wf.close()
        
        self.current_frames = []
        
    def redo(self):
        """Clear the existing recording"""
        self.current_frames = []
        
    def quit(self):       
        self.stream.stop_stream()
        self.stream.close()
        
        self.p.terminate()

class App:
    """The user interface and experiment actions"""
    def __init__(self, master, sentences, filename_base):
        self.master = master
        self.sentences = sentences
        
        # make it cover the entire screen        
        w, h = master.winfo_screenwidth(), master.winfo_screenheight()
        master.overrideredirect(1)
        master.geometry("%dx%d+0+0" % (w, h))

        frame = Frame(master)
        frame.pack(fill=BOTH, expand = True)
        
        # TODO: setting
        self.recorder = Recorder(filename_base)

        self.sentence = StringVar()
        self.sentence_display = Label(frame, textvariable=self.sentence, 
                                      font=("Helvetica", 16))
        self.sentence_display.pack(side=TOP)
        self.sentence.set("\n\nKlik op volgende om het inspreken en opnemen te starten!\n")
        
        self.redo_button = Button(frame, text="Opnieuw opnemen", fg="red",
                                  command=self.recorder.redo)
        self.redo_button.pack(side=LEFT, fill=X, expand = True)
        
        self.continue_button = Button(frame, text="Volgende",
                                      command=self.pressed_next)
        self.continue_button.pack(side=RIGHT, fill=X, expand=True)

        self.sentence_count = 0
        self.pause_offset = 0
        # When pausing it is not allowed to press any button!
        self.pause_lock = False
        
    def sentence_noise_cb(self):
        character = random.sample("#+&%X-/= ", 1)[0]
        text = character * random.randint(5,15)
        return lambda: self.sentence.set("\n\n" + text + "\n")
        
    def pressed_next(self):
        global MIN_SENTENCE_WAIT, MAX_SENTENCE_WAIT, SENTENCE_WAIT_FRAME
        
        if self.pause_lock:
            return
        
        if self.sentence_count == int(len(self.sentences) / 2):
            self.sentence.set("\n\nPauze! Neem gerust een kop koffie of wat thee.\n")
            self.recorder.save()
            self.recorder.paused = True
            self.pause_offset += 1
        elif self.sentence_count < len(self.sentences) + self.pause_offset:
            self.pause_lock = True
            if self.sentence_count == 0:
                # first sentence? start recording!
                self.recorder.start()
                self.recorder.paused = True        
            elif not self.recorder.paused:
                # done with the pause?
                self.recorder.save()
                self.recorder.paused = True
            
            # disable the buttons
            self.redo_button.config(state = DISABLED)
            self.continue_button.config(state = DISABLED)
            
            sentence_wait = random.randint(int(MIN_SENTENCE_WAIT * 1000),
                                           int(MAX_SENTENCE_WAIT * 1000)) / 1000
            
            # pause and do a countdown loop
            for i in range(0, int(sentence_wait * (1000/SENTENCE_WAIT_FRAME))):
                self.master.after(int(i * SENTENCE_WAIT_FRAME),
                                  self.sentence_noise_cb())
            
            # enable buttons
            self.redo_button.config(state = ACTIVE)
            self.continue_button.config(state = ACTIVE)
            
            # start recording and show sentence!
            def assign_sentence():
                text = self.sentences[self.sentence_count - self.pause_offset
                                      - 1]
                self.recorder.paused = False
                self.pause_lock = False
                self.sentence.set("\n\n" + text + "\n")
                
            self.master.after(int(sentence_wait * 1000), assign_sentence)
        elif self.sentence_count == len(self.sentences) + self.pause_offset:
            self.sentence.set("\n\nEinde opname :)\n")
            self.recorder.save()
            self.recorder.quit()
        else:
            global root
            root.destroy()
            
        self.sentence_count += 1

def start(sentences, output):
    global root
    
    # this is the GUI part    
    root = Tk()
    
    App(root, sentences, output)
    
    root.mainloop()
