/* 
 * © 2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library 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 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "stdafx.h"

#include <memory>
#include "grtlistdiff.h"
#include "grtpp.h"
#include "listdifference.h"
#include "changefactory.h"
#include "changelistobjects.h"
#include "grtdiff.h"

#include <memory>

namespace grt
{


static bool pless(const ValueRef &l, const ValueRef &r)
{
  if ((l.type() == r.type() && l.type() == ObjectType)
    && ObjectRef::can_wrap(l) && ObjectRef::can_wrap(r))
  {
    ObjectRef left = ObjectRef::cast_from(l);
    ObjectRef right = ObjectRef::cast_from(r);
    if(left->has_member("name"))
      return left->get_string_member("name") < right->get_string_member("name");
  }
  return l < r;
}



static bool peq(const ValueRef &l, const ValueRef &r)
{
  if ((l.type() == r.type() && l.type() == ObjectType)
    && ObjectRef::can_wrap(l) && ObjectRef::can_wrap(r))
  {
    ObjectRef left = ObjectRef::cast_from(l);
    ObjectRef right = ObjectRef::cast_from(r);
    if(left->has_member("name"))
      return left->get_string_member("name") == right->get_string_member("name");
  }
  return l == r;
}


bool pless_struct::operator()(const ValueRef &_Left, const ValueRef &_Right) const
{ // apply operator< to operands
  return _Left < _Right;
}


//template<>
//bool lt_first<GrtListDifference::RemoveActionSet::value_type, class _LtOp>::operator ()(
//  const GrtListDifference::RemoveActionSet::value_type& l, const GrtListDifference::RemoveActionSet::value_type& r)
//{
//  return Lt(*(l.first), *(r.first));
//}

struct omf_eq
{
  const Omf *omf;
  omf_eq(const Omf *omf) : omf(omf) {};
  bool operator ()(const ValueRef &l, const ValueRef &r)
  {
    return omf->equal(l, r);
  }
};

struct omf_lt
{
  const Omf *omf;
  omf_lt(const Omf *omf) : omf(omf) {};

  bool operator ()(const ValueRef &l, const ValueRef &r)
  {
    return omf->less(l, r);
  }
};

DiffChange *GrtListDiff::diff(const BaseListRef &source, const BaseListRef &target, const Omf *omf, const TSlotNormilizerSlot sqlDefinitionCmp)
{
  std::auto_ptr<GrtListDifference > actions_ptr(new GrtListDifference);
  GrtListDifference &actions= *actions_ptr;
  actions.clear();

  if (!omf)
  {
    actions.parse_structure(source.content().raw_begin(), source.content().raw_end(), 
                            target.content().raw_begin(), target.content().raw_end(), peq, pless);
  } 
  else
  {
    actions.parse_structure(source.content().raw_begin(), source.content().raw_end(),
                            target.content().raw_begin(), target.content().raw_end(), omf_eq(omf), omf_lt(omf));
  }

  ChangeSet changes;
  for (GrtListDifference::AddActionSet::iterator iter= actions.added.begin(); iter != actions.added.end(); ++iter)
  {
    ValueRef value= iter->first;
    changes.append(new ListItemAddedChange(*iter, actions, ValueRef(value)));
  }

  for (GrtListDifference::RemoveActionSet::iterator iter= actions.removed.begin(); iter != actions.removed.end(); ++iter)
    changes.append(new ListItemRemovedChange(*iter, actions));

  for (GrtListDifference::MoveActionSet::iterator iter= actions.moved.begin(); iter != actions.moved.end(); ++iter)
    changes.append(new ListItemOrderChange(*iter, actions, ValueRef(iter->first.first), ValueRef(iter->second.first), omf, sqlDefinitionCmp));

  assert(actions.stable1.size() == actions.stable2.size());
  size_t count= actions.stable1.size();
  for (size_t i= 0; i < count ; ++i)
    changes.append(create_stable_item_modified_change(i, actions, ValueRef(*actions.stable1[i]), ValueRef(*actions.stable2[i]), omf, sqlDefinitionCmp));

  // TODO Modified action for stable elements

  //struct CreateModifiedAction 
  //{
  //  ChangeSet& changes;
  //  GrtListDiff& factory;
  //  CreateModifiedAction(ChangeSet& changes, GrtListDiff& factory) : changes(changes), factory(factory) {}

  //  void operator() (ValueRef &source, ValueRef &target)
  //  {
  //    changes.append(factory.create_item_modified_change(ValueRef(source), ValueRef(target)));
  //  }
  //};

  //actions.for_each_stable(CreateModifiedAction(changes, *this));
  //actions.for_each_moved(CreateModifiedAction(changes, *this));

  if (!changes.empty())
    return new ListChange(changes, source.content().get_grt(), actions_ptr.release());

  return NULL;
}

////////////////////////////////////////////////////////////////////////////


template<class T>
bool less_for_type(const T& l, const T& r);

template<>
bool less_for_type(const ObjectRef &l, const ObjectRef &r)
{
  return l.id() < r.id();
}

template<>
bool less_for_type(const DictRef &l, const DictRef &r)
{
  // TODO implement this
  return l.count() < r.count();
}

template<>
bool less_for_type(const BaseListRef &l, const BaseListRef &r)
{
  if (l.content_type() != r.content_type())
    return l.content_type() < r.content_type(); // TODO what if type is ANY?
  // TODO implement this
  return l.count() < r.count();
}


bool less(const ValueRef& l, const ValueRef& r)
{
  const Type type=  l.type();
  if (type != r.type())
    return type < r.type();

  switch(type)
  {
  case AnyType:
    return false;
  case IntegerType:
  case DoubleType:
  case StringType:
    return l < r;
  case ObjectType:
    return less_for_type(ObjectRef::cast_from(l), ObjectRef::cast_from(r));
  case ListType:
    return less_for_type(BaseListRef::cast_from(l), BaseListRef::cast_from(r));
  case DictType:
    return less_for_type(DictRef::cast_from(l), DictRef::cast_from(r));
  }

  //assert(0); TODO
  return false;
}

ListItemModifiedChange *create_item_modified_change(
  const GrtListDifference::ItemPair& action, 
  GrtListDifference& actions, 
  const ValueRef &source, 
  const ValueRef &target,
  const Omf* omf,
  const TSlotNormilizerSlot sqlDefinitionCmp)
{
  DiffChange *subchange= diff_make(source, target, omf, sqlDefinitionCmp);
  if (subchange)
  {
//    diff_make(source, target, omf, sqlDefinitionCmp);
    return new ListItemModifiedChange(action, actions, subchange);
  }
  return NULL;
}

ListItemModifiedChange *create_stable_item_modified_change(
  size_t stable_index,
  GrtListDifference& actions,
  const ValueRef &source, 
  const ValueRef &target,
  const Omf* omf,
  const TSlotNormilizerSlot sqlDefinitionCmp)
{
  GrtListDifference::ItemPair action;
  action.first= GrtListDifference::Item(source, GrtListDifference::Position(-1, stable_index));
  action.second= GrtListDifference::Item(target, GrtListDifference::Position(-1, stable_index));
  return create_item_modified_change(
    action, 
    actions, 
    source, 
    target,
    omf,
	sqlDefinitionCmp);
}


}
