

#ifndef VALIB_FILTER_H
#define VALIB_FILTER_H

#include "spk.h"

class Chunk;
class Sink;
class Source;
class Filter;

class NullSink;
class NullFilter;
class SourceFilter;
class SinkFilter;


#define FILTER_SAFE(call) if (!call) return false;










































































































class Chunk
{
public:
  
  

  Speakers  spk;

  uint8_t  *rawdata;
  samples_t samples;
  size_t    size;

  bool      sync;
  vtime_t   time;
            
  bool      eos;

  
  

  Chunk():
    spk(spk_unknown),
    rawdata(0),
    size(0),
    sync(false),
    time(0),
    eos(false)
  {}

  Chunk(Speakers _spk, 
    bool _sync = false, vtime_t _time = 0, bool _eos  = false)
  {
    set_empty(_spk, _sync, _time, _eos);
  }

  Chunk(Speakers _spk, samples_t _samples, size_t _size,
    bool _sync = false, vtime_t _time = 0, bool _eos  = false)
  {
    set_linear(_spk, _samples, _size, _sync, _time, _eos);
  }

  Chunk(Speakers _spk, uint8_t *_rawdata, size_t _size,
    bool _sync = false, vtime_t _time = 0, bool _eos  = false)
  {
    set_rawdata(_spk, _rawdata, _size, _sync, _time, _eos);
  }

  Chunk(Speakers _spk, uint8_t *_rawdata, samples_t _samples, size_t _size,
    bool _sync = false, vtime_t _time = 0, bool _eos  = false)
  {
    set(_spk, _rawdata, _samples, _size, _sync, _time, _eos);
  }


  inline void set_dummy()
  {
    spk = spk_unknown;
    rawdata = 0;
    samples.zero();
    size = 0;
    sync = false;
    time = 0;
    eos = false;
  }
  
  inline void set_empty(Speakers _spk, 
    bool _sync = false, vtime_t _time = 0, bool _eos  = false)
  {
    spk = _spk;
    rawdata = 0;
    samples.zero();
    size = 0;
    sync = _sync;
    time = _time;
    eos = _eos;
  }

  inline void set_linear(Speakers _spk, samples_t _samples, size_t _size,
    bool _sync = false, vtime_t _time = 0, bool _eos  = false)
  {
    
    assert(_spk.format == FORMAT_LINEAR);

    spk = _spk;
    rawdata = 0;
    samples = _samples;
    size = _size;
    sync = _sync;
    time = _time;
    eos = _eos;
  }

  inline void set_rawdata(Speakers _spk, uint8_t *_rawdata, size_t _size,
    bool _sync = false, vtime_t _time = 0, bool _eos  = false)
  {
    
    assert(_spk.format != FORMAT_LINEAR);

    spk = _spk;
    rawdata = _rawdata;
    samples.zero();
    size = _size;
    sync = _sync;
    time = _time;
    eos = _eos;
  }

  inline void set(Speakers _spk, uint8_t *_rawdata, samples_t _samples, size_t _size,
    bool _sync = false, vtime_t _time = 0, bool _eos  = false)
  {
    
    assert((_spk.format != FORMAT_LINEAR) || (_rawdata == 0));

    spk = _spk;
    rawdata = _rawdata;
    samples = _samples;
    size = _size;
    sync = _sync;
    time = _time;
    eos = _eos;
  }

  inline void set_sync(bool _sync, vtime_t _time)
  {
    sync = _sync;
    time = _time;
  }

  inline void set_eos(bool _eos = true)
  {
    eos = _eos;
  }

  inline bool is_dummy() const
  {
    return spk.format == FORMAT_UNKNOWN;
  }

  inline bool is_empty() const
  { 
    return size == 0; 
  }

  inline void drop(size_t _size)
  {
    if (_size > size)
      _size = size;

    if (spk.format == FORMAT_LINEAR)
      samples += _size;
    else
      rawdata += _size;

    size -= _size;
    sync = false;
  };
};















































































class Sink
{
public:
  virtual ~Sink() {};

