/* 
 * © 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.
 */
#ifndef __changelistobjects_H
#define __changelistobjects_H

#include <iostream>
#include <assert.h>

#include "diffchange.h"
#include "listdifference.h"
#include "grtlistdiff.h"

#include "grtpp_util.h"

namespace grt 
{

struct pless_struct
  : public std::binary_function<ValueRef, ValueRef, bool>
{ // functor for operator<
  bool operator()(const ValueRef&_Left, const ValueRef&_Right) const;
};

//////////////////////////////////////////////////////////////
// @class 
//////////////////////////////////////////////////////////////
//
// @brief 
//
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////

struct MYSQLGRT_PUBLIC GrtListDifference : public ListDifference<ValueRef,internal::List::raw_iterator,internal::List::raw_iterator>
{
  typedef std::map<ValueRef, bool, pless_struct> ModifiedChangeApplied;
  ModifiedChangeApplied modified_change_applied;
};


//class MYSQLGRT_PUBLIC ListItemChange : public DiffChange
//{
//public:
//  virtual void prepare()= 0;
//};
typedef DiffChange ListItemChange;

//////////////////////////////////////////////////////////////
class MYSQLGRT_PUBLIC ListItemModifiedChange : public ListItemChange
{
  GrtListDifference& _actions;
  //DiffChange* _subchange;
  ChangeSet _cs;

  GrtListDifference::ItemPair _action;
public:
  GrtListDifference::ItemPair get_action() const { return _action; }

  ListItemModifiedChange(
    const GrtListDifference::ItemPair &action, 
    GrtListDifference& actions, 
    DiffChange* change
    ) 
    : ListItemChange(ListItemModified), 
    _actions(actions),
    //_subchange(change), 
    _action(action)
  {
    _cs.append(change);
    _actions.modified_change_applied[_action.first.first]= false;
  }

  ~ListItemModifiedChange() 
  { 
    //delete _subchange; 
    delete *_cs.changes.begin();
  }

  size_t get_source_object_index() const
  {
    return _actions.calc_index(_action.first.second);
  }

  const ChangeSet* subchanges() const { return &_cs; }

  void apply(ValueRef& v)
  {
    if (is_disabled())
      return;

    if (_actions.modified_change_applied[_action.first.first])
      return;

    _actions.modified_change_applied[_action.first.first]= true;
    //size_t index= _actions.calc_index(_action.second);
    size_t index= get_source_object_index();
    ValueRef value= BaseListRef::cast_from(v).get(index);
    //_subchange->apply(value);
    (*_cs.changes.begin())->apply(value);
  }

  void dump_log(int level) const
  {
    std::cout << std::string(level, ' ');
    std::cout << get_type_name() <<  std::endl;
    //_subchange->dump_log(level+1);
    (*_cs.changes.begin())->dump_log(level+1);
  }
};

//class MYSQLGRT_PUBLIC ListStableItemModifiedChange : public ListItemChange
//{
//  size_t _stable_index;
//  GrtListDifference& _actions;
//  DiffChange* _subchange;
//public:
//  ListStableItemModifiedChange(
//    size_t &stable_index, 
//    GrtListDifference& actions, 
//    DiffChange* change
//    ) 
//    : ListItemChange(ListStableItemModified), 
//    _subchange(change), 
//    _stable_index(stable_index),
//    _actions(actions)
//  {
//  }
//
//  ~ListStableItemModifiedChange() 
//  { 
//    delete _subchange; 
//  }
//
//  void apply(ValueRef& v)
//  {
//    if (_actions.modified_change_applied[_action.first])
//      return;
//    _actions.modified_change_applied[_action.first]= true;
//    size_t index= _actions.calc_index(_action.second);
//    ValueRef value= BaseListRef::cast_from(v).get(index);
//    _subchange->apply(value);
//  }
//
//  void dump_log(int level) const
//  {
//    std::cout << std::string(level, ' ');
//    std::cout << get_type_name() <<  std::endl;
//    _subchange->dump_log(level+1);
//  }
//};
//////////////////////////////////////////////////////////////
ListItemModifiedChange *create_item_modified_change(
                                     const GrtListDifference::ItemPair& action, 
                                     GrtListDifference& actions, 
                                     const ValueRef &source, 
                                     const ValueRef &target,
                                     const Omf* omf,
									 const TSlotNormilizerSlot sqlDefinitionCmp);

ListItemModifiedChange *create_stable_item_modified_change(
  size_t stable_index, 
  GrtListDifference& actions, 
  const ValueRef &source, 
  const ValueRef &target,
  const Omf* omf,
  const TSlotNormilizerSlot sqlDefinitionCmp);

//////////////////////////////////////////////////////////////
class MYSQLGRT_PUBLIC ListItemAddedChange : public DiffChange
{
  GrtListDifference::Item _action;
  GrtListDifference& _actions;
  ValueRef _value_to_add;
public:
  ListItemAddedChange(const GrtListDifference::Item& action, GrtListDifference& actions, ValueRef v) 
    : ListItemChange(ListItemAdded), 
    _action(action),
    _actions(actions),
    _value_to_add(copy_value(v, true))
  {
  }

