#include "filter_graph.h"
#include "stdio.h"  
#include "string.h" 

#ifdef _MSC_VER
#define snprintf _snprintf
#endif

inline static bool is_invalid(int node)
{
  return (node != node_end) && (node == node_err || node < 0 || node > graph_nodes);
}


FilterGraph::FilterGraph(int _format_mask)
:start(_format_mask), end(-1)
{
  filter[node_start] = &start;
  filter[node_end]   = &end;
  drop_chain();
};

FilterGraph::~FilterGraph()
{};












bool
FilterGraph::build_chain(int node)
{
  Speakers spk;
  while (node != node_end)
  {
    spk = filter[node]->get_output();
    FILTER_SAFE(add_node(node, spk));
    node = next[node];
  }

  return true;
}








void
FilterGraph::drop_chain()
{
  ofdd = false;

  start.reset();
  end.reset();
  next[node_start] = node_end;
  prev[node_start] = node_end;
  next[node_end] = node_start;
  prev[node_end] = node_start;
  node_state[node_start] = ns_dirty;
  node_state[node_end] = ns_ok;
}
















void
FilterGraph::set_dirty(int node)
{
  if (node == node_end || node < 0 || node > graph_nodes)
    return;

  if (node_state[node] == ns_ok)
    node_state[node] = ns_dirty;
}

void 
FilterGraph::invalidate_chain()
{
  int node = node_start;
  while (node != node_end)
  {
    if (node_state[node] == ns_ok)
      node_state[node] = ns_dirty;
    node = next[node];
  }
}














void 
FilterGraph::rebuild_node(int node)
{
  if (node == node_start || node < 0 || node > graph_nodes)
    return;

  node_state[node] = ns_flush;
}











bool
FilterGraph::add_node(int node, Speakers spk)
{
  
  
  
  

  if (spk == spk_unknown)
  {
    ofdd = true;

    end.set_input(spk);
    next[node] = node_end;
    prev[node_end] = node;
    return true;
  }

  
  

  int next_node = get_next(node, spk);

  
  
  
  

  if (is_invalid(next_node))
    return false;

  
  
  
  

  if (next_node == node_end)
  {
    end.set_input(spk);
    next[node] = node_end;
    prev[node_end] = node;
    return true;
  }

  
  

  

  filter[next_node] = init_filter(next_node, spk);

  
  
  

  if (!filter[next_node])
    return false;

  
  
  

  FILTER_SAFE(filter[next_node]->set_input(spk));

  
  next[node] = next_node;
  prev[next_node] = node;
  next[next_node] = node_end;
  prev[node_end] = next_node;
  node_state[next_node] = ns_ok;

  
  
  

  ofdd = false;
  node = next[node_start];
  while (node != node_end)
  {
    ofdd |= filter[node]->is_ofdd();
    node = next[node];
  }

  return true;
}





bool
FilterGraph::process_internal(bool rebuild)
{
  Speakers spk;
  Chunk chunk;

  int node = prev[node_end];
  while (node != node_end)
  {
    
    

    if (filter[node]->is_empty())
    {
      
      node = prev[node];
      continue;
    }

    
    
    

    spk = filter[node]->get_output();

    
    

    if (spk != filter[next[node]]->get_input())
    {
      
      
      
      
      
      
      FILTER_SAFE(build_chain(node));
    }
    else if (node_state[next[node]] == ns_rebuild)
    {
      
      
      FILTER_SAFE(build_chain(node));
    }
    else if (rebuild && node_state[node] == ns_dirty)
    {
      
      
      
      
      
      
      
      
      if (next[node] != get_next(node, spk))
        node_state[next[node]] = ns_flush;
      else
        node_state[node] = ns_ok;
    }

    
    

    if (node_state[next[node]] == ns_flush)
    {
      
      chunk.set_empty(spk, false, 0, true);
      FILTER_SAFE(filter[next[node]]->process(&chunk));
      node_state[next[node]] = ns_rebuild;
    }
    else
    {
      
      FILTER_SAFE(filter[node]->get_chunk(&chunk));
      FILTER_SAFE(filter[next[node]]->process(&chunk));
    }
    node = next[node];
  }

  return true;
}









