// node_manager_test.cpp
// PURPOSE
//   A little test harness for cortex::NodeManager.
//
// HISTORY
//   e.moon		27jul99		Begun

#include <Alert.h>
#include <Application.h>
#include <CheckBox.h>
#include <Debug.h>
#include <Entry.h>
#include <Font.h>
#include <MediaAddOn.h>
#include <MediaRoster.h>
#include <MenuBar.h>
#include <Menu.h>
#include <MenuItem.h>
#include <Slider.h>
#include <StringView.h>
#include <TimeCode.h>
#include <Window.h>

#include "NodeManager.h"
#include "NodeGroup.h"
#include "NodeRef.h"

class App;

class TestWindow :
	public		BWindow {
	typedef		BWindow _inherited;
	
public:
	~TestWindow() {}
		
	TestWindow(cortex::NodeGroup* _group) :
		BWindow(
			BRect(200, 200, 500, 370),
			"node_manager_test",
			B_TITLED_WINDOW,
			B_ASYNCHRONOUS_CONTROLS),
		m_group(_group),
		m_ref(0),
		m_positionView(0),
		m_font(be_bold_font) {
		
		init();
	}
	
	bool QuitRequested() {
		if(m_group) {
			m_group->removeListener(this);
			m_ref->removePositionListener(this);
			m_group = 0;
		}
		be_app->PostMessage(B_QUIT_REQUESTED);
		return true;
	}
	
	void MessageReceived(BMessage* msg) {
		status_t err;
		switch(msg->what) {
			case 'Play':
				doPlay();
				break;
			case 'Stop':
				doStop();
				break;
			case 'Free':
				doRelease();
				break;
				
			case 'Star':
				m_group->setStartPosition((bigtime_t)m_startSlider->Value());
				break;

			case 'End_':
				m_group->setEndPosition((bigtime_t)m_endSlider->Value());
				break;
			case 'Loop':
				m_ref->setCycling(m_loopCheckBox->Value() != 0);
				break;
				
			case 'Po0_':
				m_ref->disablePositionReports();
				break;
			case 'Po1_':
				m_ref->enablePositionReports();
				break;


			// target object deleted?
			case cortex::Listenable::M_DELETED:
				PRINT(("@ cortex::Listenable::M_DELETED\n"));
				m_group = 0;
				msg->SendReply('Okay');
				break;
			
			// group messages
			case cortex::NodeGroup::M_NODE_ADDED:
				PRINT(("@ cortex::NodeGroup::M_NODE_ADDED\n"));
				break;
			case cortex::NodeGroup::M_NODE_REMOVED:
				PRINT(("@ cortex::NodeGroup::M_NODE_REMOVED\n"));
				break;
			case cortex::NodeGroup::M_TRANSPORT_STATUS:
				PRINT(("@ cortex::NodeGroup::M_TRANSPORT_STATUS\n"));
				{
					uint32 state;
					err = msg->FindInt32("transportState", (int32*)&state);
					if(err < B_OK) {
						PRINT(("  transportState not found.\n"));
						break;
					}
					switch(state) {
						case cortex::NodeGroup::TRANSPORT_INVALID:
							PRINT(("  TRANSPORT_INVALID\n"));
							break;
						case cortex::NodeGroup::TRANSPORT_STOPPED:
							PRINT(("  TRANSPORT_STOPPED\n"));
							break;
						case cortex::NodeGroup::TRANSPORT_STARTING:
							PRINT(("  TRANSPORT_STARTING\n"));
							break;
						case cortex::NodeGroup::TRANSPORT_RUNNING:
							PRINT(("  TRANSPORT_RUNNING\n"));
							break;
						case cortex::NodeGroup::TRANSPORT_STOPPING:
							PRINT(("  TRANSPORT_STOPPING\n"));
							break;
						default:
							PRINT(("  [unknown state %ld]\n", state));
							break;
					}
				}
				break;
			case cortex::NodeGroup::M_TIME_SOURCE_SET:
				PRINT(("@ cortex::NodeGroup::M_TIME_SOURCE_SET\n"));
				break;
				
			case cortex::NodeRef::M_POSITION:
//				PRINT(("@ cortex::NodeRef::M_POSITION\n"));
				updatePosition(msg);
				break;
			default:
				_inherited::MessageReceived(msg);
				break;
		}
	}
	
private:
	BFont												m_font;
	BStringView*								m_positionView;
	cortex::NodeGroup*					m_group;
	cortex::NodeRef*						m_ref;
	
	BSlider*										m_startSlider;
	BSlider*										m_endSlider;
	BCheckBox*									m_loopCheckBox;
	
	void init() {
		BMenuBar* menuBar = new BMenuBar(
			BRect(0,0,0,0),
			"menuBar");
		
		BMenu* menu = new BMenu("File");
		BMenuItem* item = new BMenuItem(
			"Quit", new BMessage(B_QUIT_REQUESTED));
		menu->AddItem(item);
		menuBar->AddItem(menu);

		menu = new BMenu("Group");
		item = new BMenuItem(
			"Play", new BMessage('Play'));
		menu->AddItem(item);
		item = new BMenuItem(
			"Stop", new BMessage('Stop'));
		menu->AddItem(item);
		menu->AddItem(new BSeparatorItem());
		item = new BMenuItem(
			"Release Group", new BMessage('Free'));
		menu->AddItem(item);
		menu->AddItem(new BSeparatorItem());
		item = new BMenuItem(
			"Position Reports On", new BMessage('Po1_'));
		menu->AddItem(item);
		item = new BMenuItem(
			"Position Reports Off", new BMessage('Po0_'));
		menu->AddItem(item);
		
		menuBar->AddItem(menu);
		
		AddChild(menuBar);
		
		BRect b = Bounds();
		b.top = menuBar->Frame().bottom + 1;
		b.left += 8;
		b.bottom -= 90;
		m_positionView = new BStringView(
			b, "positionView", "[...]");

		m_font.SetSize(48.0);
		m_positionView->SetFont(&m_font);
		AddChild(m_positionView);
		
		b.top = b.bottom + 1;
		b.bottom = b.top + 30;
		b.right -= 8;
		m_startSlider = new BSlider(
			b, "startSlider", "start", new BMessage('Star'),
			0, 1000000L);
		m_startSlider->SetModificationMessage(new BMessage('Star'));
		AddChild(m_startSlider);
		b.top = b.bottom + 1;
		b.bottom = b.top + 30;
		m_endSlider = new BSlider(
			b, "endSlider", "end", new BMessage('End_'),
			0, 1000000L);
		m_endSlider->SetModificationMessage(new BMessage('End_'));
		AddChild(m_endSlider);
		b.top = b.bottom + 1;
		b.bottom = b.top + 30;
		m_loopCheckBox = new BCheckBox(
			b, "loopCheckBox", "loop", new BMessage('Loop'));
		m_loopCheckBox->SetValue(1);
		AddChild(m_loopCheckBox);
		
		m_ref = m_group->nodeAt(0);
		ASSERT(m_ref);
//		m_ref->addPositionListener(this);
//		m_ref->setPositionUpdatePeriod(10000LL);

		m_group->setEndPosition(1000000LL);
		m_endSlider->SetValue((int32)m_group->endPosition());
		m_ref->setCycling(true);
		m_group->addListener(this);
	}
	
	void doPlay() {
		if(!m_group) {
			(new BAlert("no group", "no group", "ok"))->Go();
			return;
		}
		m_group->start();
	}
	
	void doStop() {
		if(!m_group) {
			(new BAlert("no group", "no group", "ok"))->Go();
			return;
		}
		m_group->stop();
	}
	
	void doRelease() {
		if(!m_group) {
			(new BAlert("no group", "no group", "ok"))->Go();
			return;
		}
		m_group->release();
	}
	
	void updatePosition(BMessage* msg) {
		bigtime_t position;
		status_t err = msg->FindInt64("position", &position);
		if(err < B_OK) {
			PRINT((
				"* updatePosition(): no position found\n"));
			return;
		}
		
		int hours, minutes, seconds, frames;
		err = us_to_timecode(
			position,
			&hours, &minutes, &seconds, &frames);
		if(err < B_OK) {
			PRINT((
				"* us_to_timecode() failed: %s\n", strerror(err)));
			return;
		}
		
		char buffer[256];
		sprintf(buffer, "%02d:%02d:%02d:%02d", hours, minutes, seconds, frames);
		m_positionView->SetText(buffer);
	}
};

