#include "blockdatasource.hh"
#include "blockreadpropagator.hh"


using namespace Gecode;


BlockDataSource::BlockDataSource (Space& home, DataSource& parent, int num_blocks, int min_block_value, int max_block_value) : 
  DataSource (home),
  m_parent (parent),
  m_unavailable (home, Int::Limits::min, Int::Limits::max),
  m_blocks (home, num_blocks, min_block_value, max_block_value),
  m_num_blocks (num_blocks),
  m_min_block_value (min_block_value),
  m_max_block_value (max_block_value)
{
  block_read (home, m_blocks, m_unavailable, parent.unavailable ());
}


BlockDataSource::BlockDataSource (bool share, Space& home, DataSource& parent, BlockDataSource& ds) : 
  DataSource (share, home, ds),
  m_parent (parent),
  m_num_blocks (ds.m_num_blocks),
  m_min_block_value (ds.m_min_block_value),
  m_max_block_value (ds.m_max_block_value)
{
  m_unavailable.update (home, share, ds.m_unavailable);
  m_blocks.update (home, share, ds.m_blocks);
}


IntVar& BlockDataSource::unavailable ()
{
  return m_unavailable;
}


IntVarArray& BlockDataSource::blocks () 
{
  return m_blocks;
}


unsigned char BlockDataSource::read (int64_t offset) const
{
  int64_t block_index = offset / BLOCK_SIZE;
  int64_t offset_in_block = offset % BLOCK_SIZE;

  int64_t block_value = m_blocks[block_index].val ();
  int64_t underlying_offset = block_value * BLOCK_SIZE + offset_in_block;
  unsigned char ch = m_parent.read (underlying_offset);
  return ch;
}


void BlockDataSource::read (int64_t block, unsigned char* buffer) const
{
  assert (block <= (int64_t) Int::Limits::max);
  if (m_unavailable.in (block)) {
    std::cout << "Erroneous read of BlockDataSource at block = " << block << std::endl;
    std::cout << "blocksDS = ";
    print (std::cout);
    std::cout << std::endl;
    assert (false);
  }

  if (m_blocks[block].assigned ()) {
    int64_t block_value = m_blocks[block].val ();
    m_parent.read (block_value, buffer);
  } else {
    std::cout << "NOT OK / NOT ASSIGNED " << block << std::endl;
    exit (1);
  }
}


void BlockDataSource::print (std::ostream& os) const 
{
  os << "BlockDataSource (";
  m_parent.print (os);
  os << ", " << m_unavailable << ", " 
     << m_num_blocks << ", " << m_min_block_value << ", " << m_max_block_value << ", [";
  bool first = true;
  for (IntVarArray::const_iterator i = m_blocks.begin (); i != m_blocks.end (); ++i) {
    if (!first) {
      os << ", "; 
    } else {
      first = false;
    }
    os << *i;
  }
  os << "])";
}
