#include "datasourceread.hh"


using namespace Gecode;


DataSourceRead::DataSourceRead (Space& home, Int::IntView offsetV, Int::IntView resultV, GetDataSource callback) : 
  Propagator (home),
  m_callback (callback),
  ds (m_callback (home)),
  offsetV (offsetV),
  resultV (resultV),
  unavailableV (ds.unavailable ())
{
  offsetV.subscribe (home, *this, Int::PC_INT_VAL);
  unavailableV.subscribe (home, *this, Int::PC_INT_DOM);
}


DataSourceRead::DataSourceRead (Space& home, bool share, DataSourceRead& p) : 
  Propagator (home, share, p),
  m_callback (p.m_callback),
  ds (m_callback (home))
{
  offsetV.update (home, share, p.offsetV);
  resultV.update (home, share, p.resultV);
  unavailableV.update (home, share, p.unavailableV);
}


Propagator* DataSourceRead::copy (Space& home, bool share)
{
  return new (home) DataSourceRead (home, share, *this);
}


size_t DataSourceRead::dispose (Space& home) 
{
  offsetV.cancel (home, *this, Int::PC_INT_VAL);
  unavailableV.cancel (home, *this, Int::PC_INT_DOM);
  (void) Propagator::dispose (home);
  return sizeof (*this);
}


PropCost DataSourceRead::cost (const Space& home, const ModEventDelta& med) const 
{
  // TODO: need to update this!
  return PropCost::binary (PropCost::HI);
}


ExecStatus DataSourceRead::propagate (Space& home, const Gecode::ModEventDelta& med) 
{
  if (offsetV.assigned ()) {
    int offset = offsetV.val ();

    // Check whether (offset is NOT IN the ds.unavailable ())
    if (ds.unavailable ().in (offset)) {
      // offset is in the current unavailableain.  Postpone.
      return ES_NOFIX;
    } else {
      // offset is not in the current unavailableain.  Continue.
      int result = 0xff & ds.read (offset);
      if (resultV.eq (home, result) == Int::ME_INT_FAILED) {
        // std::cout << "ES_FAILED" << std::endl;
        return ES_FAILED; 
      } else {
        // std::cout << "ES_SUBSUMED" << std::endl;
        return home.ES_SUBSUMED (*this);
      }
    }
  } else {
    // std::cout << "ES_NOFIX" << std::endl;
    return ES_NOFIX;
  }
  GECODE_NEVER;
  // std::cout << "DROPPED OUT" << std::endl;
}


ExecStatus DataSourceRead::post (Space& home, Int::IntView offsetV, Int::IntView resultV, GetDataSource callback)
{
  (void) new (home) DataSourceRead (home, offsetV, resultV, callback);
  return ES_OK;
}


void data_source_read (Space& home, IntVar offset, IntVar result, GetDataSource callback)
{
  Int::IntView offsetV (offset);
  Int::IntView resultV (result);
  if (DataSourceRead::post (home, offsetV, resultV, callback) != ES_OK) {
    home.fail ();
  }
}
