// objects.cc -
//
// ##Copyright##
// 
// Copyright 2000-2016 Peter Robinson  (pjr@itee.uq.edu.au)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.00 
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ##Copyright##
//
// $Id: objects.cc,v 1.6 2006/01/31 23:17:51 qp Exp $

#include "objects.h"

using namespace std;

// The next best thing to generic dispatching

size_t Object::size_dispatch(void)
{
  // if (this == NULL)
  //   {
  //     cerr << "Warning - size of NULL" << endl;
  //     return(0);
  //   }
  switch (tTag())
    {
    case tVar:
      return OBJECT_CAST(Variable *, this)->size();
      break;
    case tObjVar:
      return OBJECT_CAST(ObjectVariable *, this)->size();
      break;
    case tStruct:
      return OBJECT_CAST(Structure *, this)->size();
      break;
    case tCons:
      return Cons::size();
      break;
    case tQuant:
      return QuantifiedTerm::size();
      break;
    case tShort:
      return Short::size();
      break;
    case tLong:
      return Long::size();
      break;
    case tDouble:
      return Double::size();
      break;
    case tAtom:
      return Atom::size();
      break;
    case tString:
      return OBJECT_CAST(StringObject*, this)->size();
      break;
    case tSubst:
      return Substitution::size();
      break;
    case tSubBlock:
      return OBJECT_CAST(SubstitutionBlock *, this)->size();
      break;
    default:
      // Not all tTags considered!
      //assert(false);
      return 99999;
    }
}

//
// Check whether the object variable is in the distinctness information list.
// NOTE:
//	ObjectVariable should be dereferenced before this function is called.
//
bool
Object::isObjectVariableInDistinctList(const ObjectVariable *objvar) const
{
  assert(this->isObjectVariable());
  assert(OBJECT_CAST(const Reference*, this)->getReference() == 
	 OBJECT_CAST(const Object*, this));

  for (Object *distinct = objvar->getDistinctness(); 
       distinct->isCons(); 
       distinct = OBJECT_CAST(Cons *, distinct)->getTail())
    {
      Object *term = 
	OBJECT_CAST(Cons *, distinct)->getHead()->variableDereference();
      if (term == this)
	{
	  return true;
	}
    }

  return false;
}

//
// Check whether two object variables are distinct. 
// Return true if object variables are known to be distinct.
// NOTE:
//	ObjectVariable1 and ObjectVariable2 should be dereferenced 
//	before this function is called.
//
bool
Object::distinctFrom(const Object *objvar) const 
{
  assert(this->isObjectVariable());
  assert(OBJECT_CAST(const Reference*, this)->getReference() == this);
  assert(objvar->isObjectVariable());
  assert(OBJECT_CAST(const Reference*, objvar)->getReference() == objvar);

  if (this == objvar)
    {
      return false;
    }
  else if (this->isLocalObjectVariable() || 
	   objvar->isLocalObjectVariable())
    {
      return true;
    }
  else
    {
      return isObjectVariableInDistinctList(OBJECT_CAST(const ObjectVariable*, 
							objvar));
    }
}

//
// The function returns true if the object_variable is distinct from
// all domains in the i-th to SubSize-th entry of the substitution.
// 
bool
Object::distinctFromDomains(const SubstitutionBlock *sub_block, size_t i)
{
  assert(this->isObjectVariable());
  assert(this == this->variableDereference());

  for ( ; i <= sub_block->getSize(); i++)
    {
      assert(sub_block->getDomain(i)->variableDereference()->isObjectVariable());

      ObjectVariable *domain 
	= OBJECT_CAST(ObjectVariable*, 
		      sub_block->getDomain(i)->variableDereference());

      if (! distinctFrom(domain))
	{
	  return false;
	}
    }

  return true;
}

//
// Return the length of the bound variables list.
//
size_t
Object::boundListLength(void)
{
  Object* list = this->variableDereference();
  
  size_t length = 0;
  
  while (list->isCons())
    {
      length++;
      
      list = OBJECT_CAST(Cons *, list)->getTail()->variableDereference();
      
    }
  
  return list->isNil() ? length : UINT_MAX;
}



