/* -*- c++ -*- */
/* 
 * Copyright 2019 Jay Arrowfield.
 * 
 * This 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, or (at your option)
 * any later version.
 * 
 * This software 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 software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gnuradio/io_signature.h>
#include "pocsag_decoder_impl.h"

#define WITH_DEBUG_MESSAGES		0

namespace gr
{
namespace jay
{

pocsag_decoder::sptr pocsag_decoder::make()
{
	return gnuradio::get_initial_sptr (new pocsag_decoder_impl());
}

const char* pocsag_decoder_impl::stateNames[NUM_STATES] = {"PreambleSlow", "SyncSlow", "DataSlow", "PreambleFast", "SyncFast", "DataFast"};

/*
 * The private constructor
 */
pocsag_decoder_impl::pocsag_decoder_impl()
: gr::block("pocsag_decoder", gr::io_signature::make(1, 1, sizeof(uint8_t)), gr::io_signature::make(1, 1, sizeof(uint8_t)))
, m_state(PreambleSlow)
, m_stateNext(PreambleSlow)
, m_word(0)
, m_wordCount(0)
, m_wordSize(1)
, m_dataCount(0)
, bufferWi(0)
{
}

/*
 * Our virtual destructor.
 */
pocsag_decoder_impl::~pocsag_decoder_impl()
{
}

void pocsag_decoder_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required)
{
	ninput_items_required[0] = 8*noutput_items;
}

int pocsag_decoder_impl::general_work (int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items)
{
	uint8_t zero = 0x01;
	const uint8_t fastSyncWord[4] = {0x7C, 0xD2, 0x15, 0xD8};
	const uint8_t *in = (const uint8_t *) input_items[0];
	uint8_t *out = (uint8_t *) output_items[0];

	// Do <+signal processing+>
	size_t i = 0;
	size_t j = 0;
	for (i=0; i < ninput_items[0]; i++)
	{
		if (in[i] == 0xFF)
		{
			if (m_state != PreambleSlow)
			{
				m_state = PreambleSlow;
				m_wordCount = 0;
				m_dataCount = 0;
#if WITH_DEBUG_MESSAGES
				fprintf(stderr, "Reset\n");
#endif
			}
			continue;
		}
		
		if (in[i] == zero)
		{
			m_word = (m_word << 1) | 0;
		}
		else
		{
			m_word = (m_word << 1) | 1;
		}
		
		if (m_wordCount != 0)
		{
			m_wordCount--;
			continue;
		}
		
		m_stateNext = m_state;
		switch (m_state)
		{
			case PreambleSlow:
#if 0				
				if ((m_word & 0xFF) == 0xCC)
				{
					m_wordSize = 8;
					m_dataCount++;
					if (m_dataCount == 8)
					{
						m_wordSize = 1;
						m_stateNext = SyncSlow;
					}
					
				}
				else 
#endif
				if ((m_word & 0xFF) == 0xAA)
				{
					m_wordSize = 8;
					m_dataCount++;
					if (m_dataCount == 4)
					{
						m_wordSize = 1;
						m_dataCount = 0;
						m_stateNext = SyncFast;
					}
					
				}
				else
				{
					m_wordSize = 1;
					m_dataCount = 0;
				}
				break;
			case SyncSlow:
				if ((m_word & 0xFF) == 0x3F)
				{
					m_wordSize = 8;
					m_dataCount = 1;
					out[j++] = m_word;
					m_stateNext = DataSlow;
				}
				break;
			case DataSlow:
				m_dataCount++;
				out[j++] = m_word;
				if (m_dataCount == 0xA0)
				{
					m_wordSize = 1;
					m_stateNext = PreambleFast;
				}
				break;
			case PreambleFast:
				if ((m_word & 0xFF) == 0xAA)
				{
					m_wordSize = 8;
					m_dataCount++;					
					if (m_dataCount == 8)
					{
						m_wordSize = 1;
						m_dataCount = 0;
						m_stateNext = SyncFast;
					}
				}
				else
				{
					m_dataCount = 0;
				}
				break;
			case SyncFast:
				if ((m_word & 0xFF) == fastSyncWord[m_dataCount])
				{
					out[j++] = m_word;
					m_wordSize = 8;
					m_dataCount++;
					if (m_dataCount == 4)
					{
						m_stateNext = DataFast;
					}
				}
				break;
			case DataFast:
				m_wordSize = 8;
				out[j++] = m_word;
				break;
				
		}
		if (m_state != m_stateNext)
		{
#if WITH_DEBUG_MESSAGES
			fprintf(stderr, "State changed: %s => %s\n", stateNames[m_state], stateNames[m_stateNext]);
#endif
		}
		m_wordCount = m_wordSize - 1;
		m_state = m_stateNext;
	}

	// Tell runtime system how many input items we consumed on
	// each input stream.
	consume_each (i);

	memcpy(&buffer[bufferWi], out, j);
	
	bufferWi += j;
	
	if (bufferWi >= 4)
	{
		size_t numProcessed = dec.process(buffer, bufferWi);
		bufferWi -= numProcessed;
		memcpy(buffer, &buffer[numProcessed], bufferWi);
	}
	
	// Tell runtime system how many output items we produced.
	return j;

}

} /* namespace jay */
} /* namespace gr */