  virtual bool query_input(Speakers spk) const = 0;
  virtual bool set_input(Speakers spk) = 0;
  virtual Speakers get_input() const = 0;
  virtual bool process(const Chunk *chunk) = 0;
};

class NullSink : public Sink
{
protected:
  Speakers spk;

public:
  NullSink() {};

  virtual bool query_input(Speakers _spk) const { return true; }
  virtual bool set_input(Speakers _spk)         { spk = _spk; return true; }
  virtual Speakers get_input() const            { return spk;  }
  virtual bool process(const Chunk *_chunk)     { spk = _chunk->spk; return true; }
};




























class Source
{
public:
  virtual ~Source() {};

  virtual Speakers get_output() const = 0;
  virtual bool is_empty() const = 0;
  virtual bool get_chunk(Chunk *chunk) = 0;
};


























        
class Filter: public Sink, public Source
{
public:
  virtual ~Filter() {};

  virtual void reset() = 0;
  virtual bool is_ofdd() const = 0;

  virtual bool query_input(Speakers spk) const = 0;
  virtual bool set_input(Speakers spk) = 0;
  virtual Speakers get_input() const = 0;
  virtual bool process(const Chunk *chunk) = 0;

  virtual Speakers get_output() const = 0;
  virtual bool is_empty() const = 0;
  virtual bool get_chunk(Chunk *chunk) = 0;

  inline bool process_to(const Chunk *_chunk, Sink *_sink)
  {
    Chunk chunk;

    while (!is_empty())
    {
      FILTER_SAFE(get_chunk(&chunk));
      FILTER_SAFE(_sink->process(&chunk));
    }

    FILTER_SAFE(process(_chunk));

    while (!is_empty())
    {
      FILTER_SAFE(get_chunk(&chunk));
      FILTER_SAFE(_sink->process(&chunk));
    }

    return true;
  }

  inline bool get_from(Chunk *_chunk, Source *_source)
  {
    Chunk chunk;

    while (is_empty())
    {
      FILTER_SAFE(_source->get_chunk(&chunk));
      FILTER_SAFE(process(&chunk));
    }

    return get_chunk(_chunk);
  }

  inline bool transform(Source *_source, Sink *_sink)
  {
    Chunk chunk;

    while (is_empty())
    {
      FILTER_SAFE(_source->get_chunk(&chunk));
      FILTER_SAFE(process(&chunk));
    }

    FILTER_SAFE(get_chunk(&chunk));
    return _sink->process(&chunk);
  }
};






















class NullFilter : public Filter
{
protected:
  Speakers  spk;

  bool      sync;
  vtime_t   time;
  bool      flushing;

  uint8_t  *rawdata;
  samples_t samples;
  size_t    size;

  int       format_mask;

  virtual void on_reset() {};
  virtual bool on_process() { return true; };
  virtual bool on_set_input(Speakers _spk) { return true; };

  inline bool receive_chunk(const Chunk *_chunk)
  {
    if (!_chunk->is_dummy())
    {
      
      if (spk != _chunk->spk)
        FILTER_SAFE(set_input(_chunk->spk));

      
      if (_chunk->sync) 
      {
        sync     = true;
        time     = _chunk->time;
      }
      flushing = _chunk->eos;
      rawdata  = _chunk->rawdata;
      samples  = _chunk->samples;
      size     = _chunk->size;
    }

    return true;
  }

  inline void send_chunk_inplace(Chunk *_chunk, size_t _size)
  {
    

    if (_size > size)
      _size = size;

    _chunk->set
    (
      get_output(), 
      rawdata, samples, _size,
      sync, time,
      flushing && (size == _size)
    );

    if (spk.format == FORMAT_LINEAR)
      samples += _size;
    else
      rawdata += _size;

    size -= _size;
    sync = false;
    flushing = flushing && size;
  }

  inline void drop_rawdata(size_t _size)
  {
    if (_size > size)
      _size = size;

    rawdata += _size;
    size -= _size;
  }

