/*
 This example demonstrates how more "organic" noise signals
 can be generated by summing multiple "octaves" of noise.
*/

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
	
	ofSetWindowTitle("Multiband Noise Example");
	ofSetVerticalSync(true);
	
	setupMultibandNoiseDemo();
}

//--------------------------------------------------------------
void ofApp::update(){
	updateMultibandNoiseDemo();
}

//--------------------------------------------------------------
void ofApp::draw(){
	ofBackgroundGradient( ofColor(240), ofColor(180), OF_GRADIENT_CIRCULAR);
	renderMultibandNoiseDemo();
}

//--------------------------------------------------------------
void ofApp::setupMultibandNoiseDemo(){
	// setup and allocate resources used in the multi-noise strip demo.
	
	nNoiseStrips = 8;
	sliderGroup.resize(nNoiseStrips); 
	noiseDataStripGroup.resize(nNoiseStrips); 
	
	originX = 100; 
	originY = 100; 
	float stripWidth	= 300;
	float stripHeight	= 35;
	float yMargin		= 5;
	float stripXPos		= originX;
	float stripYPos		= originY;
	float noiseStep		= 0.001;
	
	// These are the initial weights of the sliders, which 
	// multiply against their respective noise channels. 
	float noiseAmounts[8] = {0,0,0, 0.82,0.59,0.41, 0.06,0.17}; // fer example
	
	// I'm using a simple struct ("NoiseDataStrip", in ofApp.h) to store the 
	// data contained by one of these noise recordings. Each struct contains
	// the bounding coordinates (x,y,w,h), and some other parameters, plus
	// the float array (data) containing the noise recordings. 
	//
	for (int i=0; i<nNoiseStrips; i++){
		noiseDataStripGroup[i].x = stripXPos;
		noiseDataStripGroup[i].y = stripYPos;
		noiseDataStripGroup[i].width = stripWidth;
		noiseDataStripGroup[i].height = stripHeight;
		noiseDataStripGroup[i].noiseStep = noiseStep; 
		noiseStep *= 2.0; 
		for (int j=0; j< NOISE_DATA_STRIP_LENGTH; j++){
			noiseDataStripGroup[i].data[j] = 1.0; 
		}
		
		float sliderX = stripXPos+stripWidth+yMargin;
		float sliderAmount = noiseAmounts[i]; //1.0 / (powf(2.0, i));
		sliderGroup[i].setup(sliderX, stripYPos, 16,stripHeight, 0.00, 1.0, sliderAmount, true,true);
		stripYPos += stripHeight + yMargin;
	}

}

//--------------------------------------------------------------
void ofApp::updateMultibandNoiseDemo(){
	
	// For each noise strip
	for (int i=0; i<nNoiseStrips; i++){
		
		// Push the older data to the end of the array
		float *data = (float *)noiseDataStripGroup[i].data;
		for (int j=(NOISE_DATA_STRIP_LENGTH-1); j>0; j--){
			data[j] = data[j-1];
		}
		
		// Add the most recent data, the noise value. 
		// Here's where we actually fetch the noise, using ofNoise().
		// Note how ofNoise() requires an argument ('t'); this is
		// the coordinate (on a one-dimensional axis) whose
		// corresponding noise value we wish to obtain. 
		float noiseStep = noiseDataStripGroup[i].noiseStep; 
		float t = (ofGetElapsedTimeMillis()/10.0 + i) * noiseStep;
		data[0] = ofNoise(t);
	}
	
	// Compute the normalization factor: the total sum of the weights
	// for all of the contributing noise channels. This number is the largest
	// value which the sum of noise streams could possibly achieve.
	float normalizationFactor = 0;
	for (int i=0; i<nNoiseStrips; i++){
		float weight = sliderGroup[i].getValue();
		normalizationFactor += weight;
	}
	if (normalizationFactor == 0){
		normalizationFactor = 1.0;
	}
	
	// For every sample in the recording history,
	for (int j=0; j<NOISE_DATA_STRIP_LENGTH; j++){
		float sumj = 0; 
		
		// Sum the weighted contribution from each of the noise strips. 
		for (int i=0; i<nNoiseStrips; i++){
			float val = noiseDataStripGroup[i].data[j];
			float weight = sliderGroup[i].getValue();
			sumj += (weight * val); 
		}
		
		// Normalize it to the range 0...1 by dividing it
		// by normalizationFactor, as we discussed above. 
		summedNoiseData[j] = sumj / normalizationFactor;
	}
	
	
}


