#!/usr/bin/env python

"""
Python Command Line Musical Interpreter for music_syn.
Pranav Ravichandran (me@onloop.net)
"""

from . import pysynth, pysynth_b, pysynth_s, play_wav
import sys
import os

# Type 'help' to access.
helpContent = ("------------------------------\nmusic_syn musical note interpreter."
               "\nUsage: <Duration><Note> <Duration2><Note2> .... <DurationN><NoteN>"
               "\nOptional arguments:\n\t--bpm=Beats per minute [Default:120]"
               "\n\t--repeat=Number of bars [Default:1]"
               "\n\t--sound=Instrument [a = Flute/Organ, b = piano, s = plucked string, Default = a]"
               "\n\t--save=filename (Filename to save the file to. Appends .wav to filename)"
               "\nSamples:""\n8g 8g 8g 2eb 8r 8f 8f 8f 1d --sound=a"
               "\n4e4 4e4 4f4 4g4 4g4 4f4 4e4 4d4 4c4 4c4 4d4 4e4 4e4 4d4 2d4 4e4 4e4 4f4 4g4 "
               "4g4 4f4 4e4 4d4 4c4 4c4 4d4 4e4 4d4 4c4 2c4 --bpm=200 --repeat=1 --sound=s "
               "--save=Ode_to_Joy\nCommands: 'exit' and 'help'\n------------------------------")

usageHelp = "Notes are 'a' through 'g' of course,\noptionally with '#' or 'b' appended for sharps or flats.\nFinally the octave number (defaults to octave 4 if not given).\nAn asterisk at the end makes the note a little louder (useful for the beat).\n'r' is a rest.\n\nNote value is a number:\n1=Whole Note; 2=Half Note; 4=Quarter Note, etc.\nDotted notes can be written in two ways:\n1.33 = -2 = dotted half\n2.66 = -4 = dotted quarter\n5.33 = -8 = dotted eighth\n--------------------------------"

warningStr = "Improper Syntax - Type 'help' to see usage."

invalidCmd = "Command does not exist."

invalidOption = "Invalid Option."


class mEnv:
    cliInput = ''
    synthParam = []
    bpmVal = 0
    repeatVal = 0
    inputEntered = False
    instrument = ''
    outFile = ''
    trashFile = True

    def __init__(self):
        """ Constructor class. """

        # Get the user input.
        cliInput = input(">>> ")

        self.parse(cliInput)

        # Different cases of input, when optional argument 'sound' is given.
        if self.instrument == 'a' or self.instrument == '':
            self.synthSounds(pysynth, self.outFile)
        elif self.instrument == 'b':
            self.synthSounds(pysynth_b, self.outFile)
        elif self.instrument == 's':
            self.synthSounds(pysynth_s, self.outFile)
        else:
            print(invalidOption)
            mEnv()

    def parse(self, cliInput):
        """ Parse command line input."""

        # 'exit' command.
        if cliInput == 'exit':
            sys.exit()
        # 'help' command.
        if cliInput == 'help':
            print('\n' + helpContent + '\n' + usageHelp + '\n')
            mEnv()

        # List with whitespace as delimiter.
        cliInput = cliInput.split()
        self.synthParam = []

        for comp in cliInput:
            # Optional arguments.
            if comp.startswith('--'):
                comp = comp.strip('-')
                comp = comp.split('=')
                if comp[0] == 'bpm':
                    try:
                        self.bpmVal = int(comp[1])
                    except IndexError:
                        print(warningStr)
                        mEnv()
                elif comp[0] == 'repeat':
                    try:
                        self.repeatVal = int(comp[1])
                    except IndexError:
                        print(warningStr)
                        mEnv()
                elif comp[0] == 'sound':
                    try:
                        self.instrument = str(comp[1])
                    except IndexError:
                        print(warningStr)
                        mEnv()
                elif comp[0] == 'save':
                    try:
                        self.outFile = str(comp[1]) + '.wav'
                        self.trashFile = False
                    except IndexError:
                        print(warningStr)
                        mEnv()

                continue

            # Notes and beats.
            i = 0
            for alphanum in comp:
                if alphanum.isalpha():
                    try:
                        self.synthParam.append((comp[i:], int(comp[:i])))
                    except ValueError:
                        print(invalidCmd)
                        mEnv()

                    break
                i += 1

    def play(self, outFile):
        """ Open the .wav file and play it."""

        if outFile == '':
            outFile = 'temp.wav'

        a = play_wav.Sound()
        a.playFile(outFile)

    def removeFile(self, outFile):
        """ Delete the .wav file."""

        if outFile == '':
            outFile = 'temp.wav'

        if self.trashFile:
            os.remove(outFile)

    def synthSounds(self, renderSound, outFile):
        """ Render sound with pysynth_a, pysynth_b or pysynth_s based on user preference."""

        if outFile == '':
            outFile = 'temp.wav'

        try:
            # Different cases of input, when optional arguments 'bpm' and 'repeat' are given.
            if self.bpmVal and self.repeatVal:
                renderSound.make_wav(self.synthParam, fn=outFile, silent=True, bpm=self.bpmVal, repeat=self.repeatVal)
            elif self.bpmVal:
                renderSound.make_wav(self.synthParam, fn=outFile, silent=True, bpm=self.bpmVal)
            elif self.repeatVal:
                renderSound.make_wav(self.synthParam, fn=outFile, silent=True, repeat=self.repeatVal)
            else:
                renderSound.make_wav(self.synthParam, fn=outFile, silent=True)
        except KeyError:
            print(warningStr)
            mEnv()


if __name__ == "__main__":
    # Print introductory help content.
    print(helpContent)

    # Interpreter loop.
    while True:
        a = mEnv()
        try:
            a.play(a.outFile)
        except:
            a.trashFile = False
            if a.outFile == '':
                a.outFile = 'temp.wav'
            print('Could not play file. Saved to ' + a.outFile)
        a.removeFile(a.outFile)
