// -*- c++ -*-
//=============================================================================
// Copyright (C) 2011 by Denys Duchier
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
// 
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
// more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//=============================================================================
#include "disjunctor.hh"

namespace Gecode { namespace Disjunctor_ {

    //=========================================================================
    // Disjunctor (following example in MPG)
    //=========================================================================

    struct DisjunctorObject: public LocalObject
    {
      int n;
      DisjunctorObject(Space& home) : LocalObject(home), n(0) {}
      DisjunctorObject(Space& home, bool share, DisjunctorObject& d)
	: LocalObject(home, share, d), n(d.n) {}
      virtual LocalObject* copy(Space& home, bool share)
      { return new (home) DisjunctorObject(home, share, *this); }
      virtual size_t dispose(Space&) { return sizeof(*this); }
    };

    Disjunctor::Disjunctor(): LocalHandle() {}
    Disjunctor::Disjunctor(Space& home)
      : LocalHandle(new (home) DisjunctorObject(home)) {}
    Disjunctor::Disjunctor(const Disjunctor& d): LocalHandle(d) {}
    int Disjunctor::get() const { return static_cast<DisjunctorObject*>(object())->n; }
    void Disjunctor::incr() { static_cast<DisjunctorObject*>(object())->n += 1; }
    void Disjunctor::decr() { static_cast<DisjunctorObject*>(object())->n -= 1; }
    void Disjunctor::dispose(Space& home)
    { static_cast<DisjunctorObject*>(object())->dispose(home); }

    //=========================================================================
    // Forwarder<View>
    //     used to forward the domain of a view V1 into a view V2 located in a
    // subspace (the subspace of a speculative clause).  V2 represents V1 in
    // that space.
    //
    // BasicForwarder
    //     base class of all Forwarders.  It declares the virtual functions
    // that must be implemented to do the actual work, and contains the pointer
    // implementing the singly-linked list of Forwarders associated with each
    // clause.  It also redefines new and delete to manage memory in the
    // subspace.
    //=========================================================================

    struct BasicForwarder
    {
      BasicForwarder* next;

      BasicForwarder() : next(NULL) {}

      // forward V1's domain into V2.  This is used each time the Clause (as a
      // a propagator) is woken to perform propagation.

      virtual ExecStatus forward_in(Space& inner_home) = 0;

      // forward V2's domain into V1.  This is used after propagation in the
      // case where the Clause has been committed to (because it is the last
      // one remaining).

      virtual void forward_out(Space& outer_home) = 0;

      // needed by delete to know how much to free
      virtual size_t size() const = 0;

      // usage: new (home) FRWRDR(...);
      // to allocate the instance of FRWRDR on home's heap

      static void* operator new(size_t n, Space& home)
      { return home.ralloc(n); }

      // usage: delete (home) PTR;
      // to free the instance pointed to by PTR from home's heap

      static void  operator delete(void* ptr, Space& home)
      { home.rfree(ptr, ((BasicForwarder*)ptr)->size()); }

      virtual BasicForwarder*
      copy(Space& outer_home, Space& inner_home, bool share) = 0;

      virtual void cancel(Space& home, Clause&) = 0;
    };

    // Forwarder parametrized by View

    template <typename View>
    struct Forwarder: public BasicForwarder
    {
      View outer;
      View inner;
      Forwarder(View v1, View v2): outer(v1), inner(v2) {}
      Forwarder() {}
      virtual ExecStatus forward_in(Space& home);
      virtual void forward_out(Space& home);
      virtual size_t size() const { return sizeof(*this); }
      virtual BasicForwarder* copy(Space& outer_home, Space& inner_home, bool share)
      {
	Forwarder<View>* fwd = new (inner_home) Forwarder<View>();
	fwd->outer.update(outer_home, share, outer);
	fwd->inner.update(inner_home, share, inner);
	return fwd;
      }
      virtual void cancel(Space& home, Clause& c);
    };

    // instances of Forwarder that we actually use

    typedef Forwarder<Int::IntView> IntForwarder;
    typedef Forwarder<Int::BoolView> BoolForwarder;
    typedef Forwarder<Set::SetView> SetForwarder;

