#include "stdafx.h"

#include "mdc.h"

#include "mini_view.h"

#include "wbcanvas/model_figure_impl.h"
#include "wbcanvas/model_layer_impl.h"




using namespace mdc;
using namespace wb;



MiniView::MiniView(mdc::Layer *layer)
: mdc::Figure(layer), _canvas_view(0), _viewport_figure(0)
{
  _updating_viewport= false;
  _skip_viewport_update= false;

  set_cache_toplevel_contents(true);

  layer->get_view()->set_event_callbacks(sigc::mem_fun(*this, &MiniView::view_button_cb), 
    sigc::mem_fun(*this, &MiniView::view_motion_cb),
    sigc::slot<bool, CanvasView*, KeyInfo, EventState, bool>());
}


MiniView::~MiniView()
{
}


bool MiniView::view_button_cb(mdc::CanvasView *view, mdc::MouseButton btn, bool press, mdc::Point pos, mdc::EventState)
{
  if (btn == mdc::ButtonLeft && _viewport_figure)
  {
    if (press)
      _click_pos= pos;
    else
    {
      if (_click_pos == pos)
      {
        double scale;
        Rect bounds= get_scaled_target_bounds(scale);
        Rect rect= _viewport_figure->get_bounds();
        Rect nrect;

        nrect.pos.x= pos.x - rect.width()/2;
        nrect.pos.y= pos.y - rect.height()/2;
        nrect.size= rect.size;

        if (nrect.xmin() < bounds.xmin()) nrect.pos.x= bounds.xmin();
        if (nrect.ymin() < bounds.ymin()) nrect.pos.y= bounds.ymin();
        if (nrect.xmax() > bounds.xmax()) nrect.pos.x= bounds.xmax()-nrect.width();
        if (nrect.ymax() > bounds.ymax()) nrect.pos.y= bounds.ymax()-nrect.height();

        _viewport_figure->set_bounds(nrect);
        _viewport_figure->set_needs_render();
        viewport_dragged(rect);
      }
    }
  }

  return false;
}


bool MiniView::view_motion_cb(mdc::CanvasView*, mdc::Point, mdc::EventState)
{
  return false;
}


void MiniView::update_size()
{
  Size size= get_layer()->get_view()->get_total_view_size();

  set_fixed_size(size);
  resize_to(size);

  viewport_changed();
}


void MiniView::render_figure(CairoCtx *cr, const model_FigureRef &elem)
{
  model_Figure::ImplData *e= elem->get_data();

  if (e)
    e->render_mini(cr);
}



void MiniView::render_layer(CairoCtx *cr, const model_LayerRef &layer, bool draw)
{
  model_Layer::ImplData *l= layer->get_data();

  if (l && draw)
    l->render_mini(cr);

  for (size_t c= layer->figures().count(), i= 0; i < c; i++)
  {
    model_FigureRef figure(layer->figures()[i]);
    mdc::CanvasItem *figure_layer;
   
    if (figure->get_data()->get_canvas_item())
    { 
      cr->save();
    
      figure_layer= figure->get_data()->get_canvas_item()->get_parent();
      cr->translate(figure_layer->get_position());

      render_figure(cr, figure);
    
      cr->restore();
    }
  }
}


mdc::Rect MiniView::get_scaled_target_bounds(double &scale)
{
  mdc::Rect rect;

  scale= 1.0;

  if (_canvas_view)
  {
    Size full_size= _canvas_view->get_total_view_size();
    Size mini_size= get_layer()->get_view()->get_total_view_size();

    // calculate a scaling for fitting the whole view
    if (full_size.width/full_size.height < mini_size.width/mini_size.height)
      scale= mini_size.height/full_size.height;
    else
      scale= mini_size.width/full_size.width;

    rect.pos.x= (mini_size.width - full_size.width * scale) / 2;
    rect.pos.y= (mini_size.height - full_size.height * scale) / 2;
    rect.size.width= full_size.width * scale;
    rect.size.height= full_size.height * scale;
  }
  return rect;
}


