/*
 * Copyright (c) 2011 Dan Wilcox <danomatika@gmail.com>
 *
 * BSD Simplified License.
 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
 * WARRANTIES, see the file, "LICENSE.txt," in this distribution.
 *
 * See https://github.com/danomatika/ofxPd for documentation
 *
 */
#include "AppCore.h"

//--------------------------------------------------------------
void AppCore::setup(const int numOutChannels, const int numInChannels,
				    const int sampleRate, const int ticksPerBuffer) {

	ofSetFrameRate(60);
	ofSetVerticalSync(true);
	//ofSetLogLevel(OF_LOG_VERBOSE);
	
	// double check where we are ...
	cout << ofFilePath::getCurrentWorkingDirectory() << endl;
	
	if(!pd.init(numOutChannels, numInChannels, sampleRate, ticksPerBuffer)) {
		ofLog(OF_LOG_ERROR, "Could not init pd");
		OF_EXIT_APP(1);
	}
    midiChan = 1; // midi channels are 1-16
	
	// subscribe to receive source names
	pd.subscribe("toOF");
	pd.subscribe("env");

	// add message receiver, disables polling (see processEvents)
	pd.addReceiver(*this);   // automatically receives from all subscribed sources
	pd.ignore(*this, "env"); // don't receive from "env"
    //pd.ignore(*this);             // ignore all sources
	//pd.receive(*this, "toOF");	// receive only from "toOF"
	
    // add midi receiver
    //pd.addMidiReceiver(*this);  // automatically receives from all channels
    //pd.ignoreMidi(*this, 1);     // ignore midi channel 1
    pd.ignoreMidi(*this);        // ignore all channels
    //pd.receiveMidi(*this, 1);    // receive only from channel 1

	// add the data/pd folder to the search path
	pd.addToSearchPath("pd");

	// audio processing on
	pd.start();

	// open patch
    //patchName = "targetSynth6.pd";
    patchName = "tweakathon_synth_pulse.pd";
	Patch patch = pd.openPatch(patchName);
	cout << patch << endl;


}

//--------------------------------------------------------------
void AppCore::update() {
	ofBackground(100, 100, 100);
	
	// update scope array from pd
	pd.readArray("scope", scopeArray);
}

//--------------------------------------------------------------
void AppCore::draw() {

	// draw scope
	ofSetColor(0, 255, 0);
	ofSetRectMode(OF_RECTMODE_CENTER);
	float x = 0, y = ofGetHeight()/2;
	float w = ofGetWidth() / (float) scopeArray.size(), h = ofGetHeight()/2;
	for(int i = 0; i < scopeArray.size()-1; ++i) {
		ofLine(x, y+scopeArray[i]*h, x+w, y+scopeArray[i+1]*h);
		x += w;
	}
}

//--------------------------------------------------------------
void AppCore::exit() {
    pd.stop();
}

//--------------------------------------------------------------
void AppCore::playTone(int pitch) {
	pd << StartMessage() << "pitch" << pitch << FinishList("tone") << Bang("tone");
}

//--------------------------------------------------------------
void AppCore::keyPressed (int key) {

	switch(key) {
	
		case 'a':
			playTone(60);
			break;
		case 'w':
			playTone(61);
			break;
		case 's':
			playTone(62);
			break;
		case 'e':
			playTone(63);
			break;
		case 'd':
			playTone(64);
			break;
		case 'f':
			playTone(65);
			break;
		case 't':
			playTone(66);
			break;
		case 'g':
			playTone(67);
			break;
		case 'y':
			playTone(68);
			break;
		case 'h':
			playTone(69);
			break;
		case 'u':
			playTone(70);
			break;
		case 'j':
			playTone(71);
			break;
		case 'k':
			playTone(72);
			break;
			
		case ' ':
			if(pd.isReceiving(*this, "env")) {
				pd.ignore(*this, "env");
                cout << "ignoring env" << endl;
			}
			else {
				pd.receive(*this, "env");
                cout << "receiving from env" << endl;
			}
			break;
			
		default:
			break;
	}
}

//--------------------------------------------------------------
void AppCore::audioReceived(float * input, int bufferSize, int nChannels) {
	pd.audioIn(input, bufferSize, nChannels);
}

//--------------------------------------------------------------
void AppCore::audioRequested(float * output, int bufferSize, int nChannels) {
	pd.audioOut(output, bufferSize, nChannels);
}

//--------------------------------------------------------------
void AppCore::print(const std::string& message) {
	cout << message << endl;
}

//--------------------------------------------------------------		
void AppCore::receiveBang(const std::string& dest) {
	cout << "OF: bang " << dest << endl;
}