int
FilterGraph::chain_text(char *buf, size_t buf_size) const
{
  size_t i;
  Speakers spk;

  char *buf_ptr = buf;
  int node = next[node_start];

  spk = filter[node]->get_input();

  if (spk.mask || spk.sample_rate)
    i = snprintf(buf_ptr, buf_size, "(%s %s %i)", spk.format_text(), spk.mode_text(), spk.sample_rate);
  else
    i = snprintf(buf_ptr, buf_size, "(%s)", spk.format_text());

  buf_ptr += i;
  buf_size = (buf_size > i)? buf_size - i: 0;

  while (node != node_end)
  {
    spk = filter[node]->get_output();

    if (spk.mask || spk.sample_rate)
      i = snprintf(buf_ptr, buf_size, " -> %s -> (%s %s %i)", get_name(node), spk.format_text(), spk.mode_text(), spk.sample_rate);
    else
      i = snprintf(buf_ptr, buf_size, " -> %s -> (%s)", get_name(node), spk.format_text());
    buf_ptr += i;
    buf_size = (buf_size > i)? buf_size - i: 0;

    node = next[node];
  }
  return buf_ptr - buf;
}





void
FilterGraph::reset()
{
  drop_chain();
}

bool
FilterGraph::is_ofdd() const
{
  return ofdd;
}

bool
FilterGraph::query_input(Speakers _spk) const
{
  
  if (!start.query_input(_spk))
    return false;

  
  return !is_invalid(get_next(node_start, _spk));
}

bool
FilterGraph::set_input(Speakers _spk)
{
  drop_chain();
  if (!query_input(_spk))
    return false;

  FILTER_SAFE(start.set_input(_spk));
  FILTER_SAFE(build_chain(node_start));
  return true;
}

Speakers
FilterGraph::get_input() const
{
  return start.get_input();
};

bool
FilterGraph::process(const Chunk *_chunk)
{
  if (_chunk->is_dummy())
    return true;

  if (_chunk->spk != filter[next[node_start]]->get_input())
    FILTER_SAFE(set_input(_chunk->spk));

  FILTER_SAFE(start.process(_chunk))
  FILTER_SAFE(process_internal(true));
  return true;
};

Speakers
FilterGraph::get_output() const
{
  return end.get_output();
};

bool
FilterGraph::is_empty() const
{
  
  
  

  int node = node_end;
  do {
    if (!filter[node]->is_empty())
      return false;

    node = prev[node];
  } while (node != node_end);
  return true;
}

bool
FilterGraph::get_chunk(Chunk *chunk)
{
  
  
  

  if (!end.is_empty())
    return end.get_chunk(chunk);

  
  
  

  FILTER_SAFE(process_internal(false));
  if (!end.is_empty())
    return end.get_chunk(chunk);

  
  

  chunk->set_dummy();

  return true;
}
















FilterChain::FilterChain(int _format_mask)
:FilterGraph(_format_mask)
{
  chain_size = 0;
};

FilterChain::~FilterChain()
{
  drop();
};





bool
FilterChain::add_front(Filter *_filter, const char *_desc)
{
  if (chain_size >= graph_nodes)
    return false;

  for (int i = chain_size; i > 0; i--)
  {
    chain[i] = chain[i-1];
    desc[i] = desc[i-1];
  }

  chain[0] = _filter;
  desc[0] = strdup(_desc);
  chain_size++;
  return true;
}

bool
FilterChain::add_back(Filter *_filter, const char *_desc)
{
  if (chain_size >= graph_nodes)
    return false;

  chain[chain_size] = _filter;
  desc[chain_size] = strdup(_desc);
  chain_size++;
  return true;
}

void
FilterChain::drop()
{
  drop_chain();
  for (int i = 0; i < chain_size; i++)
    safe_delete(desc[i]);
  chain_size = 0;
}





const char *
FilterChain::get_name(int _node) const
{
  if (_node >= chain_size)
    return 0;

  return desc[_node];
}

Filter *
FilterChain::init_filter(int _node, Speakers _spk)
{
  if (_node >= chain_size)
    return 0;

  return chain[_node];
}

int
FilterChain::get_next(int _node, Speakers _spk) const
{
  if (_node == node_start)
    return chain_size? 0: node_end;

  if (_node >= chain_size - 1)
    return node_end;

  if (chain[_node+1]->query_input(_spk))
    return _node + 1;
  else
    return node_err;
}
