/*  Copyright (C) 2008--2010 Joshua Judson Rosen <rozzin@geekspace.com>.

    This program is free software: you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 3 as published by the Free Software Foundation.

    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; see the file COPYING. If not, see
    <http://www.gnu.org/licenses/> or write to:

        The Free Software Foundation, inc.
        51 Franklin Street, Fifth Floor
        Boston, MA 02110-1301
        USA
*/

#include <stdlib.h>

#include <glib/grand.h>
#include "virand/random.h"

#include <math.h>
#include <cairo.h>

#include <glib-object.h>
#include "graphics.h"
#include "radial.h"

#include <glib.h>

struct _VisualID_RadialClass {
  VisualID_GlyphClass parent_class;
};

G_DEFINE_TYPE (VisualID_Radial, visualid_radial, VISUALID_TYPE_GLYPH)

static void
exec_radial (VisualID_Radial *radial, cairo_t *cr)
{
  double x, y;
  int i;

  cairo_get_current_point (cr, &x, &y);
  cairo_new_sub_path (cr);

  cairo_save (cr);

  for (i = -1;
       i < radial->n;
       i++)
    {
      double angle = M_PI*2 * i/radial->n; 

      double vx = -sin (angle) * .6, vy = cos (angle) * .6;

      cairo_line_to (cr, vx, vy);

      if (i >= 0 && radial->vertex_child) {
        if (angle > radial->angle_cutoff &&
            (M_PI*2 - angle) > radial->angle_cutoff)
          {
            continue;
          }

        cairo_save (cr);

        cairo_translate (cr, vx, vy);
        cairo_rotate (cr, angle);
        cairo_scale (cr, radial->vc_scale, radial->vc_scale);

        visualid_draw_path (radial->vertex_child, cr);

        cairo_restore (cr);
      }
    }

  cairo_close_path (cr);

  if (radial->eye_child) {
    cairo_new_sub_path (cr);
    cairo_move_to (cr, 0, 0);

    cairo_save (cr);
    cairo_translate (cr, -(radial->eye_sep)/2, radial->eye_org);
    cairo_scale (cr, radial->ec_scale, radial->ec_scale);
    visualid_draw_path (radial->eye_child, cr);
    cairo_restore (cr);

    cairo_save (cr);
    cairo_translate (cr, radial->eye_sep/2, radial->eye_org);
    cairo_scale (cr, radial->ec_scale, radial->ec_scale);
    visualid_draw_path (radial->eye_child, cr);
    cairo_restore (cr);
  }

  if (radial->mouth_child) {
    cairo_new_sub_path (cr);
    cairo_move_to (cr, 0, 0);

    cairo_save (cr);
    cairo_translate (cr, 0, radial->mouth_org);
    cairo_scale (cr, radial->mc_scale, radial->mc_scale);
    visualid_draw_path (radial->mouth_child, cr);
    cairo_restore (cr);
  }

  cairo_move_to (cr, x, y);

  cairo_restore (cr);
}

static void
radial_constructed (GObject *self)
{
  VisualID_Radial *rg = VISUALID_RADIAL (self);
  VisualID_Glyph *glyph = VISUALID_GLYPH (self);
  GObjectClass *parent_class = G_OBJECT_CLASS (visualid_radial_parent_class);

  if (parent_class->constructed) {
    parent_class->constructed (self);
  }

  rg->n = (int) virand_range_biased (glyph->generator->state, 3, 12, 2);

  if ((rg->spawn_vertex = virand_prob (glyph->generator->state, 0.5))) {
    rg->vc_scale = g_rand_double_range (glyph->generator->state, 0.15, 0.7);
  }

  if ((rg->spawn_mouth = virand_prob (glyph->generator->state, 0.6))) {
    rg->mc_scale = g_rand_double_range (glyph->generator->state, 0.1, 0.3);
  }

  if ((rg->spawn_eye = virand_prob (glyph->generator->state, 0.3))) {
    rg->ec_scale = g_rand_double_range (glyph->generator->state, 0.1, 0.4);
    rg->eye_sep = g_rand_double_range (glyph->generator->state, 0.2, 0.3);
    rg->eye_org = g_rand_double_range (glyph->generator->state, 0.2, 0.3);
    rg->mouth_org = g_rand_double_range (glyph->generator->state, -0.2, -0.3);
  }

  if (virand_prob (glyph->generator->state, 0.5)) {
    rg->angle_cutoff = g_rand_double_range (glyph->generator->state,
                                            M_PI/2, M_PI/1.2);
  } else {
    rg->angle_cutoff = M_PI;
  }
}

