/* 
 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * 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 General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "stdafx.h"

#include "wb_find_dialog.h"
#include "model/wb_model_diagram_form.h"

#include "wb_context.h"

#include "base/string_utilities.h"

#include "grts/structs.db.h"

using namespace grt;
using namespace wb;
using namespace base;





FindDialogBE::FindDialogBE(WBContext *wb)
: _wb(wb)
, _match_case(false), _search_in_comments(false)
, _search_in_sql(false)
, _object_activated(false)
, _abort_flag(false)
, _stack(NULL)
{

  _changed_param= true;
  _curmatch= 0;

  _text= 0;
  _lower_text= 0;

  _search_diagram_only= false;
}


FindDialogBE::~FindDialogBE()
{
  g_free(_text);
  g_free(_lower_text);
}


void FindDialogBE::set_activate_slot(const boost::function<void (GrtObjectRef, GrtObjectRef, std::string)> &slot)
{
  _activate_result_item= slot;
}


void FindDialogBE::set_text(const std::string &text)
{
  if (!_text || strcmp(_text, text.c_str())!=0)
  {
    g_free(_text);
    g_free(_lower_text);

    _text= g_strdup(text.c_str());
    _lower_text= g_utf8_casefold(text.data(), text.length());
    _changed_param= true;
  }
}


void FindDialogBE::set_match_case(bool flag)
{
  if (_match_case != flag)
  {
    _match_case= flag;
    _changed_param= true;
  }
}


void FindDialogBE::set_search_in_comments(bool flag)
{
  if (_search_in_comments != flag)
  {
    _search_in_comments= flag;
    _changed_param= true;
  }
}


void FindDialogBE::set_search_in_sql(bool flag)
{
  if (_search_in_sql != flag)
  {
    _search_in_sql= flag;
    _changed_param= true;
  }
}


void FindDialogBE::set_search_diagram_only(bool flag)
{
  _search_diagram_only= flag;
}


void FindDialogBE::set_abort_flag()
{
  _abort_flag= true;
}


GrtObjectRef get_location(const bec::CallStack &s)
{
  if (s.empty())
    return GrtObjectRef();

  bec::CallStack::const_reverse_iterator iter = s.rbegin();
  for(;iter != s.rend(); ++iter)
  {
    if (model_ObjectRef::can_wrap(*iter))
      return *iter;
  }
  return s.back();
}


void FindDialogBE::add_match(const GrtObjectRef &object, const std::string &text)
{
  GrtObjectRef location;
  if (_stack)
  {
    location= get_location(*_stack);
  }
  add_match(location, object, text);
}


void FindDialogBE::add_match(const GrtObjectRef &location, const GrtObjectRef &object, const std::string &text)
{
  if (_wb->get_parent_for_object<model_Diagram>(location) == location)
    return;

  for (std::vector<ResultItem>::const_iterator i= _matches.begin(); i != _matches.end(); ++i)
  {
    if (i->object == object && i->location == location)
      return;

    if (_search_diagram_only && location == i->location)
      return;
  }

  ResultItem result;
  result.location= location;
  result.object= object;
  result.text= text;

  _matches.push_back(result);
}


FindResult FindDialogBE::got_no_results()
{
  _wb->show_status_text(_("No matches"));
  return NotFound;
}

FindResult FindDialogBE::got_last_match()
{
  _wb->show_status_text(_("Reached the end of list"));
  return NoMoreMatches;
}

FindResult FindDialogBE::got_first_match()
{
  _wb->show_status_text(_("Reached the beginning of list"));
  return NoMoreMatches;
}

FindResult FindDialogBE::got_result()
{
  activate_found_object();
  return FoundMatch;
}


FindResult FindDialogBE::find_next()
{
  if (_changed_param)
    do_search();
  else if (!_matches.empty())
    ++_curmatch;

  if (_matches.empty())
    return got_no_results();

  if (_curmatch == (long)_matches.size())
    return got_last_match();

  if (_curmatch > (long)_matches.size())
    _curmatch= 0;

  return got_result();
}


FindResult FindDialogBE::find_previous()
{
  if (_changed_param)
    do_search();
  else if (!_matches.empty())
    --_curmatch;

  if (_matches.empty())
    return got_no_results();

  if (_curmatch == -1 && _object_activated)
    return got_first_match();

  if (_curmatch < -1 || !_object_activated)
    _curmatch= _matches.size() - 1;

  return got_result();
}


void FindDialogBE::activate_found_object()
{
  _object_activated= true;
  if (_curmatch >= 0 && _curmatch < (long)_matches.size())
  {
    ResultItem &match(_matches[_curmatch]);

    activate_result_item(match);
  }
}


bool FindDialogBE::activate_result_item(const ResultItem  &item)
{
  _activate_result_item(item.location, item.object, item.text);

  return true;
}



bool FindDialogBE::match(const char *text)
{
  if (!text)
    return false;

  

  if (_match_case)
  {
    if (strstr(text, _text))
      return true;
  }
  else
  {
    gchar *lowername= g_utf8_casefold(text, g_utf8_strlen(text, G_MAXINT8));

    if (g_strrstr(lowername, _lower_text ? _lower_text : _text))
    {
      g_free(lowername);
      return true;
    }
    g_free(lowername);
  }
  return false;
}



void FindDialogBE::do_search()
{
  CatalogIterator<FindDialogBE> iter;
  iter.append_allTypesCb(this, &FindDialogBE::search_in_name<GrtObject>);

  do_iterate(*this, iter);
}

