/*---------------------------------------------------------------------------*\

	FILE....: callmonitor.cpp
	AUTHOR..: Ron Lee
	DATE....: 19/6/2007
	DESC....: Implementation of full duplex tap functions


	 Voicetronix Voice Processing Board (VPB) Software
	 Copyright (C) 2007-2008, Ron Lee, Voicetronix www.voicetronix.com.au

	 This library is free software; you can redistribute it and/or
	 modify it under the terms of the GNU Lesser General Public
	 License version 2.1 as published by the Free Software Foundation.

	 This library 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
	 Lesser General Public License for more details.

	 You should have received a copy of the GNU Lesser General Public
	 License along with this library; if not, write to the Free Software
	 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
	 MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include "apifunc.h"
#include "mapdev.h"
#include "mess.h"

#include <fcntl.h>
#include <unistd.h>
#include <cstdio>

using std::string;


struct LogThread
{ //{{{
	typedef std::vector<LogThread>  List;

	pthread_t	thread;
	pthread_mutex_t	mutex;
	bool		active;
	string		filename;
	WFILE	       *wav;
	int		fd;

	LogThread()
	    : active(false)
	{
		pthread_mutex_init(&mutex, NULL);
	}

	~LogThread()
	{
		pthread_mutex_destroy(&mutex);
	}
}; //}}}

static LogThread::List  LogThreads;


static void logging_thread_cleanup(void *p)
{ //{{{
	VPBPortHandle	h = (long)p;

	vpb_wave_close_write(LogThreads[h].wav);
	close(LogThreads[h].fd);
	LogThreads[h].active = false;
} //}}}

static void *logging_thread(void *p)
{ //{{{
	size_t		NBUF   = sysconf(_SC_PAGESIZE);
	VPBPortHandle	h      = (long)p;
	LogThread      &thread = LogThreads[h];
	char		buf[NBUF];
	char		tapnode[128];
	unsigned short  bd, ch;
	int		oldcancelstate;

	maphndletodev(h, &bd, &ch);
	snprintf( tapnode, sizeof(tapnode), "/dev/vt/board%u/tap%u", vpb_c->vpbreg(bd)->cardtypnum, ch);
	tapnode[sizeof(tapnode) - 1] = '\0';

	pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldcancelstate );

	thread.fd = open(tapnode, O_RDONLY);
	if( thread.fd == -1 ) {
		mprintf("FAILED to open '%s': %m", tapnode);
		goto hell;
	}

	try {
		vpb_wave_open_write(&thread.wav, thread.filename, VPB_LINEAR);
	}
	catch( const std::exception &e ) {
		mprintf("FAILED to open log file: %s", e.what());
		goto hell2;
	}

	pthread_cleanup_push(logging_thread_cleanup, p);
	pthread_setcancelstate( oldcancelstate, NULL );

	try {
		for(;;) {
			int n = read(thread.fd, buf, NBUF);
			if( n == 0 ) continue;
			if( n == -1 ) {
				mprintf("Read error from '%s': %m", tapnode);
				break;
			}
			vpb_wave_write(thread.wav, buf, n);
		}
	} catch( const std::exception &e ) {
		mprintf("FAILED writing log file: %s", e.what());
	}

	pthread_cleanup_pop(1);
	return NULL;

    hell2:
	close(thread.fd);

    hell:
	pthread_detach(pthread_self());
	thread.active = false;

	return NULL;
} //}}}

void WINAPI vpb_log_to_file(VPBPortHandle h, const std::string &filename)
{ //{{{
	// Allocate space for all possible threads on the first call.
	// We hold references to objects in this list in the individual
	// threads which can be invalidated if a reallocation occurs later.
	if( LogThreads.empty() )
		LogThreads.resize( get_total_port_count() );

	pthread_mutex_lock(&LogThreads[h].mutex);

	if( LogThreads[h].active ) {
		pthread_mutex_unlock(&LogThreads[h].mutex);
		throw VpbException("Logging is already active on handle %d", h);
	}

	LogThreads[h].active   = true;
	LogThreads[h].filename = filename;

	pthread_mutex_unlock(&LogThreads[h].mutex);

	if( pthread_create( &LogThreads[h].thread, NULL, logging_thread, (void*)(intptr_t)h ) ) {
		LogThreads[h].active = false;
		throw VpbException("Failed to start logging thread for handle %d", h);
	}
} //}}}

void WINAPI vpb_log_terminate(VPBPortHandle h)
{ //{{{
	if( (VPBPortHandle)LogThreads.size() <= h || ! LogThreads[h].active )
		throw VpbException("No logging thread active for port %d", h);

	pthread_cancel(LogThreads[h].thread);
	pthread_join(LogThreads[h].thread, NULL);
} //}}}

void WINAPI vpb_monitor_audio(VPBPortHandle dest, VPBPortHandle src)
{ //{{{
	unsigned short  b1, c1;
	unsigned short  b2, c2;

	maphndletodev(dest, &b1, &c1);
	maphndletodev(src,  &b2, &c2);

	VPBREG	&reg = *vpb_c->vpbreg(b1);

	reg.hostdsp->TapListen(c1, b2, c2);
} //}}}

void WINAPI vpb_monitor_terminate(VPBPortHandle h)
{ //{{{
	unsigned short  bd, ch;
	maphndletodev(h, &bd, &ch);

	VPBREG	&reg = *vpb_c->vpbreg(bd);

	reg.hostdsp->UNListen(ch);
} //}}}

