#include "equals.h"
#include "membertype.h"
#include <memory>

using namespace aka2;


bool aka2::element_equals(const void *lhs, const void *rhs, const element_op& op) {
  if (!attributes_equals(lhs, rhs, op))
    return false;
  return op.equals(lhs, rhs);
}

bool aka2::attributes_equals(const void *lhs, const void *rhs, const element_op &op) {
  if (op.get_attribute_types() != 0) {
    const attribute_types &attrtypes = *op.get_attribute_types();
    for (attribute_types::const_iterator it = attrtypes.begin();
	 it != attrtypes.end(); ++it) {
      if (!it->second.equals(lhs, rhs))
	return false;
    }
  }
  if (op.get_anyattr_type() != 0) {
    const any_member &mtype = *op.get_anyattr_type();
    const_memberpair lhspair = mtype.get_member(lhs);
    const_memberpair rhspair = mtype.get_member(rhs);
    assert(&lhspair.op_ == &rhspair.op_);
    return lhspair.op_.equals(lhs, rhs);
  }
  return true;
}

bool aka2::sequence_equals(const void *lhs, const void *rhs, const sequence_op &sop) {
  if (!attributes_equals(lhs, rhs, sop))
    return false;

  const member_types &mtypes = sop.get_member_types();
  for (member_types::const_iterator it = mtypes.begin();
       it != mtypes.end(); ++it) {
    if (!it->equals(lhs, rhs))
      return false;
  }
  return true;
}


bool aka2::choice_equals(const void *lhs, const void *rhs, const choice_op &cop) {
  assert(cop.get_attribute_types() == 0);

  if (cop.size(lhs) != cop.size(rhs))
    return false;

  std::auto_ptr<item_iterator> lhsit(cop.get_iterator(lhs));
  std::auto_ptr<item_iterator> rhsit(cop.get_iterator(rhs));

  while (lhsit->has_next() && rhsit->has_next()) {
    const item *lhsitem = lhsit->next();
    const item *rhsitem = rhsit->next();

    if (&lhsitem->get_op() != &rhsitem->get_op())
      return false;

    if (!lhsitem->get_op().equals(lhsitem->element(), rhsitem->element()))
      return false;
  }
  return true;
}


bool aka2::all_equals(const void *lhs, const void *rhs, const all_op &aop) {

  if (!attributes_equals(lhs, rhs, aop))
    return false;

  const member_map &mmap = aop.get_member_map();
  for (member_map::const_iterator it = mmap.begin(); it != mmap.end(); ++it) {
    if (!it->second.equals(lhs, rhs))
      return false;
  }
  return true;
}

bool aka2::simpletype_equals(const void *lhs, const void *rhs, const simpletype_op& op) {
  return op.equals(lhs, rhs);
}

bool aka2::array_equals(const void *lhs, const void *rhs, 
			const array_op &aop, const element_op &vop) {

  if (aop.empty(lhs) && aop.empty(rhs))
    return true;

  if (aop.size(lhs) != aop.size(rhs))
    return false;

  std::auto_ptr<array_iterator> lhsit(aop.get_iterator(lhs));
  std::auto_ptr<array_iterator> rhsit(aop.get_iterator(rhs));

  while (lhsit->has_next() && rhsit->has_next()) {
    const void *lhsitem = lhsit->next();
    const void *rhsitem = rhsit->next();
    if (!vop.equals(lhsitem, rhsitem))
      return false;
  }
  return true;
}

bool aka2::simplecontent_equals(const void *lhs, const void *rhs, const simplecontent_op &sop) {
  if (!attributes_equals(lhs, rhs, sop))
    return false;
  const member_type &mtype = sop.get_valuetype();
  return mtype.equals(lhs, rhs);
}

bool aka2::ptrmember_equals(const void *lhs, const void *rhs, const ptrmember_op &pop) {
  if (pop.is_null(lhs) && pop.is_null(rhs))
    return true;
  if (pop.is_null(lhs) || pop.is_null(rhs))
    return false;

  // Both must be non-null value.
  const void *lhsval = pop.dereference(lhs);
  const void *rhsval = pop.dereference(rhs);
  return pop.get_value_op().equals(lhsval, rhsval);
}