static gboolean
radial_extend (VisualID_Glyph *glyph, int atlevel)
{
  VisualID_Radial *rg = VISUALID_RADIAL (glyph);

  gboolean extended = FALSE;

  if (atlevel) { /* May be < 0, which means keep going..., OK? */
    if (rg->vertex_child) {
      extended |= visualid_glyph_extend (rg->vertex_child, atlevel-1);
    }

    if (rg->mouth_child) {
      extended |= visualid_glyph_extend (rg->mouth_child, atlevel-1);
    }

    if (rg->eye_child) {
      extended |= visualid_glyph_extend (rg->eye_child, atlevel-1);
    }

    return extended;
  }

  if (rg->spawn_vertex) {
    if (visualid_glyph_spawn (glyph, "vertex-child")) {
      extended = TRUE;
    }
  }

  if (rg->spawn_mouth) {
    if (visualid_glyph_spawn (glyph, "mouth-child")) {
      extended = TRUE;
    }
  }

  if (rg->spawn_eye) {
    if (visualid_glyph_spawn (glyph, "eye-child")) {
      extended = TRUE;
    }
  }

  return extended;
}

static void
radial_dispose (GObject *self)
{
  VisualID_Radial *radial = VISUALID_RADIAL (self);

  if (radial->mouth_child) {
    visualid_glyph_unparent (radial->mouth_child);
    radial->mouth_child = NULL;
  }

  if (radial->eye_child) {
    visualid_glyph_unparent (radial->eye_child);
    radial->eye_child = NULL;
  }

  if (radial->vertex_child) {
    visualid_glyph_unparent (radial->vertex_child);
    radial->vertex_child = NULL;
  }

  G_OBJECT_CLASS (visualid_radial_parent_class)->dispose (self);
}

enum {
  PROP_VERTEX_COUNT = 1,
  PROP_VERTEX_CHILD,
  PROP_VERTEX_SCALE,
  PROP_VERTEX_ANGLE_CUTOFF,

  PROP_EYE_CHILD,
  PROP_EYE_SCALE,
  PROP_EYE_ORIGIN,
  PROP_EYE_SEPARATION,

  PROP_MOUTH_CHILD,
  PROP_MOUTH_SCALE,
  PROP_MOUTH_POS
};

