/* -*- c++ -*- */
/* 
 * Copyright 2080-2011 Steve Glass
 * 
 * This file is part of OP25.
 * 
 * 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 "decoder_bf_impl.h"

#include <gnuradio/io_signature.h>
#include <algorithm>
#include <iostream>
#include <itpp/comm/bch.h>
#include "logfile_du_handler.h"
#include "offline_imbe_decoder.h"
#include "voice_du_handler.h"
#include "op25_yank.h"
#include "bch.h"

using namespace std;

namespace gr {
  namespace op25 {

    decoder_bf::sptr
    decoder_bf::make(bool idle_silence /*= true*/, bool verbose /*= false*/)
    {
      return gnuradio::get_initial_sptr
        (new decoder_bf_impl(idle_silence, verbose));
    }

    decoder_bf_impl::decoder_bf_impl(bool idle_silence /*= true*/, bool verbose /*= false*/) :
      gr::block("decoder_bf",
		gr::io_signature::make(1, 1, sizeof(uint8_t)),
		gr::io_signature::make(0, 1, sizeof(float))),
      d_data_unit(),
      d_data_unit_handler(),
      d_frame_hdr(),
      d_imbe(imbe_decoder::make()),
      d_state(SYNCHRONIZING),
      d_p25cai_du_handler(NULL),
      d_idle_silence(idle_silence),
      d_verbose(false)
    {
      set_logging(verbose);

      d_p25cai_du_handler = new p25cai_du_handler(d_data_unit_handler,
						    "224.0.0.1", 23456);
      d_data_unit_handler = data_unit_handler_sptr(d_p25cai_du_handler);

      d_snapshot_du_handler = new snapshot_du_handler(d_data_unit_handler);
      d_data_unit_handler = data_unit_handler_sptr(d_snapshot_du_handler);

      d_crypto_module = crypto_module::sptr(new crypto_module(verbose));

      d_crypto_module_du_handler = crypto_module_du_handler::sptr(new crypto_module_du_handler(d_data_unit_handler, d_crypto_module));
      d_data_unit_handler = data_unit_handler_sptr(d_crypto_module_du_handler);

      d_data_unit_handler = data_unit_handler_sptr(new voice_du_handler(d_data_unit_handler, d_imbe, d_crypto_module));
    }

    decoder_bf_impl::~decoder_bf_impl()
    {
    }

    void
    decoder_bf_impl::forecast (int noutput_items,
			       gr_vector_int &ninput_items_required)
    {
      /* This block consumes 4800 symbols/s and produces 8000
       * samples/s. That's a work rate of 3/5 or 0.6. If no audio
       * output is available we'll produce silence.
       */
      const size_t nof_inputs = ninput_items_required.size();
      const int nsamples_reqd = .6 * noutput_items;
      fill(&ninput_items_required[0],
	   &ninput_items_required[nof_inputs],
	   nsamples_reqd);
    }

    gr::msg_queue::sptr
    decoder_bf_impl::get_msgq() const
    {
      return d_snapshot_du_handler->get_msgq();
    }

    void
    decoder_bf_impl::set_msgq(gr::msg_queue::sptr msgq)
    {
      d_snapshot_du_handler->set_msgq(msgq);
    }

    int
    decoder_bf_impl::general_work (int noutput_items,
                       gr_vector_int &ninput_items,
                       gr_vector_const_void_star &input_items,
                       gr_vector_void_star &output_items)
    {
      try {
        gr::thread::scoped_lock lock(d_mutex);

        // process input
        const uint8_t *in = reinterpret_cast<const uint8_t*>(input_items[0]);
        for(int i = 0; i < ninput_items[0]; ++i) {
            dibit d = in[i] & 0x3;
            receive_symbol(d);
        }
        consume_each(ninput_items[0]);

        // produce audio
        audio_samples *samples = d_imbe->audio();
        float *out = reinterpret_cast<float*>(output_items[0]);
        const int n = min(static_cast<int>(samples->size()), noutput_items);
        if(0 < n) {
            copy(samples->begin(), samples->begin() + n, out);
            samples->erase(samples->begin(), samples->begin() + n);
        }
        if((d_idle_silence) && (n < noutput_items)) {
            fill(out + n, out + noutput_items, 0.0);
        }
        return (d_idle_silence ? noutput_items : n);

      } catch(const std::exception& x) {
        cerr << x.what() << endl;
        exit(1);
      } catch(...) {
        cerr << "unhandled exception" << endl;
        exit(2); }
    }

    const char*
    decoder_bf_impl::destination() const
    {
      return d_p25cai_du_handler->destination();
    }

    bool
    decoder_bf_impl::correlated()
    {
      static const bool FS[] = {
	0, 1, 0, 1, 0, 1, 0, 1,
	0, 1, 1, 1, 0, 1, 0, 1, 
	1, 1, 1, 1, 0, 1, 0, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 
	0, 1, 1, 1, 0, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1
      };
      static const size_t FS_SZ = sizeof(FS)/sizeof(FS[0]);

      uint8_t errs = 0;
      for(size_t i = 0; i < FS_SZ; ++i) {
	if(d_frame_hdr[i] ^ FS[i]) {
	  ++errs;
	}
      }
      return (errs <= 4);
    }

    data_unit_sptr
    decoder_bf_impl::identified()
    {
      static const size_t NID[] = {
	63,  62,  61,  60,  59,  58,  57,  56,
	55,  54,  53,  52,  51,  50,  49,  48,
	112, 111, 110, 109, 108, 107, 106, 105,
	104, 103, 102, 101, 100, 99,  98,  97,
	96,  95,  94,  93,  92,  91,  90,  89,
	88,  87,  86,  85,  84,  83,  82,  81,
	80,  79,  78,  77,  76,  75,  74,  73,
	72,  69,  68,  67,  66,  65,  64,
      };
      size_t NID_SZ = sizeof(NID) / sizeof(NID[0]);
      
      bit_vector b(NID_SZ);
      yank(d_frame_hdr,  NID, NID_SZ, b, 0);
      if(bchDec(b) >= 0) {
	yank_back(b, 0, d_frame_hdr, NID, NID_SZ);
	d_data_unit = data_unit::make_data_unit(d_frame_hdr);
  d_data_unit->set_logging(d_verbose);
      } else {
	data_unit_sptr null;
	d_data_unit = null;
      }
      return d_data_unit;
    }

    void
    decoder_bf_impl::receive_symbol(dibit d)
    {
      d_frame_hdr.push_back(d & 0x2);
      d_frame_hdr.push_back(d & 0x1);
      const size_t frame_hdr_sz = d_frame_hdr.size();

      switch(d_state) {
      case SYNCHRONIZING:
	if(48 <= frame_hdr_sz) {
	  d_frame_hdr.erase(d_frame_hdr.begin(), d_frame_hdr.begin() + (frame_hdr_sz - 48));
	  if(correlated()) {
            d_state = IDENTIFYING;
	  }
	}
	break;
      case IDENTIFYING:
	if(114 == frame_hdr_sz) {
	  if(identified()) {
            d_state = READING;
	  } else {
            d_state = SYNCHRONIZING;
	  }
	}
	break;
      case READING:
	d_data_unit->extend(d);
	if(d_data_unit->is_complete()) {
	  d_data_unit->correct_errors();
	  d_data_unit_handler->handle(d_data_unit);
	  data_unit_sptr null;
	  d_data_unit = null;
	  d_state = SYNCHRONIZING;
	}
	break;
      }
    }

    void
    decoder_bf_impl::set_idle_silence(bool idle_silence/* = true*/)
    {
      gr::thread::scoped_lock lock(d_mutex);

      d_idle_silence = idle_silence;
    }

    void
    decoder_bf_impl::set_logging(bool verbose/* = true*/)
    {
      if (verbose) fprintf(stderr, "[%s<%lu>] verbose logging enabled\n", name().c_str(), unique_id());

      d_verbose = verbose;

      if (d_crypto_module)
        d_crypto_module->set_logging(verbose);
    }

    void
    decoder_bf_impl::set_key(const key_type& key)
    {
      d_crypto_module->set_key(key);
    }

    void
    decoder_bf_impl::set_key_map(const key_map_type& keys)
    {
      d_crypto_module->set_key_map(keys);
    }
  } /* namespace op25 */
} /* namespace gr */