  GrtListDifference::Item get_action() const { return _action; }

  ValueRef get_added_object() const { return _value_to_add; }

  int get_object_insertion_index() const
  {
    grt::GrtListDifference actions= _actions;
    grt::GrtListDifference::Item action= _action;
    return actions.calc_index(action.second);
  }

  void apply(ValueRef& v)
  {
    if (is_disabled())
      return;

    int index= _actions.apply_added(_action);
    BaseListRef::cast_from(v).ginsert(_value_to_add, index);
  }

  void dump_log(int level) const
  {
    std::cout << std::string(level, ' ');
    std::cout << get_type_name() <<  " index" << _action.second.first << ':' << _action.second.second;
    if (ObjectRef::can_wrap(_value_to_add) && ObjectRef::cast_from(_value_to_add).has_member("name"))
      std::cout << " name:" << ObjectRef::cast_from(_value_to_add).get_string_member("name").c_str();
    std::cout << std::endl;
  }
};


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

class MYSQLGRT_PUBLIC ListItemRemovedChange : public DiffChange
{
  GrtListDifference::Item _action;
  GrtListDifference& _actions;
public:
  ListItemRemovedChange(const GrtListDifference::Item& action, GrtListDifference& actions) 
    : ListItemChange(ListItemRemoved),
    _action(action),
    _actions(actions)
  {
  }

  GrtListDifference::Item get_action() const { return _action; }

  size_t get_source_object_index() const
  {
    return _actions.calc_index_for_item_removed_action(_action);
  }

  void apply(ValueRef& v)
  {    
    if (is_disabled())
      return;

    int index= _actions.apply_removed(_action);
    BaseListRef::cast_from(v).remove(index);
  }

  void dump_log(int level) const
  {
    std::cout << std::string(level, ' ');
    std::cout << get_type_name() <<  " index" << _action.second.first << ':' << _action.second.second << std::endl;
  }

};

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

class MYSQLGRT_PUBLIC ListItemOrderChange : public DiffChange
{
  GrtListDifference::ItemPair _action;
  GrtListDifference& _actions;
  ListItemModifiedChange* _subchange;
  grt::ChangeSet cs;

public:
  ListItemOrderChange(const GrtListDifference::ItemPair& action, GrtListDifference& actions, const ValueRef &source, const ValueRef &target, const Omf* omf, const TSlotNormilizerSlot sqlDefinitionCmp) 
    : ListItemChange(ListItemOrderChanged),
    _action(action),
    _actions(actions)
  {
    _subchange= create_item_modified_change(_action, _actions, source, target, omf,sqlDefinitionCmp);
    cs.append(_subchange);
  }

  GrtListDifference::ItemPair get_action() const { return _action; }
  virtual const grt::ChangeSet* subchanges() const { return &cs; }

  void apply(ValueRef& v)
  {   
    if (is_disabled())
      return;

    std::pair<int, int> ind= _actions.apply_moved(_action);
    BaseListRef::cast_from(v).reorder(ind.first, ind.second);

// although _subchange containds both before and after positions we may not update it
//    if (_subchange) // this will signal that item to modify changed position
//      _subchange->_action.first.second= _action.second.second;
  }
};

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

class MYSQLGRT_PUBLIC ListChange : public MultiChange
{
protected:
  GRT* _grt;
  GrtListDifference *_actions;
public:
  ListChange(ChangeSet& changes, GRT* grt, GrtListDifference *actions) : MultiChange(ListModified, changes), _grt(grt), _actions(actions) 
  {
  }

  ~ListChange() { delete _actions; }

  const GrtListDifference& get_actions() const { return *_actions; }

  void apply(ValueRef& v)
  {
    MultiChange::apply(v);
    //unsigned int num= _actions->item_count();
    //std::vector<internal::Value*> res(num);
    //_actions->expand(res.begin());

    //BaseListRef list(_grt, MYX_ANY_VALUE);
    //for (std::vector<internal::Value*>::iterator iter= res.begin(); iter != res.end(); ++iter)
    //  list.ginsert( ValueRef(*iter) );

    //v= list;
  }
};


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

}
#endif
