
""" waves """
#from __future__ import print_function
import time
import math


def _apply_adsr(value,
    attack=3, # time in seconds
    decay=3, # time in seconds
    sustaint=3, # time in seconds
    sustainl=0.7, # level to sustain at (0.0-1.0)
    release=3, # time in seconds
    gap=3 # time in seconds
    ):
    def _mapper(x, in_min, in_max, out_min, out_max):
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    stage = time.time() % (attack + decay + sustaint + release + gap)
    gain = 1
    if stage <= attack:
        # need to map range 0-1 over full attack time
        gain = _mapper(stage, 0, attack, 0, 1)
    elif stage <= (attack + decay):
        # need to map range 1-sustainl over full decay time
        gain = _mapper(stage, attack, attack+decay, 1, sustainl)
    elif stage <= (attack + decay + sustaint):
        gain = sustainl
    elif stage <= (attack + decay + sustaint + release):
        # need to map range sustainl-0 over full release time
        gain = _mapper(stage, attack + decay + sustaint, attack + decay + sustaint + release, sustainl, 0)
    elif stage <= (attack + decay + sustaint + release + gap):
        gain = 0

    return value * gain

def _wave_sine(r):
    return math.sin(2 * math.pi * r)

def _wave_cosin(r):
    return math.cos(2 * math.pi * r)

def _wave_square(r):
    if r < 0.5:
        return 1
    else:
        return 0

def _wave_triangle(ratio):
    if ratio == 0.5:
        return 1
    elif ratio < 0.5:
        return ratio * 2
    else:
        return ratio * 2 - 1
        
def _wave_saw_asc(ratio):
    return ratio

def _wave_saw_desc(ratio):
    return 1 - ratio

def _amp_lineargain(value, gain=1.0, offset=0.0):
    return (value * gain) + offset

def do_wave(shape='sin', period=60, amplifier=None, envolope=None, precision=2):
    """ Generate a wave from the current time

    Parameters:
    shape:          The shape of the wave
    period:         The period, in seconds, of the wave
    amplifier:      Amplifier to apply to the wave
    envolope:       ASDR envolope to apply to the wave
    precision:      Number of decimal points to return
    """

    ratio = (time.time() % period) / period

    if shape == 'sin':
        waved = _wave_sine(ratio)
    elif shape == 'cos':
        waved = _wave_cosin(ratio)
    elif shape == 'square':
        waved = _wave_square(ratio)
    elif shape == 'triangle':
        waved = _wave_triangle(ratio)
    elif shape == 'saw_asc':
        waved = _wave_saw_asc(ratio)
    elif shape == 'saw_desc':
        waved = _wave_saw_desc(ratio)
    else:
        waved = 1

    # Is there more than one kind of these?
    amped = waved
    if amplifier != None:
        if amplifier['type'] == 'lineargain':
            params = amplifier.copy()
            params['value'] = waved
            amped = _amp_lineargain(**params)

    if envolope != None:
        params = envolope.copy()
        params['value'] = amped
        envp = _apply_adsr(**params)
    else:
        envp = amped

    return round(envp, precision)


# def do_multi_wave(?array of waves?):
#     pass