void AppCore::receiveFloat(const std::string& dest, float value) {
	cout << "OF: float " << dest << ": " << value << endl;
}

void AppCore::receiveSymbol(const std::string& dest, const std::string& symbol) {
	cout << "OF: symbol " << dest << ": " << symbol << endl;
}

void AppCore::receiveList(const std::string& dest, const List& list) {
	cout << "OF: list " << dest << ": ";
	
	// step through the list
	for(int i = 0; i < list.len(); ++i) {
		if(list.isFloat(i))
			cout << list.getFloat(i) << " ";
		else if(list.isSymbol(i))
			cout << list.getSymbol(i) << " ";
	}
    
    // you can also use the built in toString function or simply stream it out
    // cout << list.toString();
    // cout << list;
	
    // print an OSC-style type string
	cout << list.types() << endl;
}

void AppCore::receiveMessage(const std::string& dest, const std::string& msg, const List& list) {
	cout << "OF: message " << dest << ": " << msg << " " << list.toString() << list.types() << endl;
}

//--------------------------------------------------------------
void AppCore::receiveNoteOn(const int channel, const int pitch, const int velocity) {
	cout << "OF MIDI: note on: " << channel << " " << pitch << " " << velocity << endl;
}

void AppCore::receiveControlChange(const int channel, const int controller, const int value) {
	cout << "OF MIDI: control change: " << channel << " " << controller << " " << value << endl;
}

// note: pgm nums are 1-128 to match pd
void AppCore::receiveProgramChange(const int channel, const int value) {
	cout << "OF MIDI: program change: " << channel << " " << value << endl;
}

void AppCore::receivePitchBend(const int channel, const int value) {
	cout << "OF MIDI: pitch bend: " << channel << " " << value << endl;
}

void AppCore::receiveAftertouch(const int channel, const int value) {
	cout << "OF MIDI: aftertouch: " << channel << " " << value << endl;
}

void AppCore::receivePolyAftertouch(const int channel, const int pitch, const int value) {
	cout << "OF MIDI: poly aftertouch: " << channel << " " << pitch << " " << value << endl;
}

// note: pd adds +2 to the port num, so sending to port 3 in pd to [midiout],
//       shows up at port 1 in ofxPd
void AppCore::receiveMidiByte(const int port, const int byte) {
	cout << "OF MIDI: midi byte: " << port << " " << byte << endl;
}

//--------------------------------------------------------------
void AppCore::processEvents() {
	
	cout << "Number of waiting messages: " << pd.numMessages() << endl;
	
	while(pd.numMessages() > 0) {
		Message& msg = pd.nextMessage();

		switch(msg.type) {
			
			case pd::PRINT:
				cout << "OF: " << msg.symbol << endl;
				break;
			
			// events
			case pd::BANG:
				cout << "OF: bang " << msg.dest << endl;
				break;
			case pd::FLOAT:
				cout << "OF: float " << msg.dest << ": " << msg.num << endl;
				break;
			case pd::SYMBOL:
				cout << "OF: symbol " << msg.dest << ": " << msg.symbol << endl;
				break;
			case pd::LIST:
				cout << "OF: list " << msg.list << msg.list.types() << endl;
				break;
			case pd::MESSAGE:
				cout << "OF: message " << msg.dest << ": " << msg.symbol << " " 
					 << msg.list << msg.list.types() << endl;
				break;
			
			// midi
			case pd::NOTE_ON:
				cout << "OF MIDI: note on: " << msg.channel << " "
					 << msg.pitch << " " << msg.velocity << endl;
				break;
			case pd::CONTROL_CHANGE:
				cout << "OF MIDI: control change: " << msg.channel
					 << " " << msg.controller << " " << msg.value << endl;
				break;
			case pd::PROGRAM_CHANGE:
				cout << "OF MIDI: program change: " << msg.channel << " "
					 << msg.value << endl;
				break;
			case pd::PITCH_BEND:
				cout << "OF MIDI: pitch bend: " << msg.channel << " "
					 << msg.value << endl;
				break;
			case pd::AFTERTOUCH:
				cout << "OF MIDI: aftertouch: " << msg.channel << " "
					 << msg.value << endl;
				break;
			case pd::POLY_AFTERTOUCH:
				cout << "OF MIDI: poly aftertouch: " << msg.channel << " "
					 << msg.pitch << " " << msg.value << endl;
				break;
			case pd::BYTE:
				cout << "OF MIDI: midi byte: " << msg.port << " 0x"
					 << hex << (int) msg.byte << dec << endl;
				break;
		
			case pd::NONE:
				cout << "OF: NONE ... empty message" << endl;
				break;
		}
	}
}
