"use strict";

class VC
{
	constructor( canvas, config )
	{
		const defConfig =
		{
			clearInterval: 50,
			overScan: 0.82,
			hOffset: 0.06525,
			pulseLength: ( 0.2 / 1000 ),
			lineWidth: 2.5,
			brightness: 1,
			saturation: 1,
			blend: false,
			hFreq: 225.0,
			vFreq: 3
		};

		config = Object.assign( defConfig, config );

		this.lines = [];

		this.lastClear = 0;
		this.clearInterval = config.clearInterval;

		this.canvas = canvas;

		this.width = this.canvas.width;
		this.height = this.canvas.height;

		this.lineWidth = config.lineWidth;
		this.blend = config.blend;

		this.config = config;

		this.graphicsContext = null;
		this.audioContext = null;
		this.audioInput = null;
		this.decoder = null;
	}

	async start()
	{
		this.graphicsContext = this.canvas.getContext( "2d" );

		this.audioContext = new window.AudioContext();

		let stream = await navigator.mediaDevices.getUserMedia(
			{
				audio: {
					echoCancellation: false,
					noiseSuppression: false,
					autoGainControl: false,
					channelCount: {
						exact: 2
					}
				}
			} );

		this.audioInput = this.audioContext.createMediaStreamSource( stream );

		await this.audioContext.audioWorklet.addModule( 'cv-decoder.js' )

		this.decoder = new AudioWorkletNode( this.audioContext, 'cv-decoder',
			{
				numberOfInputs: 2,
				numberOfOutputs: 0,
				processorOptions: this.config
			}
		);

		this.decoder.port.onmessage = event =>
		{
			if( this.lines.length < 1024 )
				this.lines.push( event.data )
		};

		this.audioInput.connect( this.decoder );

		requestAnimationFrame( () => this.draw() );
	}

	activate()
	{
		this.audioContext.resume();
	}

	draw()
	{
		if( Date.now() - this.lastClear > this.clearInterval )
		{
			this.graphicsContext.fillStyle = 'rgba(0,0,0,0.05)';
			this.graphicsContext.globalCompositeOperation = 'source-over';
			this.graphicsContext.fillRect( 0, 0, this.width, this.height );
			this.lastClear = Date.now();
		}

		if( this.blend )
			this.graphicsContext.globalCompositeOperation = 'screen';
		else
			this.graphicsContext.globalCompositeOperation = 'source-over';

		this.graphicsContext.lineWidth = this.lineWidth;

		const width = this.width;
		const height = this.height;

		for( let line of this.lines )
		{
			var gradient = this.graphicsContext.createLinearGradient( line.x1 * width, line.y * height, line.x2 * width, line.y * height );

			for( let color of line.colors )
				gradient.addColorStop( color.phase / line.maxPhase, 'rgb(' + color.r + ',' + color.g + ',' + color.b + ')' );

			this.graphicsContext.beginPath();

			this.graphicsContext.moveTo( line.x1 * width + Math.random() * 2.0 - 1.0, line.y * height + Math.random() * 2.0 - 1.0 );
			this.graphicsContext.lineTo( line.x2 * width + Math.random() * 2.0 - 1.0, line.y * height + Math.random() * 2.0 - 1.0 );

			this.graphicsContext.strokeStyle = gradient;

			this.graphicsContext.stroke();
		}

		this.lines = [];

		requestAnimationFrame( () => this.draw() );
	}
}