class App :
	public		BApplication {
	typedef		BApplication _inherited;
	
public:
	cortex::NodeManager*	const manager;
	cortex::NodeManager*	group;
	
	cortex::Connection*		connection;
	
	~App() {
	}
	
	App() :
		BApplication("application/x-vnd.meadgroup-node_manager_test"),
		manager(new cortex::NodeManager()) {
		
		// set up nodes & connections
		status_t err;
		cortex::NodeRef* producer = 0;
		cortex::NodeRef* producer2 = 0;
		cortex::NodeRef* mixer = 0;
	
		// * file producer
		entry_ref testFile;
		if(get_ref_for_path("./test.wav", &testFile) < B_OK) {
			PRINT(("Failed to find ref: %s\n", strerror(err)));
			PostMessage(B_QUIT_REQUESTED);
			return;
		}
		bigtime_t duration;
		err = manager->instantiate(
			testFile, B_BUFFER_PRODUCER, &producer, 0, &duration);
		if(err < B_OK) {
			PRINT((
				"!!! producer not created: %s\n",
				strerror(err)));
			PostMessage(B_QUIT_REQUESTED);
			return;
		}

		// * file producer 2
		if(get_ref_for_path("./test2.wav", &testFile) < B_OK) {
			PRINT(("Failed to find ref: %s\n", strerror(err)));
			PostMessage(B_QUIT_REQUESTED);
			return;
		}
		err = manager->instantiate(
			testFile, B_BUFFER_PRODUCER, &producer2, 0, &duration);
		if(err < B_OK) {
			PRINT((
				"!!! producer2 not created: %s\n",
				strerror(err)));
			PostMessage(B_QUIT_REQUESTED);
			return;
		}

//		// * ToneProducer
//		dormant_node_info producerInfo;
//		int32 count = 1;
//		err = manager->roster->GetDormantNodes(
//			&producerInfo,
//			&count,
//			0,
//			0,
//			"ToneProducer");
//		if(err < B_OK) {
//			PRINT((
//				"!!! producer not found: %s\n",
//				strerror(err)));
//			PostMessage(B_QUIT_REQUESTED);
//			return;
//		}
//		err = manager->instantiate(
//			producerInfo,
//			&producer);
//		if(err < B_OK) {
//			PRINT((
//				"!!! producer not created: %s\n",
//				strerror(err)));
//			PostMessage(B_QUIT_REQUESTED);
//			return;
//		}
			

		// fetch system mixer
		mixer = manager->audioMixerNode();
		if(!mixer) {
			PRINT((
				"!!! No system mixer.\n"));
			PostMessage(B_QUIT_REQUESTED);
			return;
		}
		

		// find connection points
		media_output producerOut;
		err = producer->findFreeOutput(&producerOut);
		if(err < B_OK) {
			PRINT((
				"!!! No producer output found: %s\n",
				strerror(err)));
			PostMessage(B_QUIT_REQUESTED);
			return;
		}
		ASSERT(producerOut.source != media_source::null);

		media_input mixerIn;
		err = mixer->findFreeInput(&mixerIn);	
		if(err < B_OK) {
			PRINT((
				"!!! No mixer input found: %s\n",
				strerror(err)));
			PostMessage(B_QUIT_REQUESTED);
			return;
		}
		ASSERT(mixerIn.destination != media_destination::null);

		err = manager->connect(producerOut, mixerIn, &connection);
		if(err < B_OK) {
			PRINT((
				"!!! connect(producer-out, mixer-in) failed: %s\n",
				strerror(err)));
			PostMessage(B_QUIT_REQUESTED);
			return;
		}

//		// find connection points for producer2
//		err = producer2->findFreeOutput(&producerOut);
//		if(err < B_OK) {
//			PRINT((
//				"!!! No producer2 output found: %s\n",
//				strerror(err)));
//			PostMessage(B_QUIT_REQUESTED);
//			return;
//		}
//		ASSERT(producerOut.source != media_source::null);
//
//		err = mixer->findFreeInput(&mixerIn);	
//		if(err < B_OK) {
//			PRINT((
//				"!!! No mixer input found: %s\n",
//				strerror(err)));
//			PostMessage(B_QUIT_REQUESTED);
//			return;
//		}
//		ASSERT(mixerIn.destination != media_destination::null);
//
//		err = manager->connect(producerOut, mixerIn, &connection);
//		if(err < B_OK) {
//			PRINT((
//				"!!! connect(producer-out, mixer-in) failed: %s\n",
//				strerror(err)));
//			PostMessage(B_QUIT_REQUESTED);
//			return;
//		}

		// create a group
		cortex::NodeGroup* group = manager->createGroup("la di dah");
		group->addNode(producer);
//		group->addNode(producer2);
//		producer2->setCycling(true);
		
		// create window
		TestWindow* window = new TestWindow(group);
		window->Show();
	}
	
	bool QuitRequested() {

//		PRINT((
//			"Disconnecting group.\n"));
//		manager->disconnect(connection);
//
		PRINT((
			"Shutting down NodeManager\n"));
		manager->Lock();
		manager->Quit();
		PRINT((
			"  - done.\n"));

		return true;
	}
};



int main() {
	App app;
	app.Run();
	return 0;
}
/*
	BApplication app("application/x-vnd.meadgroup-node_manager_test");
	status_t err;
		
	cortex::NodeManager* manager = new cortex::NodeManager();


	// start it
	err = group->start();
	if(err < B_OK) {
		PRINT((
			"!!! group->start() failed: %s\n",
			strerror(err)));
		return -1;
	}
	
	// wait for a keypress
	snooze(20000LL);
	
	// stop the group
	err = group->stop();		
	if(err < B_OK) {
		PRINT((
			"!!! group->stop() failed: %s\n",
			strerror(err)));
	}
	
	manager->Lock();
	manager->Quit();
	
	PRINT((
		"deleted.\n"));	
	
	return 0;
}
*/

// END -- node_manager_test.cpp --