    //=========================================================================
    // IntForwarder
    //=========================================================================

    template <>
    ExecStatus IntForwarder::forward_in(Space& home) // inner home
    {
      Int::ViewRanges<Int::IntView> r(outer);
      GECODE_ME_CHECK(inner.inter_r(home,r));
      return ES_OK;
    }

    template <>
    void IntForwarder::forward_out(Space& home) // outer home
    {
      // it is guaranteed by design that after successful propagation the
      // domain of the inner variable is a non-empty narrowing of the domain of
      // the outer variable.  therefore, we can simply replace the domain of
      // the outer variable with the domain of the inner variable.
      Int::ViewRanges<Int::IntView> r(inner);
      outer.narrow_r(home, r, false);
    }

    template <>
    void IntForwarder::cancel(Space& home, Clause& c)
    { outer.cancel(home, c, Int::PC_INT_DOM); }

    //=========================================================================
    // BoolForwarder
    //=========================================================================

    template <>
    ExecStatus BoolForwarder::forward_in(Space& home)
    {
      Int::ViewRanges<Int::BoolView> r(outer);
      GECODE_ME_CHECK(inner.inter_r(home,r));
      return ES_OK;
    }

    template <>
    void BoolForwarder::forward_out(Space& home)
    {
      Int::ViewRanges<Int::BoolView> r(inner);
      outer.narrow_r(home, r, false);
    }

    template <>
    void BoolForwarder::cancel(Space& home, Clause& c)
    { outer.cancel(home, c, Int::PC_INT_DOM); }

    //=========================================================================
    // SetForwarder
    //=========================================================================

    template <>
    ExecStatus SetForwarder::forward_in(Space& home)
    {
      Set::GlbRanges<SetView> gr(outer);
      GECODE_ME_CHECK(inner.includeI(home,gr));
      Set::LubRanges<SetView> lr(outer);
      GECODE_ME_CHECK(inner.intersectI(home,lr));
      GECODE_ME_CHECK(inner.cardMin(home, outer.cardMin()));
      GECODE_ME_CHECK(inner.cardMax(home, outer.cardMax()));
      return ES_OK;
    }

    template <>
    void SetForwarder::forward_out(Space& home)
    {
      Set::GlbRanges<SetView> gr(inner);
      outer.includeI(home, gr);
      Set::LubRanges<SetView> lr(inner);
      outer.intersectI(home, lr);
      outer.cardMin(home, inner.cardMin());
      outer.cardMax(home, inner.cardMax());
    }

    template <>
    void SetForwarder::cancel(Space& home, Clause& c)
    { outer.cancel(home, c, Set::PC_SET_ANY); }

    //=========================================================================
    // SubSpace
    //=========================================================================

    void SubSpace::forward(IntView outer, IntView inner)
    {
      BasicForwarder* fwd = new (*this) IntForwarder(outer, inner);
      fwd->next = forwarder;
      forwarder = fwd;
    }

    void SubSpace::forward(BoolView outer, BoolView inner)
    {
      BasicForwarder* fwd = new (*this) BoolForwarder(outer, inner);
      fwd->next = forwarder;
      forwarder = fwd;
    }

    void SubSpace::forward(SetView outer, SetView inner)
    {
      BasicForwarder* fwd = new (*this) SetForwarder(outer, inner);
      fwd->next = forwarder;
      forwarder = fwd;
    }

    ExecStatus SubSpace::forward_in()
    {
      for (BasicForwarder* p=forwarder; p!=NULL; p=p->next)
	GECODE_ES_CHECK(p->forward_in(*this));
      return ES_OK;
    }

    ExecStatus SubSpace::forward_out(Space& home) // outer home
    {
      for (BasicForwarder* p=forwarder; p!=NULL; p=p->next)
	p->forward_out(home);
      return ES_OK;
    }

    SubSpace::SubSpace(): GenericSpace(), forwarder(NULL) {}

    SubSpace::SubSpace(bool share, SubSpace& s)
      : GenericSpace(share, s), forwarder(NULL)
    {
      BasicForwarder** prev = &forwarder;
      for (BasicForwarder* p=s.forwarder; p!=NULL; p=p->next)
	{
	  BasicForwarder* fwd = p->copy(*s.homeDuringCloning, *this, share);
	  *prev = fwd;
	  prev = &fwd->next;
	}
    }

