/* 
 * © 2007-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 "model_figure_impl.h"
#include "model_diagram_impl.h"
#include "model_layer_impl.h"
#include "model_model_impl.h"

#include "grt/common.h"
#include "grt/grt_manager.h"
#include <grtpp_undo_manager.h>


model_Figure::ImplData::ImplData(model_Figure *owner)
  : model_Object::ImplData(owner), _realizing(false)
{
  _connected_update_options= false;
  
  owner->signal_changed().connect(sigc::mem_fun(this, &model_Figure::ImplData::member_changed));
}


void model_Figure::ImplData::update_options(const std::string &key)
{
  
}


void model_Figure::ImplData::set_layer(const model_LayerRef &nlayer)
{
  model_LayerRef oldLayer(self()->_layer);
  
  if (get_canvas_view())
    get_canvas_view()->lock_redraw();
  
  self()->_layer= nlayer;
  
  if (self()->_layer.is_valid())
  {
    mdc::CanvasItem *figure= get_canvas_item();
    
    model_Layer::ImplData *layer= nlayer->get_data();
    mdc::AreaGroup *ag= 0;
    if (layer)
      ag= layer->get_area_group();
    
    if (oldLayer.is_valid())
    {
      // update the position of the figure
      self()->_top= *self()->_top - *self()->_layer->top() + *oldLayer->top();
      self()->_left= *self()->_left - *self()->_layer->left() + *oldLayer->left();
    }
    else
    {
      self()->_top= *self()->_top - *self()->_layer->top();
      self()->_left= *self()->_left - *self()->_layer->left();
    }
    
    if (figure && ag)
    {        
      // will remove from old group if needed
      ag->add(figure);
      
      figure->move_to(mdc::Point(*self()->_left, *self()->_top));
    }
  }
  if (get_canvas_view())
    get_canvas_view()->unlock_redraw();

  try_realize();
}


void model_Figure::ImplData::member_changed(const std::string &name, const grt::ValueRef &ovalue)
{
  if (name == "name")
  {
    if (_in_view)
    {
      self()->owner()->signal_refreshDisplay().emit(self());
    }
  }
  else if (name == "color")
  {
    if (get_canvas_item())
    {
      if (self()->_color.is_valid() && *self()->_color.c_str())
        ((wbfig::BaseFigure*)get_canvas_item())->set_color(mdc::Color::parse(*self()->_color));
      else
        ((wbfig::BaseFigure*)get_canvas_item())->unset_color();
    }
  }
  else if (get_canvas_item()) // only valid if realized
  {
    if (name == "height")
    {
      mdc::Size size(get_canvas_item()->get_size());
      mdc::Size min_size(get_canvas_item()->get_min_size());

      if (*self()->_height < min_size.height)
        self()->_height= min_size.height;
     
      get_canvas_view()->lock();

      if (*self()->_height != size.height)
      {
        size.height= *self()->_height;
        if (*self()->_manualSizing)
          get_canvas_item()->set_fixed_size(size);
        else
          get_canvas_item()->resize_to(size);
      }

      get_canvas_view()->unlock();
    }
    else if (name == "width")
    {
      mdc::Size size(get_canvas_item()->get_size());
      mdc::Size min_size(get_canvas_item()->get_min_size());

      if (*self()->_width < min_size.width)
        self()->_width= min_size.width;

      get_canvas_view()->lock();

      if (*self()->_width != size.width)
      {
        size.width= *self()->_width;
        if (*self()->_manualSizing)
          get_canvas_item()->set_fixed_size(size);
        else
          get_canvas_item()->resize_to(size);
      }

      get_canvas_view()->unlock();
    }
    else if (name == "left")
    {      
      get_canvas_view()->lock();
      mdc::Point pos= get_canvas_item()->get_position();
      if (*self()->_left != pos.x)
      {
        pos.x= *self()->_left;
        get_canvas_item()->move_to(pos);
      }
      get_canvas_view()->unlock();
    }
    else if (name == "top")
    {      
      get_canvas_view()->lock();
      mdc::Point pos= get_canvas_item()->get_position();
      if (*self()->_top != pos.y)
      {
        pos.y= *self()->_top;
        get_canvas_item()->move_to(pos);
      }
      get_canvas_view()->unlock();
    }
    else if (name == "manualSizing")
    {
      if (*self()->_manualSizing == 0 && *grt::IntegerRef::cast_from(ovalue) != 0)
      {
        // enabling auto-size
        // must save the original fixed size to the undo stack, so that it can be restored on Undo
        if (_self->get_grt()->tracking_changes())
        {
          grt::UndoManager *um= _self->get_grt()->get_undo_manager();
          um->add_undo(new grt::UndoObjectChangeAction(self(), "width"));
          um->add_undo(new grt::UndoObjectChangeAction(self(), "height"));
        }
      }

      dynamic_cast<wbfig::BaseFigure*>(get_canvas_item())->set_allow_manual_resizing(*self()->_manualSizing != 0);
    }
    else if (name == "expanded")
    {
      if (get_canvas_item())
        ((wbfig::BaseFigure*)get_canvas_item())->toggle(*self()->_expanded!=0);
    }
    else if (name == "visible")
    {
      if (*self()->_visible)
        try_realize();
      else
        unrealize();
    }
  }
}


bool model_Figure::ImplData::is_realizable()
{
  if (_in_view && *self()->_visible && self()->layer().is_valid())
  {
    model_Layer::ImplData *layer= self()->_layer->get_data();
    model_Diagram::ImplData *view= self()->owner()->get_data();
  
    if (layer && layer->get_area_group() && view && view->get_canvas_view())
      return true;
  }
  return false;
}


void model_Figure::ImplData::figure_bounds_changed(const mdc::Rect &rect)
{
  mdc::Rect bounds(get_canvas_item()->get_bounds());

  self()->_left= grt::DoubleRef(bounds.xmin());
  self()->_top= grt::DoubleRef(bounds.ymin());
  self()->_width= grt::DoubleRef(bounds.width());
  self()->_height= grt::DoubleRef(bounds.height());

  relayout_badges();
}


void model_Figure::ImplData::figure_resized(const mdc::Rect &rect)
{
  mdc::Rect bounds= get_canvas_item()->get_bounds();

  model_Model::ImplData *model= self()->owner()->owner()->get_data();
  bool skip_undo= true;
  if (model && !_realizing && rect != bounds)
    skip_undo= false;

  grt::AutoUndo undo(self()->get_grt(), skip_undo);

  self()->manualSizing(1);
  dynamic_cast<wbfig::BaseFigure*>(get_canvas_item())->set_allow_manual_resizing(true);

  self()->left(grt::DoubleRef(bounds.xmin()));
  self()->top(grt::DoubleRef(bounds.ymin()));
  self()->width(grt::DoubleRef(bounds.width()));
  self()->height(grt::DoubleRef(bounds.height()));
    
  undo.end(strfmt("Resize '%s'", self()->_name.c_str()));
}


void model_Figure::ImplData::finish_realize()
{
  mdc::Size size;
  mdc::Point pos;
  wbfig::BaseFigure *figure= ((wbfig::BaseFigure*)get_canvas_item());

  _realizing= true;

  if (!_connected_update_options)
  {
    _connected_update_options= true;
    self()->owner()->owner()->get_data()->signal_options_changed().connect(sigc::mem_fun(this, &model_Figure::ImplData::update_options));
  }

  figure->set_tag(self()->id());

  figure->signal_bounds_changed().connect(sigc::mem_fun(this, &model_Figure::ImplData::figure_bounds_changed));
  figure->signal_interactive_resize().connect(sigc::mem_fun(this, &model_Figure::ImplData::figure_resized));

  pos.x= *self()->_left;
  pos.y= *self()->_top;

  
  if (*self()->_manualSizing)
  {
    figure->set_allow_manual_resizing(true);
    size.width= *self()->_width;
    size.height= *self()->_height;
    figure->set_fixed_size(size);
  }
  else
  {
    if (*self()->_width > 0 && *self()->_height > 0)
    {
      size.width= *self()->_width;
      size.height= *self()->_height;
      figure->resize_to(size);
    }
    else
    {
      // force relayout and fetch size
      figure->relayout();
      self()->_width= figure->get_size().width;
      self()->_height= figure->get_size().height;
    }
  }
  figure->move_to(pos);

  // check if the figure is in the views selection list and select it if so
  if (self()->owner()->selection().get_index(model_FigureRef(self())) != grt::BaseListRef::npos)
  {
    if (self()->owner()->selection().count() == 1)
      figure->get_view()->get_selection()->set(figure);
    else
      figure->get_view()->get_selection()->add(figure);
  }

  model_Diagram::ImplData *view= self()->owner()->get_data();
  model_Model::ImplData *model= self()->owner()->owner()->get_data();
  if (model)
  {
    std::string font;
    
    font= model->get_string_option(strfmt("%s:TitleFont", self()->class_name().c_str()), "");
    if (!font.empty())
      figure->set_title_font(parse_font_spec(font));

    font= model->get_string_option(strfmt("%s:ItemsFont", self()->class_name().c_str()), "");
    if (font.empty())
      font= model->get_string_option(strfmt("%s:TextFont", self()->class_name().c_str()), "");
    if (!font.empty())
      figure->set_content_font(parse_font_spec(font));
  }

  view->stack_figure(self(), get_canvas_item());

  _realizing= false;
}


void model_Figure::ImplData::highlight(const mdc::Color *color)
{
  if (get_canvas_item())
    dynamic_cast<wbfig::BaseFigure*>(get_canvas_item())->highlight(color);
}


void model_Figure::ImplData::unhighlight()
{
  if (get_canvas_item())
    dynamic_cast<wbfig::BaseFigure*>(get_canvas_item())->unhighlight();
}


void model_Figure::ImplData::render_mini(mdc::CairoCtx *cr)
{
  mdc::Rect rect= get_canvas_item()->get_bounds();
  
  cr->set_color(mdc::Color::parse(*self()->_color));
  cr->rectangle(rect);
  cr->fill();
}


void model_Figure::ImplData::unrealize()
{
  if (get_canvas_item())
  {
    get_canvas_item()->get_view()->remove_item(get_canvas_item());
  }
}


mdc::CanvasView* model_Figure::ImplData::get_canvas_view() const
{
  if (self()->owner().is_valid())
  {
    model_Diagram::ImplData *view= self()->owner()->get_data();
    if (view)
      return view->get_canvas_view();
  }
  return 0;
}



void model_Figure::ImplData::relayout_badges()
{
  if (!_badges.empty() && get_canvas_item())
  {
    mdc::Rect bounds(get_canvas_item()->get_root_bounds());
    mdc::Point pos;
    
    pos.x= bounds.xmax() - 4;
    pos.y= bounds.ymin() + 5;
    
    for (std::list<BadgeFigure*>::const_iterator iter= _badges.begin(); iter != _badges.end(); ++iter)
    {
      (*iter)->get_layer()->get_root_area_group()->raise_item(*iter);
      (*iter)->set_position(pos);
      (*iter)->set_visible(true);
      (*iter)->set_needs_relayout();
      
      pos.y+= (*iter)->get_size().height + 5;
    }
  }
}


void model_Figure::ImplData::add_badge(BadgeFigure *badge)
{
  badge->set_visible(false);
  _badges.push_back(badge);
  
  relayout_badges();
}


void model_Figure::ImplData::remove_badge(BadgeFigure *badge)
{
  std::list<BadgeFigure*>::iterator iter= std::find(_badges.begin(), _badges.end(), badge);
  
  if (iter != _badges.end())
    _badges.erase(iter);
  
  relayout_badges();
}


BadgeFigure *model_Figure::ImplData::get_badge_with_id(const std::string &badge_id)
{
  for (std::list<BadgeFigure*>::const_iterator iter= _badges.begin(); 
       iter != _badges.end(); ++iter)
  {
    if ((*iter)->badge_id() == badge_id)
      return *iter;
  }
  return 0;
}
