// src/cairo_elements.cc - Copyright 2005, 2006, University
//               of Padova, dept. of Pure and Applied
//               Mathematics
//
// This file is part of SGPEMv2.
//
// This 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; either version 3 of the License, or
// (at your option) any later version.
//
// SGPEMv2 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 SGPEMv2. If not, see http://www.gnu.org/licenses/.


#include "cairo_elements.hh"

#include <cmath>

#ifndef M_PI
  #define M_PI 3.14159265358979323846
#endif

using namespace sgpem;


CairoElements::CairoElements(const Cairo::RefPtr<Cairo::Context>& ctx)
    : _ctx(ctx)
{
}


void
CairoElements::draw_3dsphere(const Point& center, float radius, const Color& cl)
{
  using namespace Cairo;

  const float& x = center.x;
  const float& y = center.y;
  const float& r = cl.r;
  const float& g = cl.g;
  const float& b = cl.b;

  // Draw initial sphere perimeter
  _ctx->save();
  _ctx->begin_new_path();
  _ctx->arc(x, y, radius, 0, 2*M_PI);
  
  RefPtr<Gradient> grad = RadialGradient::create(x, y, radius, x, y, radius - .15);
  grad->add_color_stop_rgba(0, r - .2, g - .2, b - .2, 1);
  grad->add_color_stop_rgba(1,      r,      g,      b, 1);
  _ctx->set_source(grad);
  _ctx->fill_preserve();

  _ctx->set_source_rgba(0, 0, 0, 1);
  _ctx->set_line_width(radius * .01);
  _ctx->stroke();
  _ctx->restore();

  // Internal gradient
  _ctx->save();
  grad = LinearGradient::create(x, y, x, y + radius);
  grad->add_color_stop_rgba(1, r - .4, g - .4, b - .4,  1);
  grad->add_color_stop_rgba(0,      r,      g,      b, .7);
  _ctx->set_source(grad);
  _ctx->translate(x*.2, (y + radius*8 / 9) * 1 / 3);
  _ctx->scale(.8, 2. / 3);
  _ctx->arc(x, y, radius, 0, 2*M_PI);
  _ctx->fill();
  _ctx->restore();

  // Internal glow
  _ctx->save();
  grad = LinearGradient::create(x, y - radius, x, y);
  grad->add_color_stop_rgba(0, 1, 1, 1, .9);
  grad->add_color_stop_rgba(1, 1, 1, 1, .2);
  _ctx->set_source(grad);
  _ctx->translate(x * .2, (y - radius*8 / 9) * 1 / 3);
  _ctx->scale(.8, 2. / 3);
  _ctx->arc(x, y, radius, 0, 2*M_PI);
  _ctx->fill();
  _ctx->restore();
}

void
CairoElements::draw_3dcube(const Rectangle& area, const Color& cl,
                           const float x_percent, const float y_percent)
{
  using namespace Cairo;

  const float& x0 = area.x0;
  const float& y0 = area.y0;
  const float& w = area.w;
  const float& h = area.h;
  const float& r = cl.r;
  const float& g = cl.b;
  const float& b = cl.b;

  _ctx->save();
  _ctx->set_line_join(LINE_JOIN_ROUND);

  // Front side of the cube:
  _ctx->rectangle(x0,               y0 + (1 - y_percent) * h,
                  w * x_percent,    h * y_percent);
                  
  _ctx->set_source_rgb(r, g, b);
  _ctx->fill_preserve();
  _ctx->set_source_rgb(0, 0, 0);
  _ctx->stroke();

  // ``Upper'' visible cube side:
  _ctx->begin_new_path();
  _ctx->move_to(x0, y0 + (1 - y_percent) * h);
  _ctx->line_to(x0 + (1 - x_percent) * w, y0);
  _ctx->line_to(x0 + w, y0);
  _ctx->line_to(x0 + w * x_percent, y0 + (1 - y_percent) * h);
  _ctx->close_path();

  _ctx->set_source_rgb(r - 0.2, g - 0.2, b - 0.2);
  _ctx->fill_preserve();
  _ctx->set_source_rgb(0, 0, 0);
  _ctx->stroke();
  
  // ``Right'' visible cube side:
  _ctx->begin_new_path();
  _ctx->move_to(x0 + w * x_percent, y0 + (1 - y_percent) * h);
  _ctx->line_to(x0 + w, y0);
  _ctx->line_to(x0 + w, y0 + y_percent * h);
  _ctx->line_to(x0 + w * x_percent, y0 + h);
  _ctx->close_path();

  _ctx->set_source_rgb(r - 0.4, g - 0.4, b - 0.4);
  _ctx->fill_preserve();
  _ctx->set_source_rgb(0, 0, 0);
  _ctx->stroke();

  _ctx->restore();
}


void
CairoElements::draw_container(const Rectangle& area)
{
  using namespace Cairo;

  const float& x0 = area.x0;
  const float& y0 = area.y0;
  const float& w = area.w;
  const float& h = area.h;

  const double corner_radius = 0.05;

  _ctx->save();
  _ctx->begin_new_path();
  _ctx->move_to(x0 + corner_radius, y0);

  // NW -> NE
  _ctx->line_to(x0 + w - corner_radius, y0);
  _ctx->curve_to(x0 + w, y0, x0 + w, y0, x0 + w, y0 + corner_radius);
  // NE -> SE
  _ctx->line_to(x0 + w, y0 + h - corner_radius);
  _ctx->curve_to(x0 + w, y0 + h, x0 + w, y0 + h, x0 + w - corner_radius, y0 + h);
  // SE -> SW
  _ctx->line_to(x0 + corner_radius, y0 + h);
  _ctx->curve_to(x0, y0 + h, x0, y0 + h, x0, y0 + h - corner_radius);
  // SW -> NW
  _ctx->line_to(x0, y0 + corner_radius);
  _ctx->curve_to(x0, y0, x0, y0, x0 + corner_radius, y0);
  _ctx->close_path();

  _ctx->set_source_rgb(1, 1, 0.9);
  _ctx->fill_preserve();

  _ctx->set_line_width(.005);
  _ctx->set_source_rgb(0, 0, 0);

  _ctx->stroke();
  _ctx->restore();
}


void
CairoElements::draw_expandable(const Rectangle& area, bool expanded)
{
  using namespace Cairo;
  
  const float& x0 = area.x0;
  const float& y0 = area.y0;
  const float& w = area.w;
  const float& h = area.h;

  _ctx->save();

  _ctx->set_line_width(.005);
  _ctx->set_source_rgb(1, 1, 1);

  _ctx->begin_new_path();
  _ctx->rectangle(x0, y0, w, h);
  _ctx->set_source_rgb(1, 1, 1);
  _ctx->fill_preserve();
  _ctx->set_source_rgb(0, 0, 0);
  _ctx->stroke();

  _ctx->move_to(x0 + w / 10, y0 + h / 2);
  _ctx->line_to(x0 + w*9 / 10, y0 + h / 2);
  _ctx->stroke();

  if (!expanded) // The "-" becomes a "+"
  {
    _ctx->move_to(x0 + w / 2, y0 + h / 10);
    _ctx->line_to(x0 + w / 2, y0 + h*9 / 10);
    _ctx->stroke();
  }

  _ctx->restore();
}

