/**
 * @module  audio-shader
 */

var Through = require('audio-through');
var inherits = require('inherits');
var createShader = require('nogl-shader-output');
var createContext = require('nogl');
var Shader = require('gl-shader');


/**
 * @constructor
 *
 * @param {Function} fn Shader code
 * @param {Object} options Options
 */
function AudioShader (shaderCode, options) {
	if (!(this instanceof AudioShader)) return new AudioShader(shaderCode, options);

	options = options || {};

	Through.call(this, options);

	//refine number of channels - vec4 is max output
	var channels = this.format.channels = Math.min(this.format.channels, 4);

	//refine shader code, if not passed
	if (!shaderCode) {
		var vecType = channels === 1 ? 'float' : ('vec' + channels);
		shaderCode = `${vecType} mainSound( float time ){
			return ${vecType}( sin(6.2831*440.0*time)*exp(-3.0*time) );
		}`;
	}

	var gl = options.gl || createContext();

	var shader = Shader(gl, `
		precision mediump float;
		precision mediump int;

		attribute vec2 position;

		uniform float iGlobalTime;
		uniform float iSampleRate;
		uniform vec3  iResolution;

		varying float time;

		void main (void) {
			gl_Position = vec4(position, 0, 1);
			time = iGlobalTime + (position.x * 0.5 + 0.5) * iResolution.x / iSampleRate;
		}
	`, `
		precision mediump float;
		precision mediump int;

		uniform vec3      iResolution;           // viewport resolution (in pixels)
		uniform float     iGlobalTime;           // shader playback time (in seconds)
		uniform int       iFrame;                // shader playback frame
		uniform vec4      iDate;                 // (year, month, day, time in seconds)
		uniform float     iSampleRate;           // sound sample rate (i.e., 44100)
		uniform vec3      iChannelResolution[4]; // channel resolution (in pixels)
		// uniform sampler2D iChannel0;             // input channel1
		// uniform sampler2D iChannel1;             // input channel2
		// uniform sampler2D iChannel2;             // input channel3
		// uniform sampler2D iChannel3;             // input channel4

		varying float time;

		${shaderCode}

		void main (void) {
			vec4 result = vec4(mainSound(time)${
				channels === 1 ? ', 0, 0, 0' :
				channels === 3 ? ', 0' :
				channels === 4 ? '' : ', 0, 0'
			});
			gl_FragColor = result;
		}`);

	//setup shader
	this.draw = createShader(shader, {
		width: this.format.samplesPerFrame,
		height: 1
	});

	//clean on end
	this.on('end', function () {
		throw Error('Unimplemented');
	});
}

inherits(AudioShader, Through);



/**
 * Send chunk to audio-shader, invoke done on return.
 * The strategy: render each audio channel to it’s own line in result
 * TODO: thing to replace with textures
 * TODO: provide input channels as textures
 * TODO: provide values of previous input/output to implement filters
 */
AudioShader.prototype.process = function (chunk, done) {
	var w = this.format.samplesPerFrame;
	var channels = Math.min(chunk.numberOfChannels, this.format.channels);

	//set up current chunk as a channels data
	// for (var channel = 0; channel < channels; channel++) {
	// 	var texture = new Texture(gl, chunk.getChannelData(channel));
	// 	this.shader.uniforms[`iChannel${channel}`] = texture.bind();
	// 	this.shader.uniforms.iChannelResolution[channel] = [chunk.length, 1, 1];
	// 	this.shader.uniforms.iChannelTime[channel] = [];
	// }

	//preset new time value
	var d = new Date();

	//render chunk
	var result = this.draw({
		iResolution: [w, 1, 1],
		iSampleRate: this.format.sampleRate,
		iGlobalTime: this.time,
		iFrame: this.frame,
		iDate: [
			d.getFullYear(), // the year (four digits)
			d.getMonth(), // the month (from 0-11)
			d.getDate(), // the day of the month (from 1-31)
			d.getHours()*60.0*60 + d.getMinutes()*60 + d.getSeconds()
		]
	});

	//transform result to buffer channels (color channel per audio channel)
	for (var channel = 0; channel < channels; channel++) {
		var cData = chunk.getChannelData(channel);
		for (var i = 0; i < w; i++) {
			cData[i] = result[i * 4 + channel];
		}
	}

	done();
}


module.exports = AudioShader;