static void
radial_get_property (GObject *object,
                     guint property_id,
                     GValue *value,
                     GParamSpec *pspec)
{
  VisualID_Radial *self = VISUALID_RADIAL (object);

  switch (property_id)
    {
    case PROP_VERTEX_CHILD:
      g_value_set_object (value, self->vertex_child);
      break;
    case PROP_VERTEX_COUNT:
      g_value_set_int (value, self->n);
      break;
    case PROP_VERTEX_SCALE:
      g_value_set_double (value, self->vc_scale);
      break;
    case PROP_VERTEX_ANGLE_CUTOFF:
      g_value_set_double (value, self->angle_cutoff);
      break;

    case PROP_MOUTH_CHILD:
      g_value_set_object (value, self->mouth_child);
      break;
    case PROP_MOUTH_POS:
      g_value_set_double (value, self->mouth_org);
      break;
    case PROP_MOUTH_SCALE:
      g_value_set_double (value, self->mc_scale);
      break;

    case PROP_EYE_CHILD:
      g_value_set_object (value, self->eye_child);
      break;
    case PROP_EYE_ORIGIN:
      g_value_set_double (value, self->eye_org);
      break;
    case PROP_EYE_SEPARATION:
      g_value_set_double (value, self->eye_sep);
      break;
    case PROP_EYE_SCALE:
      g_value_set_double (value, self->ec_scale);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
radial_set_property (GObject *object,
                     guint property_id,
                     const GValue *value,
                     GParamSpec *pspec)
{
  VisualID_Radial *self = VISUALID_RADIAL (object);

  switch (property_id)
    {
    case PROP_VERTEX_CHILD:
      visualid_glyph_take_child (VISUALID_GLYPH (self),
                                 g_value_get_object (value),
                                 &self->vertex_child);
      break;
    case PROP_VERTEX_COUNT:
      self->n = g_value_get_int (value);
      break;
    case PROP_VERTEX_SCALE:
      self->vc_scale = g_value_get_double (value);
      break;
    case PROP_VERTEX_ANGLE_CUTOFF:
      self->angle_cutoff = g_value_get_double (value);
      break;

    case PROP_MOUTH_CHILD:
      visualid_glyph_take_child (VISUALID_GLYPH (self),
                                 g_value_get_object (value),
                                 &self->mouth_child);
      break;
    case PROP_MOUTH_SCALE:
      self->mc_scale = g_value_get_double (value);
      break;
    case PROP_MOUTH_POS:
      self->mouth_org = g_value_get_double (value);
      break;

    case PROP_EYE_CHILD:
      visualid_glyph_take_child (VISUALID_GLYPH (self),
                                 g_value_get_object (value),
                                 &self->eye_child);
      break;
    case PROP_EYE_ORIGIN:
      self->eye_org = g_value_get_double (value);
      break;
    case PROP_EYE_SEPARATION:
      self->eye_sep = g_value_get_double (value);
      break;
    case PROP_EYE_SCALE:
      self->ec_scale = g_value_get_double (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static int
visualid_radial_complexity (VisualID_Radial *radial, int recurse)
{
  int complexity = radial->n;

  if (recurse--) {
    if (radial->eye_child) {
      complexity += 2 * visualid_glyph_complexity (radial->eye_child, recurse);
    }

    if (radial->vertex_child) {
      complexity += (visualid_glyph_complexity (radial->vertex_child, recurse)
                     * radial->n);
    }

    if (radial->mouth_child) {
      complexity += visualid_glyph_complexity (radial->mouth_child, recurse);
    }
  }

  return complexity;
}

static void
visualid_radial_class_init (VisualID_RadialClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);

  VISUALID_GLYPH_CLASS (class)->extend = radial_extend;
  VISUALID_GLYPH_CLASS (class)->render = (visualid_render_fn *) exec_radial;
  VISUALID_GLYPH_CLASS (class)->complexity_fn =
    (visualid_complexity_fn *) visualid_radial_complexity;

  gobject_class->get_property = radial_get_property;
  gobject_class->set_property = radial_set_property;
  gobject_class->constructed = radial_constructed;
  gobject_class->dispose = radial_dispose;

  g_object_class_install_property
    (gobject_class, PROP_VERTEX_COUNT,
     g_param_spec_int ("vertex-count",
                       "Vertex count",
                       "The number of vertices in the polygon",
                       3, 12, 7,
                       G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_VERTEX_SCALE,
     g_param_spec_double ("vertex-scale",
                          "Vertex scale",
                          "Relative scale of vertices",
                          .01, 1.0, .25,
                          G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_VERTEX_CHILD,
     g_param_spec_object ("vertex-child",
                          "Vertex child",
                          "Child-glyph used at vertices",
                          VISUALID_TYPE_GLYPH,
                          G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_VERTEX_ANGLE_CUTOFF,
     g_param_spec_double ("vertex-angle-cutoff",
                          "Vertex angle-cuttoff",
                          "The maximum angle from the top at which vertex-children may appear",
                          M_PI/4,
                          3.15,
                          M_PI/2.1,
                          G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_EYE_CHILD,
     g_param_spec_object ("eye-child",
                          "Eye child",
                          "Child-glyph used as eyes",
                          VISUALID_TYPE_GLYPH,
                          G_PARAM_READWRITE));
  g_object_class_install_property
    (gobject_class, PROP_EYE_SCALE,
     g_param_spec_double ("eye-scale",
                          "Eye scale",
                          "Relative scale of eye-glyphs",
                          0.01, 1.0, .25,
                          G_PARAM_READWRITE));
  g_object_class_install_property
    (gobject_class, PROP_EYE_ORIGIN,
     g_param_spec_double ("eye-origin",
                          "Eye origin",
                          "The offset of the eye-glyphs up from the centre",
                          0, .5, 0,
                          G_PARAM_READWRITE));
  g_object_class_install_property
    (gobject_class, PROP_EYE_SEPARATION,
     g_param_spec_double ("eye-separation",
                          "Eye separation",
                          "The separation between eye-glyphs",
                          .1, .5, .1,
                          G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_MOUTH_CHILD,
     g_param_spec_object ("mouth-child",
                          "Mouth child",
                          "Child-glyph used as a mouth",
                          VISUALID_TYPE_GLYPH,
                          G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_MOUTH_SCALE,
     g_param_spec_double ("mouth-scale",
                          "Mouth scale",
                          "Relative scale of mouth-glyph",
                          .01, 1.0, .25,
                          G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_MOUTH_POS,
     g_param_spec_double ("mouth-origin",
                          "Mouth origin",
                          "Offset of mouth-glyph down from centre",
                          -.5, 0, 0,
                          G_PARAM_READWRITE));
}

static void
visualid_radial_init (VisualID_Radial *self)
{
}

