/////////////////////////////////////////////////////////////////////////////
// File:        testcommon.cpp
//	Common functions to perform testing
// Author:      Cesar Mauri Loba (cesar at crea-si dot com)
// Copyright:   (C) 2010 Cesar Mauri Loba - CREA Software Systems
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////
#include "testcommon/testcommon.h"
#include "spcore/module.h"
#include "spcore/coreruntime.h"
#include "spcore/iterator.h"
#include "spcore/basictypes.h"

#ifndef WIN32
	#include <unistd.h>
	#include <sys/time.h>
	#include <sys/select.h>	
	#include <termios.h>
	#include <fcntl.h>
#else
	#include <conio.h>
#endif


using namespace spcore;
using namespace std;


void DumpTypeInstance (const spcore::CTypeAny& t, const char* indent)
{
	//cout << indent << "Type name: " << t.getTypeName() << endl;
	cout << indent << "TypeID: " << t.GetTypeID() << endl;
	
	// Dump contents for known types
	if (t.GetTypeID()== CTypeInt::getTypeID()) {
		cout << indent << "Value: " << dynamic_cast<const CTypeInt*>(&t)->getValue() << endl;
	}
	else if (t.GetTypeID()== CTypeFloat::getTypeID()) {
		cout << indent << "Value: " << dynamic_cast<const CTypeFloat*>(&t)->getValue() << endl;
	}
	else if (t.GetTypeID()== CTypeBool::getTypeID()) {
		cout << indent << "Value: " << dynamic_cast<const CTypeBool*>(&t)->getValue() << endl;
	}
	else if (t.GetTypeID()== CTypeString::getTypeID()) {
		cout << indent << "Value: " << dynamic_cast<const CTypeString*>(&t)->get() << endl;			
	}

	// Dump composed instances
	SmartPtr<IIterator<CTypeAny*> > it= t.QueryChildren();
	if (it.get()) {
		std::string new_indent(indent);
		new_indent.append("\t");
		int i= 0;
		for (it->First(); !it->IsDone(); it->Next()) {
			cout << indent << "Dumping component " << ++i << endl;
			DumpTypeInstance (*(it->CurrentItem()), new_indent.c_str());
		}
	}
}


void DumpInputPin (IInputPin& ip)
{
	cout << "\tInput pin: " << ip.GetName() << endl;
//	cout << "\tPin type: " << ip.GetTypeName() << endl;
	cout << "\tPin type ID: " << ip.GetTypeID() << endl;
	cout << "\tProperties: ";
	if (ip.GetProperties() & IInputPin::ALLOW_WRITE) cout << "ALLOW_WRITE ";
	if (ip.GetProperties() & IInputPin::ALLOW_READ) cout << "ALLOW_READ";
	cout << endl;

	if (ip.GetProperties() & IInputPin::ALLOW_READ) {
		SmartPtr<const CTypeAny> value= ip.Read();
		if (value.get()== NULL) 
			cout << "\tPin error: Read returned NULL result\n";
		else
			cout << "\tRead value type ID: " << value->GetTypeID() << endl;
	}
}

void DumpOutputPin (IOutputPin& op)
{
	cout << "Output pin: " << op.GetName() << endl;
//	cout << "\tPin type: " << op.GetTypeName() << endl;
	cout << "\tPin type ID: " << op.GetTypeID() << endl;
}

void DumpComponent (IComponent &c)
{
	cout << "Component name:" << c.GetName() << endl;
	cout << "\tComponent type:" << c.GetTypeName() << endl;
	cout << "\tProvides thread:" << c.ProvidesExecThread() << endl;
	
	cout << "\tInput pins\n";
	SmartPtr<IIterator<IInputPin*> > itipin= c.GetInputPins();
	for (itipin->First(); !itipin->IsDone(); itipin->Next()) {
		cout << "\t";
		DumpInputPin(*itipin->CurrentItem());
	}
	
	cout << "\tOutput pins\n";
	SmartPtr<IIterator<IOutputPin*> > itopin= c.GetOutputPins();
	for (itopin->First(); !itopin->IsDone(); itopin->Next()) {
		cout << "\t";
		DumpOutputPin (*itopin->CurrentItem());
	}
	
	SmartPtr<IIterator<IComponent*> > itc= c.QueryComponents();
	if (itc.get()) {
		cout << "Child components\n";
		cout << "---------------------------------------\n";
		for (itc->First(); !itc->IsDone(); itc->Next()) {
			cout << "\t";
			DumpComponent (*itc->CurrentItem());
		}
	}
}

void DumpCoreRuntime(ICoreRuntime* core)
{
	if (!core) {
		cout << "Critical error: getSpCoreRuntime() returned NULL\n" << endl;
		return;
	}

	SmartPtr<IIterator<spcore::ITypeFactory*> > ittf= core->QueryTypes();
	cout << "Dumping registered types\n";
	for (ittf->First(); !ittf->IsDone(); ittf->Next()) {
		spcore::ITypeFactory* item= ittf->CurrentItem();
		cout << "\t" << item->GetName() << endl;
	}
	
	SmartPtr<IIterator<IComponentFactory*> > itcf= core->QueryComponents();
	cout << "Dumping registered components\n";
	for (itcf->First(); !itcf->IsDone(); itcf->Next()) {
		cout << "\t" << itcf->CurrentItem()->GetName() << endl;
	}
}

void ExitErr (const char* msg)
{
	cerr << msg << endl;
	exit (-1);
}

#ifndef WIN32	
int getch_no_block(void)
{
	struct termios oldt, newt;
	int ch;
	int oldf;

	tcgetattr(STDIN_FILENO, &oldt);
	newt = oldt;
	newt.c_lflag &= ~(ICANON | ECHO);
	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
	oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
	fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

	ch = getchar();

	tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
	fcntl(STDIN_FILENO, F_SETFL, oldf);

	if(ch != EOF)
	{
		//ungetc(ch, stdin);
		return ch;
	}

	return 0;
}
#else
int getch_no_block(void)
{
	if (kbhit()) return _getch();
	return 0;
}
#endif

unsigned long get_mili_count()
{
#if defined(WIN32)
	// Windows
	return GetTickCount();
#else
	// Linux
	struct timeval tv;
	//struct timezone tz;
	gettimeofday(&tv, NULL); //&tz);

	return (((unsigned long) tv.tv_sec * 1000000 + (unsigned long) tv.tv_usec) / 1000);
#endif	
}

void calls_per_second()
{
	static unsigned int call_count= 0;
	static unsigned long prev_tstamp= 0;
	unsigned long tstamp = get_mili_count();

	++call_count;

	if (!prev_tstamp) {
		prev_tstamp= tstamp;
		return;
	}

	if (tstamp - prev_tstamp>= 1000) {
		printf ("call per second: %.2f\n", (float) (call_count * 1000) / (float) (tstamp - prev_tstamp) );
		call_count= 0;
		prev_tstamp= tstamp;
	}	
}

void sleep_milliseconds(unsigned ms)
{
#ifdef WIN32
	Sleep (ms);
#else
	usleep (ms * 1000);
#endif
}