    Space* SubSpace::copy(bool share)
    { return new SubSpace(share,*this); }

    SubSpace* SubSpace::copy(Space& home, bool share)
    { homeDuringCloning = &home; return static_cast<SubSpace*>(clone(share)); }

    void SubSpace::cancel(Space& home, Clause& c)
    {
      for (BasicForwarder*p = forwarder; p!=NULL; p=p->next)
	p->cancel(home, c);
    }

    //=========================================================================
    // Clause
    //=========================================================================

    Clause::operator GenericSpace&() { return *subhome; }
    Clause::operator Space&() { return *subhome; }
    Clause::operator Home() { return *subhome; }

    GenericSpace* Clause::generic_space() const { return subhome; }
    Space* Clause::space() const { return subhome; }

    Clause::Clause(Space& home_, Disjunctor disj_)
      : Propagator(home_), disj(disj_), subhome(new SubSpace())
    {
      disj.incr();
    }

    Clause::Clause(Space& home, bool share, Clause& c)
      : Propagator(home, share, c), subhome(c.subhome->copy(home, share))
    {
      disj.update(home, share, c.disj);
    }

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

    void Clause::forward(Space& home, IntVar outer, IntVar inner)
    {
      IntView oview(outer);
      IntView iview(inner);
      subhome->forward(oview, iview);
      oview.subscribe(home, *this, Int::PC_INT_DOM);
    }

    void Clause::forward(Space& home, BoolVar outer, BoolVar inner)
    {
      BoolView oview(outer);
      BoolView iview(inner);
      subhome->forward(oview, iview);
      oview.subscribe(home, *this, Int::PC_INT_DOM);
    }

    void Clause::forward(Space& home, SetVar outer, SetVar inner)
    {
      SetView oview(outer);
      SetView iview(inner);
      subhome->forward(oview, iview);
      oview.subscribe(home, *this, Set::PC_SET_ANY);
    }

    ExecStatus Clause::forward_in()
    { return subhome->forward_in(); }

    ExecStatus Clause::forward_out(Space& home) // outer home
    { return subhome->forward_out(home); }

    inline bool Clause::committed() const { return disj.get() == 1; }

    ExecStatus Clause::propagate(Space& home, const ModEventDelta&)
    {
      if (forward_in()      == ES_FAILED ||
	  subhome->status() == SS_FAILED   )
	{
	  if (committed()) return ES_FAILED;
	  disj.decr();
	  return home.ES_SUBSUMED(*this);
	}
      if (committed()) forward_out(home);
      // maybe do something clever about subsumption later
      return ES_FIX;
    }

    size_t Clause::dispose(Space& home)
    {
      subhome->cancel(home, *this);
      delete subhome;
      // the memory for the disj's LocalObject is not recovered
      (void) Propagator::dispose(home);
      return sizeof(*this);
    }

    PropCost Clause::cost(const Space&, const ModEventDelta&) const
    {
      // consider a clause to be crazy expensive
      return PropCost::crazy(PropCost::HI,10);
    }
}

  //===========================================================================
  // Clause (user API)
  //===========================================================================

  Clause::Clause(generic_gecode::GenericSpace& home, Disjunctor disj)
    : _home(home), _clause(new (home) Disjunctor_::Clause(home, disj)) {}
  Clause::operator generic_gecode::GenericSpace&() { return *_clause; }
  Clause::operator Space&() { return *_clause; }
  Clause::operator Home() { return *_clause; }
  generic_gecode::GenericSpace* Clause::generic_space() { return _clause->generic_space(); }
  generic_gecode::GenericSpace* Clause::generic_parent() { return &_home; }
  Space* Clause::space() { return _clause->space(); }
  void Clause::forward(IntVar outer, IntVar inner)
  { _clause->forward(_home, outer, inner); }
  void Clause::forward(BoolVar outer, BoolVar inner)
  { _clause->forward(_home, outer, inner); }
  void Clause::forward(SetVar outer, SetVar inner)
  { _clause->forward(_home, outer, inner); }

}
