import Tone from "../core/Tone";
import "../instrument/Instrument";
import "../signal/Signal";

/**
 *  @class  This is an abstract base class for other monophonic instruments to 
 *          extend. IMPORTANT: It does not make any sound on its own and
 *          shouldn't be directly instantiated.
 *
 *  @constructor
 *  @abstract
 *  @extends {Tone.Instrument}
 */
Tone.Monophonic = function(options){

	//get the defaults
	options = Tone.defaultArg(options, Tone.Monophonic.defaults);
	Tone.Instrument.call(this, options);

	/**
	 *  The glide time between notes. 
	 *  @type {Time}
	 */
	this.portamento = options.portamento;
};

Tone.extend(Tone.Monophonic, Tone.Instrument);

/**
 *  @static
 *  @const
 *  @type {Object}
 */
Tone.Monophonic.defaults = {
	"portamento" : 0
};

/**
 *  Trigger the attack of the note optionally with a given velocity. 
 *  
 *  
 *  @param  {Frequency} note     The note to trigger.
 *  @param  {Time} [time=now]     When the note should start.
 *  @param  {number} [velocity=1] velocity The velocity scaler 
 *                                determines how "loud" the note 
 *                                will be triggered.
 *  @returns {Tone.Monophonic} this
 *  @example
 * synth.triggerAttack("C4");
 *  @example
 * //trigger the note a half second from now at half velocity
 * synth.triggerAttack("C4", "+0.5", 0.5);
 */
Tone.Monophonic.prototype.triggerAttack = function(note, time, velocity){
	this.log("triggerAttack", note, time, velocity);
	time = this.toSeconds(time);
	this._triggerEnvelopeAttack(time, velocity);
	this.setNote(note, time);
	return this;
};

/**
 *  Trigger the release portion of the envelope
 *  @param  {Time} [time=now] If no time is given, the release happens immediatly
 *  @returns {Tone.Monophonic} this
 *  @example
 * synth.triggerRelease();
 */
Tone.Monophonic.prototype.triggerRelease = function(time){
	this.log("triggerRelease", time);
	time = this.toSeconds(time);
	this._triggerEnvelopeRelease(time);
	return this;
};

/**
 *  override this method with the actual method
 *  @abstract
 *  @private
 */	
Tone.Monophonic.prototype._triggerEnvelopeAttack = function(){};

/**
 *  override this method with the actual method
 *  @abstract
 *  @private
 */	
Tone.Monophonic.prototype._triggerEnvelopeRelease = function(){};

/**
 *  Get the level of the output at the given time. Measures
 *  the envelope(s) value at the time. 
 *  @param {Time} time The time to query the envelope value
 *  @return {NormalRange} The output level between 0-1
 */
Tone.Monophonic.prototype.getLevelAtTime = function(time){
	time = this.toSeconds(time);
	return this.envelope.getValueAtTime(time);
};

/**
 *  Set the note at the given time. If no time is given, the note
 *  will set immediately. 
 *  @param {Frequency} note The note to change to.
 *  @param  {Time} [time=now] The time when the note should be set. 
 *  @returns {Tone.Monophonic} this
 * @example
 * //change to F#6 in one quarter note from now.
 * synth.setNote("F#6", "+4n");
 * @example
 * //change to Bb4 right now
 * synth.setNote("Bb4");
 */
Tone.Monophonic.prototype.setNote = function(note, time){
	time = this.toSeconds(time);
	if (this.portamento > 0 && this.getLevelAtTime(time) > 0.05){
		var portTime = this.toSeconds(this.portamento);
		this.frequency.exponentialRampTo(note, portTime, time);
	} else {
		this.frequency.setValueAtTime(note, time);
	}
	return this;
};

export default Tone.Monophonic;