


#include <stdio.h>
#include "detector.h"
#include "../parsers/spdif/spdif_header.h"
#include "../parsers/mpa/mpa_header.h"
#include "../parsers/ac3/ac3_header.h"
#include "../parsers/dts/dts_header.h"


static const HeaderParser *spdif_dts_parsers[] =
{
  &spdif_header,
  &dts_header
};

static const HeaderParser *uni_parsers[] =
{
  &spdif_header,
  &ac3_header,
  &dts_header,
  &mpa_header
};


Detector::Detector()
:NullFilter(-1)
{
  out_spk = spk_unknown;
  state = state_trans;

  spdif_dts_header.set_parsers(spdif_dts_parsers, array_size(spdif_dts_parsers));
  uni_header.set_parsers(uni_parsers, array_size(uni_parsers));
}

Detector::~Detector()
{
}

const HeaderParser *
Detector::find_parser(Speakers spk) const
{
  switch (spk.format)
  {
    case FORMAT_RAWDATA: return &uni_header;
    case FORMAT_PCM16:   return &spdif_dts_header;
    case FORMAT_SPDIF:   return &spdif_dts_header;

    case FORMAT_AC3:     return &ac3_header;
    case FORMAT_DTS:     return &dts_header;
    case FORMAT_MPA:     return &mpa_header;
  };

  return 0;
}

void
Detector::reset()
{
  NullFilter::reset();

  out_spk = spk_unknown;
  state = state_trans;

  stream.reset();
  sync_helper.reset();
}

bool
Detector::is_ofdd() const
{
  return true;
}

bool
Detector::query_input(Speakers _spk) const
{
  return find_parser(_spk) != 0;
}

bool
Detector::set_input(Speakers _spk)
{
  reset();

  const HeaderParser *hparser = find_parser(_spk);
  if (!hparser)
    return false;

  if (!stream.set_parser(hparser))
    return false;

  spk = _spk;
  return true;
}


bool
Detector::process(const Chunk *_chunk)
{
  assert(is_empty());

  
  if (_chunk->is_dummy())
    return true;

  
  FILTER_SAFE(receive_chunk(_chunk));
  sync_helper.receive_sync(sync, time);
  sync = false;

  if (load())
  {
    switch (state)
    {
    case state_trans:
      if (stream.is_in_sync())
      {
        
        out_spk = stream.get_spk();
        state = state_frame;
      }
      else
      {
        
        out_spk = spk;
        state = state_debris;
      }

      return true;

    case state_empty_debris:
      if (stream.is_in_sync())
        
        state = state_format_change;
      else
        
        state = state_debris;

      return true;

    case state_empty_frame:
      if (stream.is_in_sync())
      {
        if (stream.is_new_stream())
          
          state = state_format_change;
        else if (stream.is_debris_exists())
          
          state = state_frame_debris;
        else
          
          state = state_frame;
      }
      else
        
        state = state_format_change;

      return true;
    }

    
    assert(false);
    return false;
  }
  else if (flushing)
  {
    switch (state)
    {
    case state_trans:
      
      
      

      
      
      
      
      reset();
      return true;

    case state_empty_debris:
      
      
      state = state_no_debris;
      return true;

    case state_empty_frame:
      
      
      state = state_no_frame;
      return true;
    }

    
    assert(false);
    return false;
  }
  else
    
    return true;
}

Speakers
Detector::get_output() const
{
  return out_spk;
}

bool
Detector::is_empty() const
{
  return state == state_trans || state == state_empty_debris || state == state_empty_frame;
}

bool
Detector::get_chunk(Chunk *_chunk)
{
  assert(!is_empty());

  switch (state)
  {
    
    
    

    case state_debris:
      send_debris(_chunk);
      state = state_no_debris;
      return true;

    case state_no_debris:
      
      
      
      
      
      

      if (load())
      {
        if (stream.is_in_sync())
        {
          send_eos(_chunk);
          out_spk = stream.get_spk();
          state = state_frame;
        }
        else
        {
          send_debris(_chunk);
          state = state_no_debris;
        }
      }
      else 
      {
        if (flushing)
        {
          
          
          
          stream.flush();
          _chunk->set_rawdata(out_spk, stream.get_debris(), stream.get_debris_size());
          _chunk->set_eos();
          sync_helper.send_sync(_chunk);

          
          
          
          
          reset();
        }
        else
        {
          _chunk->set_dummy();
          state = state_empty_debris;
        }
      }
      return true;

    
    
    

    case state_frame_debris:
      send_frame_debris(_chunk);
      state = state_frame;
      return true;

    case state_frame:
      send_frame(_chunk);
      state = state_no_frame;
      return true;

    case state_no_frame:
      
      
      
      
      
      

      if (load())
      {
        if (stream.is_in_sync())
        {
          if (stream.is_new_stream())
          {
            send_eos(_chunk);
            out_spk = stream.get_spk();
            state = state_frame;
          }
          else
          {
            if (stream.is_debris_exists())
            {
              send_frame_debris(_chunk);
              state = state_frame;
            }
            else
            {
              send_frame(_chunk);
              state = state_no_frame;
            }
          }
        }
        else 
        {
          
          send_eos(_chunk);

          if (stream.is_debris_exists())
          {
            
            out_spk = spk;
            state = state_debris;
          }
          else
          {
            
            out_spk = spk_unknown;
            state = state_trans;
          }
        }
      }
      else 
      {
        if (flushing)
        {
          send_eos(_chunk);
          
          
          
          
          reset();
        }
        else
        {
          _chunk->set_dummy();
          state = state_empty_frame;
        }
      }
      return true;

    
    

    case state_format_change:
      send_eos(_chunk);

      if (stream.is_in_sync())
      {
        out_spk = stream.get_spk();
        if (stream.is_debris_exists())
          state = state_frame_debris;
        else
          state = state_frame;
      }
      else if (stream.is_debris_exists())
      {
        out_spk = spk;
        state = state_debris;
      }
      else
      {
        out_spk = spk_unknown;
        state = state_trans;
      }
      return true;
  }

  
  assert(false);
  return false;
}




bool
Detector::load()
{
  uint8_t *end = rawdata + size;
  bool result = stream.load(&rawdata, end);
  size = end - rawdata;
  return result;
}

void
Detector::send_debris(Chunk *_chunk)
{
  _chunk->set_rawdata(out_spk, stream.get_debris(), stream.get_debris_size());
  sync_helper.send_sync(_chunk);
}

void
Detector::send_frame_debris(Chunk *_chunk)
{
  _chunk->set_rawdata(out_spk, stream.get_debris(), stream.get_debris_size());
  
}

void
Detector::send_frame(Chunk *_chunk)
{
  _chunk->set_rawdata(out_spk, stream.get_frame(), stream.get_frame_size());
  sync_helper.send_sync(_chunk);
}

void
Detector::send_eos(Chunk *_chunk)
{
  _chunk->set_empty(out_spk);
  _chunk->set_eos(true);

  
  sync_helper.reset(); 
}