//--------------------------------------------------------------
void ofApp::renderMultibandNoiseDemo(){
	
	// draw the individual strips
	float stackBottom = 0; 
	for (int i=0; i<nNoiseStrips; i++){
		float x = noiseDataStripGroup[i].x;
		float y = noiseDataStripGroup[i].y;
		float w = noiseDataStripGroup[i].width;
		float h = noiseDataStripGroup[i].height;
		stackBottom = y+h;
		
		float noiseStep = noiseDataStripGroup[i].noiseStep; 
		float *data = (float *) (noiseDataStripGroup[i].data);
		render1DNoiseStrip (x,y, w,h, noiseStep, data); 
	}
	
	// draw the strip containing the summed data. 
	render1DNoiseStrip(originX, stackBottom+125, 300,100, 0, (float *)summedNoiseData);
	
	string multiBandText   = "ofNoise() creates a signal that varies \n";
	multiBandText         += "smoothly between 0 and 1. More 'organic' \n";
	multiBandText         += "noise can be made by adding multiple \n";
	multiBandText         += "'octaves' of noise. The strip below shows \n";
	multiBandText         += "the sum of the above streams, weighted by \n";
	multiBandText         += "the values in their corresponding sliders. \n";	
	
	ofSetColor(0,0,0); 
	ofDrawBitmapString(multiBandText, originX,   stackBottom+33);
	ofDrawBitmapString("ofNoise()",   originX+1, stackBottom+33); //bold it
	
	ofDrawBitmapString("Noise Step", originX-46, originY-5);
	ofDrawBitmapString("Weights", originX+300+5, originY-5);
}


//--------------------------------------------------------------
void ofApp::render1DNoiseStrip (float x, float y, float width, float height, float dt, float *data){
	
	ofPushMatrix();
	ofDisableSmoothing();
	ofEnableAlphaBlending();
	ofTranslate(x, y, 0); 
	
	// Yes, this is a drop shadow
	ofFill();
	ofSetColor(0,0,0, 10); 
	ofDrawRectangle(0,0, width+4, height+2);
	ofDrawRectangle(0,0, width+2, height+4); 
	
	// Draw a white box underneath the strip
	ofFill();
	ofSetColor(255,255,255); 
	ofDrawRectangle(0,0, width, height); 
	
	// Draw a filled gray noise terrain.
	ofEnableSmoothing();
	ofFill();
	ofSetColor(190); 
	ofBeginShape();
	ofVertex(width, height);
	for (int i=0; i<NOISE_DATA_STRIP_LENGTH; i++){
		float px = ofMap(i, 0,(NOISE_DATA_STRIP_LENGTH-1), width,0); 
		float py = height * data[i];
		ofVertex(px,py);
	}
	ofVertex(0, height); 
	ofEndShape(true);
	
	// Draw the black line of the noise waveform
	ofNoFill();
	ofSetColor(255,0,0);
	ofBeginShape();	
	for (int i=0; i<NOISE_DATA_STRIP_LENGTH; i++){
		float px = ofMap(i, 0,(NOISE_DATA_STRIP_LENGTH-1), width,0); 
		float py = height * data[i];
		ofVertex(px,py);
	}
	ofEndShape(false);
	
	// Draw a box outline on top, around everything
	ofDisableSmoothing();
	ofNoFill();
	ofSetColor(0,0,0); 
	ofDrawRectangle(0,0, width, height);
	
	// Draw the dt noise-step factor
	if (dt > 0){
		ofSetColor(0,0,0); 
		string label = ofToString(dt);
		ofDrawBitmapString(label, -46, height/2+6);
	}

	ofPopMatrix();
}

// In case you're wondering, the simpleSliders get their mouse info through event handlers. 
//--------------------------------------------------------------
void ofApp::keyPressed(int key){

}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}
