/*
 *   Copyright (C) 2009,2010,2012,2014 by Jonathan Naylor G4KLX
 *
 *   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 2 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, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "DVToolWriterThread.h"

#include "DVToolWriterApp.h"
#include "MessageData.h"
#include "HeaderData.h"

CDVToolWriterThread::CDVToolWriterThread() :
wxThread(wxTHREAD_JOINABLE),
m_soundcard(NULL),
m_dongle(NULL),
m_encoder(),
m_stopped(true),
m_reader(NULL),
m_writer(NULL),
m_audioBuffer(DSTAR_AUDIO_BLOCK_SIZE * 30U),
m_count(0U),
m_killed(false)
{
}

CDVToolWriterThread::~CDVToolWriterThread()
{
}

void* CDVToolWriterThread::Entry()
{
	// Wait here until we have the essentials to run
	while (!m_killed && (m_dongle == NULL || m_soundcard == NULL))
		Sleep(500UL);		// 1/2 sec

	if (m_killed)
		return NULL;

	m_dongle->Create();
	m_dongle->SetPriority(100U);
	m_dongle->Run();

	while (!m_killed) {
		if (m_writer != NULL && m_reader != NULL)
			processFile();

		if (m_writer != NULL && m_reader == NULL)
			processSoundcard();

		Sleep(500UL);		// 1/2 sec
	}

	m_soundcard->close();
	delete m_soundcard;

	m_dongle->kill();

	return NULL;
}

void CDVToolWriterThread::kill()
{
	m_killed = true;
}

void CDVToolWriterThread::encodeCallback(const unsigned char* ambe, unsigned int length)
{
	if (m_writer == NULL)
		return;

	unsigned char frame[DV_FRAME_LENGTH_BYTES];

	::memcpy(frame, ambe, VOICE_FRAME_LENGTH_BYTES);

	if (m_count == 0U)
		::memcpy(frame + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
	else
		m_encoder.getData(frame + VOICE_FRAME_LENGTH_BYTES);

	m_count++;
	if (m_count >= 21U)
		m_count = 0U;

	m_writer->writeFrame(frame, DV_FRAME_LENGTH_BYTES);
}

bool CDVToolWriterThread::processFile(CWAVFileReader* reader, CHeaderData* header, const wxString& text, IFileWriter* writer)
{
	wxASSERT(reader != NULL);
	wxASSERT(header != NULL);
	wxASSERT(writer != NULL);

	wxLogMessage(wxT("Processing file - %s"), reader->getFilename().c_str());

	if (!m_stopped) {
		wxLogError(wxT("Already processing a file"));
		return false;
	}

	if (m_dongle == NULL) {
		wxLogError(wxT("No Dongle has been set"));
		return false;
	}

	if (m_reader != NULL) {
		m_reader->close();
		delete m_reader;
	}

	if (m_writer != NULL) {
		m_writer->close();
		delete m_writer;
	}

	wxLogMessage(wxT("Writing file - %s"), writer->getFilename().c_str());

	bool ret = writer->open();
	if (!ret) {
		wxLogError(wxT("Cannot open the file for writing"));
		return false;
	}

	writer->writeHeader(*header);

	m_encoder.reset();
	m_encoder.setMessageData(text);
	m_encoder.setHeaderData(*header);

	delete header;

	m_reader = reader;
	m_writer = writer;

	return true;
}

bool CDVToolWriterThread::processAudio(CHeaderData* header, const wxString& text, IFileWriter* writer)
{
	wxASSERT(header != NULL);
	wxASSERT(writer != NULL);

	if (!m_stopped) {
		wxLogError(wxT("Already processing a file"));
		return false;
	}

	if (m_dongle == NULL) {
		wxLogError(wxT("No Dongle has been set"));
		return false;
	}

	if (m_writer != NULL) {
		m_writer->close();
		delete m_writer;
	}

	wxLogMessage(wxT("Writing file - %s"), writer->getFilename().c_str());

	bool ret = writer->open();
	if (!ret) {
		wxLogError(wxT("Cannot open the file for writing"));
		return false;
	}

	writer->writeHeader(*header);

	m_encoder.reset();
	m_encoder.setMessageData(text);
	m_encoder.setHeaderData(*header);

	delete header;

	m_writer = writer;

	return true;
}

void CDVToolWriterThread::processAudio()
{
	m_stopped = true;
}

void CDVToolWriterThread::setSoundCard(CSoundCardReaderWriter* soundcard)
{
	wxASSERT(soundcard != NULL);

	if (!m_stopped) {
		soundcard->close();
		delete soundcard;
		return;
	}

	if (m_soundcard != NULL) {
		m_soundcard->close();
		delete m_soundcard;
	}

	m_soundcard = soundcard;
}

void CDVToolWriterThread::setDongle(CDongleThread* dongle)
{
	wxASSERT(dongle != NULL);

	if (!m_stopped) {
		dongle->kill();
		return;
	}

	if (m_dongle != NULL)
		m_dongle->kill();

	m_dongle = dongle;
}

void CDVToolWriterThread::processFile()
{
	m_stopped = false;

	m_count = 0U;

	m_dongle->setEncode();

	for (;;) {
		if (m_killed) {
			m_dongle->setIdle();
			m_reader->close();
			m_writer->close();
			delete m_reader;
			delete m_writer;
			m_reader = NULL;
			m_writer = NULL;
			return;
		}

		unsigned int space = m_dongle->getEncodeSpace();
		if (space >= DSTAR_RADIO_BLOCK_SIZE) {
			wxFloat32 audio[DSTAR_RADIO_BLOCK_SIZE];
			::memset(audio, 0x00U, DSTAR_RADIO_BLOCK_SIZE * sizeof(wxFloat32));
			unsigned int n = m_reader->read(audio, DSTAR_RADIO_BLOCK_SIZE);

			m_dongle->writeEncode(audio, DSTAR_RADIO_BLOCK_SIZE);

			// End of file?
			if (n != DSTAR_RADIO_BLOCK_SIZE)
				break;
		}

		Sleep(FRAME_TIME_MS);
	}

	Sleep(FRAME_TIME_MS * 30U);

	// Release the input file
	m_reader->close();
	delete m_reader;
	m_reader = NULL;

	// Release the output file
	m_writer->close();
	delete m_writer;
	m_writer = NULL;

	::wxGetApp().showFinished();

	wxLogMessage(wxT("Finished processing the file"));

	m_stopped = true;

	m_dongle->setIdle();
}

void CDVToolWriterThread::processSoundcard()
{
	m_stopped = false;

	m_count = 0U;

	m_dongle->setEncode();

	for (;;) {
		if (m_killed) {
			m_dongle->setIdle();
			m_writer->close();
			delete m_writer;
			m_writer = NULL;
			return;
		}

		if (m_stopped)
			break;

		unsigned int size = m_audioBuffer.dataSpace();
		if (size >= DSTAR_RADIO_BLOCK_SIZE) {
			wxFloat32 audio[DSTAR_RADIO_BLOCK_SIZE];
			unsigned int n = m_audioBuffer.getData(audio, DSTAR_RADIO_BLOCK_SIZE);
			m_dongle->writeEncode(audio, n);
		}

		Sleep(FRAME_TIME_MS / 4U);
	}

	Sleep(FRAME_TIME_MS * 30U);

	// Release the output file
	m_writer->close();
	delete m_writer;
	m_writer = NULL;

	wxLogMessage(wxT("Finished processing from the sound card"));

	m_stopped = true;

	m_dongle->setIdle();
}

void CDVToolWriterThread::callback(const wxFloat32* input, wxFloat32* output, unsigned int nSamples, int id)
{
	if (m_writer != NULL && m_reader == NULL)
		m_audioBuffer.addData(input, nSamples);
}