void MiniView::draw_contents(CairoCtx *cr)
{
  cr->set_operator(CAIRO_OPERATOR_SOURCE);
  cr->set_color(Color(0.7,0.7,0.7));
  cr->paint();

  if (!_canvas_view || !_model_diagram.is_valid() || !_model_diagram->rootLayer().is_valid())
    return;

  double scale;
  Rect bounds= get_scaled_target_bounds(scale);

  cr->save();

  cr->set_line_width(1);

  cr->set_color(Color(1, 1, 1));
  cr->rectangle(bounds);
  cr->fill_preserve();
  cr->set_color(Color(0.0, 0.0, 0.0));
  cr->stroke();

  mdc::Size page_size(_canvas_view->get_page_size());
  mdc::Count xpages, ypages;

  cr->set_color(Color(0.7, 0.7, 0.7));
  page_size.width*= scale;
  page_size.height*= scale;
  page_size= page_size.round();

  _canvas_view->get_page_layout(xpages, ypages);

  for (mdc::Count y= 1; y < ypages; y++)
  {
    cr->move_to(bounds.xmin()+0.5, floor(bounds.ymin()+y*page_size.height)+0.5);
    cr->line_to(bounds.xmax()+0.5, floor(bounds.ymin()+y*page_size.height)+0.5);
    cr->stroke();
  }

  for (mdc::Count x= 1; x < xpages; x++)
  {
    cr->move_to(floor(bounds.xmin()+x*page_size.width)+0.5, bounds.ymin()+0.5);
    cr->line_to(floor(bounds.xmin()+x*page_size.width)+0.5, bounds.ymax()+0.5);
    cr->stroke();
  }

  cr->translate(bounds.pos);
  cr->scale(scale, scale);

  render_layer(cr, _model_diagram->rootLayer(), false);
  for (size_t c= _model_diagram->layers().count(), i= 0; i < c; i++)
    render_layer(cr, _model_diagram->layers()[i], true);

  cr->restore();
}


void MiniView::viewport_changed()
{
  if (_viewport_figure && _canvas_view && !_updating_viewport)
  {
    Rect vp= _canvas_view->get_viewport();
    double scale;
    Rect bounds= get_scaled_target_bounds(scale);

    vp.pos.x= vp.pos.x * scale + bounds.xmin();
    vp.pos.y= vp.pos.y * scale + bounds.ymin();
    vp.size.width*= scale;
    vp.size.height*= scale;

    _skip_viewport_update= true;
    _viewport_figure->set_bounds(vp);
    _viewport_figure->set_needs_render();
    _skip_viewport_update= false;
  }
}


void MiniView::viewport_dragged(const mdc::Rect &orect)
{
  if (!_skip_viewport_update)
  {
    double scale;
    Rect bounds= get_scaled_target_bounds(scale);
    Rect rect= _viewport_figure->get_bounds();
    Rect nrect;

   _updating_viewport= true;

    nrect= rect;
    if (nrect.xmin() < bounds.xmin()) nrect.pos.x= bounds.xmin();
    if (nrect.ymin() < bounds.ymin()) nrect.pos.y= bounds.ymin();
    if (nrect.xmax() > bounds.xmax()) nrect.pos.x= bounds.xmax()-nrect.width();
    if (nrect.ymax() > bounds.ymax()) nrect.pos.y= bounds.ymax()-nrect.height();

    if (nrect != rect)
    {
      _viewport_figure->set_bounds(nrect);
      _viewport_figure->set_needs_render();
    }

    if (_canvas_view)
    {
      Point p;

      p.x= (nrect.xmin()-bounds.xmin())/scale;
      p.y= (nrect.ymin()-bounds.ymin())/scale;

      _canvas_view->set_offset(p);
    }
    _updating_viewport= false;
  }
}


void MiniView::set_active_view(mdc::CanvasView *canvas_view, const model_DiagramRef &model_diagram)
{
  _canvas_view= canvas_view;
  _model_diagram= model_diagram;

  if (!_viewport_figure)
  {
    _viewport_figure= new mdc::RectangleFigure(get_layer());
    _viewport_figure->set_filled(false);
    get_layer()->get_view()->get_current_layer()->add_item(_viewport_figure);
    //_viewport_figure->set_cache_toplevel_contents(false);
    _viewport_figure->set_accepts_selection(true);
    _viewport_figure->set_accepts_focus(false);
    _viewport_figure->set_state_drawing(false);
    _viewport_figure->set_auto_sizing(false);
    _viewport_figure->set_draggable(true);
    _viewport_figure->set_needs_render();

    _viewport_figure->signal_bounds_changed().connect(sigc::mem_fun(this, &MiniView::viewport_dragged));
  }

  if (_view_repaint_connection)
    _view_repaint_connection.disconnect();

  if (_view_viewport_change_connection)
    _view_viewport_change_connection.disconnect();

  if (_canvas_view)
  {
    _view_viewport_change_connection= _canvas_view->signal_viewport_changed().
      connect(sigc::mem_fun(this, &MiniView::viewport_changed));
    
    _view_repaint_connection= _canvas_view->signal_repaint().connect(
      sigc::hide(sigc::hide(sigc::hide(sigc::hide(sigc::mem_fun(this, &CanvasItem::set_needs_render))))));


    _viewport_figure->set_visible(true);

    get_layer()->get_view()->set_page_size(get_layer()->get_view()->get_viewable_size());

    resize_to(get_layer()->get_view()->get_viewable_size());
    viewport_changed();
  }
  else
    _viewport_figure->set_visible(false);

  set_needs_render();
}