//
// Check whether one of the domains contains the given object variable.
//
bool
ObjectVariable::isObjectVariableInDomain(SubstitutionBlock *sub_block)
{
  for (size_t i = sub_block->getSize(); i > 0; i--)
    {
      assert(sub_block->getDomain(i)->isObjectVariable());
      
      ObjectVariable *dom = 
	OBJECT_CAST(ObjectVariable*, sub_block->getDomain(i)->variableDereference());
      
      if (this == dom)
	{
	  return true;
	}
    }
  
  return false;
}


//
// The substitution is scanned from right to left for the local object 
// variable on a domain or range place. If the last occurrence of the 
// local object variable is as a range element, then save the corresponding
// domain element and the new end of the substitution which 
// is one where local object variable is found.
//
bool
Object::containLocalObjectVariable(Object* sub, Object*& domElem, 
				   Object*& newEnd)
{
  assert(this->isLocalObjectVariable());
  assert(sub->isNil() || 
	 (sub->isCons() && 
	  OBJECT_CAST(Cons *, sub)->isSubstitutionBlockList()));
  
  bool    found = false;
  
  Object* end = AtomTable::nil;
  
  for (Object* s = sub; 
       s->isCons(); 
       s = OBJECT_CAST(Cons *, s)->getTail())
    {
      assert(OBJECT_CAST(Cons*, s)->getHead()->isSubstitutionBlock());
      SubstitutionBlock *subblock = 
	OBJECT_CAST(SubstitutionBlock *, OBJECT_CAST(Cons *, s)->getHead());
      if (subblock->containsLocal())
	{
	  for (size_t i = 1; i <= subblock->getSize(); i++)
	    {
	      if (subblock->getDomain(i) == this)
		{
		  found = false;
		}
	      else if (subblock->getRange(i) == this)
		{
		  found = true;
		  end = s;
		  domElem = subblock->getDomain(i);
		}
	    }
	}
    }
  if (! found)
    {
      return(false);
    }
  else
    {
      newEnd = end;
    }
  return(true);
}

#ifdef QP_DEBUG
bool
Object::check_object()
{
  if (isRefTag()) return true;
  if ((tag & 1) == 0) return false;
  switch (tTag())
    {
    case Object::tVar:
      {
	Object* n  = OBJECT_CAST(Reference*, this)->getReference();
	if (n == this) return true;
	return ((n != NULL) && n->check_object());
      }
      break;
    case Object::tObjVar:
      {
	Object* n = OBJECT_CAST(Reference*, this)->getReference();
	if (n == this) return true;
	return ((n != NULL) && n->check_object());
      }
      break;
    case Object::tStruct:
      {
	Structure* s = OBJECT_CAST(Structure *, this);
	for (u_int i = 0; i <= s->getArity(); i++)
	  {
	    if (s->getArgument(i) == NULL || !s->getArgument(i)->check_object()) return false;
	  }
	return true;
      }
      break;
    case Object::tCons:
      {
	Cons* l = OBJECT_CAST(Cons *, this);
	return ((l->getHead() != NULL) && l->getHead()->check_object() && 
		(l->getTail() != NULL) && l->getTail()->check_object());
      }
      break;
    case Object::tQuant:
      {
	QuantifiedTerm* q = OBJECT_CAST(QuantifiedTerm *, this);
	return ((q->getQuantifier() != NULL) && 
		q->getQuantifier()->check_object() && 
		(q->getBoundVars() != NULL) &&
		q->getBoundVars()->check_object() && 
		(q->getBody() != NULL) &&
		q->getBody()->check_object());
      }
      break;
    case Object::tShort:
    case Object::tLong:
    case Object::tDouble:
    case Object::tAtom:
    case Object::tString:
      return true;
      break;
    case Object::tSubst:
      {
	Substitution* s = OBJECT_CAST(Substitution *, this);
	return((s->getTerm() != NULL) &&
	       s->getTerm()->check_object() && 
	       (s->getSubstitutionBlockList() != NULL) &&
	       s->getSubstitutionBlockList()->check_object());
      }
      break;
    case Object::tSubBlock:
      {
	SubstitutionBlock * s = OBJECT_CAST(SubstitutionBlock *, this);
	for (u_int i = 1; i <= s->getSize(); i++)
	  {
	    if ((s->getDomain(i) == NULL) ||
		!s->getDomain(i)->check_object() || 
		(s->getRange(i) == NULL) ||
		!s->getRange(i)->check_object() )return false;
	  }
	return true;
      }
      break;
    default:
      return false;
    }
}
#endif






