/*!
  \file
  \brief パケットを保持する

  FIFO として動作する。

  \author Satofumi KAMIMURA

  $Id: PacketBuffer.cpp 810 2009-05-07 00:51:59Z satofumi $
*/

#include "PacketBuffer.h"
#include <deque>

using namespace qrk;
using namespace std;

namespace
{
  typedef deque<size_t> Index;
  typedef map<ClientId, Index> Indexes;
}


struct PacketBuffer::pImpl
{
  deque<Packet> packets_;
  Indexes indexes_;
  size_t last_index_;


  pImpl(void) : last_index_(0)
  {
  }


  void addIndex(ClientId id)
  {
    indexes_[id].push_back(last_index_);
  }


  void push(const Packet& packet, ClientId id)
  {
    packets_.push_back(packet);
    addIndex(id);
    ++last_index_;
  }


  void push(const Packet& packet, const ClientGroup& group)
  {
    packets_.push_back(packet);

    for (ClientGroup::iterator it = group.begin(); it != group.end(); ++it) {
      addIndex(*it);
    }
    ++last_index_;
  }


  Packet pop(ClientId id)
  {
    Index& client_index = indexes_[id];
    size_t index = client_index.front();
    client_index.pop_front();

    // パケットを取り除いてよいかを評価する
    // !!! 全クライアントにおいて、最も小さい index を保持しておく

    return packets_[index];
  }
};


PacketBuffer::PacketBuffer(void) : pimpl(new pImpl)
{
}


PacketBuffer::~PacketBuffer(void)
{
}


void PacketBuffer::clear(ClientId id)
{
  pimpl->indexes_[id].clear();

  // !!! front() の削除を試す
}


bool PacketBuffer::empty(ClientId id) const
{
  return pimpl->indexes_[id].empty();
}


void PacketBuffer::push(const Packet& packet, ClientId id)
{
  pimpl->push(packet, id);
}


void PacketBuffer::push(const Packet& packet, const ClientGroup& group)
{
  pimpl->push(packet, group);
}


Packet PacketBuffer::pop(ClientId id)
{
  if (empty(id)) {
    Packet empty_packet;
    return empty_packet;

  } else {
    return pimpl->pop(id);
  }
}