  inline void drop_samples(size_t _size)
  {
    if (_size > size)
      _size = size;

    samples += _size;
    size    -= _size;
  }

public:
  NullFilter(int _format_mask) 
  {
    spk  = spk_unknown;
    size = 0;
    time = 0;
    sync = false;
    flushing = false;
    format_mask = _format_mask;
  }

  virtual void reset()
  {
    size = 0;
    time = 0;
    sync = false;
    flushing = false;
    on_reset();
  }
  
  virtual bool is_ofdd() const
  {
    return false;
  }

  virtual bool query_input(Speakers _spk) const
  { 
    
    if (_spk.format == FORMAT_LINEAR)
      return (FORMAT_MASK_LINEAR & format_mask) != 0 && _spk.mask != 0 && _spk.sample_rate != 0;
    else
      return (FORMAT_MASK(_spk.format) & format_mask) != 0;
  }

  virtual bool set_input(Speakers _spk)
  {
    reset();                
    if (!query_input(_spk)) 
      return false;

    FILTER_SAFE(on_set_input(_spk));

    spk = _spk;
    return true;
  }

  virtual Speakers get_input() const
  {
    return spk;
  }

  virtual bool process(const Chunk *_chunk)
  {
    
    if (!_chunk->is_dummy())
    {
      FILTER_SAFE(receive_chunk(_chunk));
      FILTER_SAFE(on_process());
    }
    return true;
  }

  virtual Speakers get_output() const
  {
    return spk;
  }

  virtual bool is_empty() const
  {
    
    return !size && !flushing;
  };

  virtual bool get_chunk(Chunk *_chunk)
  {
    send_chunk_inplace(_chunk, size);
    return true;
  };
};







class SourceFilter: public Source
{
protected:
  Source *source;
  Filter *filter;

public:
  SourceFilter(): source(0), filter(0)
  {}

  SourceFilter(Source *_source, Filter *_filter): source(0), filter(0)
  {
    set(_source, _filter);
  }

  bool set(Source *_source, Filter *_filter)
  {
    if (_source == 0) return false;
    source = _source;
    filter = _filter;
    return true;
  }

  void release()
  {
    source = 0;
    filter = 0;
  }

  Source *get_source() const { return source; }
  Filter *get_filter() const { return filter; }

  
  

  Speakers get_output() const
  {
    if (filter) return filter->get_output();
    if (source) return source->get_output();
    return spk_unknown;
  }

  bool is_empty() const
  {
    if (filter) return filter->is_empty() && source->is_empty();
    if (source) return source->is_empty();
    return true;
  }

  bool get_chunk(Chunk *chunk)
  {
    if (filter) return filter->get_from(chunk, source);
    if (source) return source->get_chunk(chunk);
    return false;
  }
};






class SinkFilter : public Sink
{
protected:
  Sink   *sink;
  Filter *filter;

public:
  SinkFilter(): sink(0), filter(0)
  {}

  SinkFilter(Sink *_sink, Filter *_filter): sink(0), filter(0)
  {
    set(_sink, _filter);
  }

  bool set(Sink *_sink, Filter *_filter)
  {
    if (_sink == 0) return false;
    sink = _sink;
    filter = _filter;
    return true;
  }

  void release()
  {
    sink = 0;
    filter = 0;
  }

  Sink *get_sink() const { return sink; }
  Filter *get_filter() const { return filter; }

  
  

  bool query_input(Speakers spk) const
  {
    if (filter) return filter->query_input(spk);
    if (sink) return sink->query_input(spk);
    return false;
  }

  bool set_input(Speakers spk)
  {
    if (filter) return filter->set_input(spk);
    if (sink) return sink->set_input(spk);
    return false;
  }

  Speakers get_input() const
  {
    if (filter) return filter->get_input();
    if (sink) return sink->get_input();
    return spk_unknown;
  }

  bool process(const Chunk *chunk)
  {
    if (filter) return filter->process_to(chunk, sink);
    if (sink) return sink->process(chunk);
    return false;
  }

};

#endif
