/*  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 "symmetry.h"

#include <glib.h>

struct _VisualID_SymmetryClass {
  VisualID_GlyphClass parent_class;
};

G_DEFINE_TYPE (VisualID_Symmetry, visualid_symmetry, VISUALID_TYPE_GLYPH)

static void
exec_symmetry (VisualID_Symmetry *symmetry, cairo_t *cr)
{
  double x, y;
  int i;

  if (!symmetry->child) {
    /* We might actually be without child,
       due to a complexity-limiter kicking in....
    */
    return;
  }

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

  cairo_move_to (cr, 0, 0);

  cairo_save (cr);

  for (i = 0; i < symmetry->n; i++) {
    cairo_save (cr);

    cairo_rotate (cr, 2*M_PI / symmetry->n * (i + .5));
    cairo_translate (cr, 0, symmetry->offset);
    cairo_scale (cr, symmetry->cscale, symmetry->cscale);
    if (i % 2) {
      cairo_scale (cr, -1, 1);
    }
    visualid_draw_path (symmetry->child, cr);

    cairo_restore (cr);
  }

  cairo_restore (cr);

  cairo_new_sub_path (cr);
  cairo_move_to (cr, x, y);
}

static void
symmetry_constructed (GObject *self)
{
  VisualID_Symmetry *symmetry = VISUALID_SYMMETRY (self);
  VisualID_Glyph *glyph = VISUALID_GLYPH (self);
  GObjectClass *parent_class = G_OBJECT_CLASS (visualid_symmetry_parent_class);

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

  /* XXX: this is described somewhat ambiguously in the essay,
     in terms of an undefined `rndinLevelLowbias' function: */
  symmetry->n =
    pow (2, round (virand_range_biased (glyph->generator->state,
                                        1, 3, glyph->recursion_level + 2)));
  /* ^note that level starts at 0, which would mean that top-level
     symmetry-generators always use n=8; using level+1 would start
     counting at 1, which doesn't actually impose a low bias;
     so, start counting at 2. */

  if (virand_prob (glyph->generator->state, 0.3)) {
    symmetry->offset = g_rand_double_range (glyph->generator->state, 0.1, 0.5);
  }

  symmetry->cscale = g_rand_double_range (glyph->generator->state, 0.3, 0.6)
    * (1 - symmetry->offset);
}

static gboolean
symmetry_extend (VisualID_Glyph *glyph, int atlevel)
{
  VisualID_Symmetry *symmetry = VISUALID_SYMMETRY (glyph);

  gboolean extended = FALSE;

  /* Note the special behaviour for atlevel=0, even though Symmetry
     glyphs have no base complexity of their own: */

  if (atlevel) {
    if (symmetry->child) {
      /* Symmetry always *tries* to spawn a child,
         but might actually be without child,
         due to a complexity-limiter kicking in....
      */
      extended |= visualid_glyph_extend (symmetry->child, atlevel-1);
    }
  } else {
    if (visualid_glyph_spawn (glyph, "child-glyph")) {
      extended = TRUE;
    }
  }

  return extended;
}

static void
symmetry_dispose (GObject *self)
{
  VisualID_Symmetry *symmetry = VISUALID_SYMMETRY (self);

  if (symmetry->child) {
    visualid_glyph_unparent (symmetry->child);
    symmetry->child = NULL;
  }

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

enum {
  PROP_CHILD_GLYPH = 1,
  PROP_CHILD_COUNT,
  PROP_CHILD_SCALE,
  PROP_CHILD_OFFSET
};

static void
symmetry_get_property (GObject *object,
                       guint property_id,
                       GValue *value,
                       GParamSpec *pspec)
{
  VisualID_Symmetry *self = VISUALID_SYMMETRY (object);

  switch (property_id)
    {
    case PROP_CHILD_GLYPH:
      g_value_set_object (value, self->child);
      break;
    case PROP_CHILD_COUNT:
      g_value_set_int (value, self->n);
      break; 
    case PROP_CHILD_SCALE:
      g_value_set_double (value, self->cscale);
      break;
    case PROP_CHILD_OFFSET:
      g_value_set_double (value, self->offset);
      break;
 
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
symmetry_set_property (GObject *object,
                       guint property_id,
                       const GValue *value,
                       GParamSpec *pspec)
{
  VisualID_Symmetry *self = VISUALID_SYMMETRY (object);

  switch (property_id)
    {
    case PROP_CHILD_GLYPH:
      visualid_glyph_take_child (VISUALID_GLYPH (self),
                                 g_value_get_object (value),
                                 &self->child);
      break;
    case PROP_CHILD_COUNT:
      self->n = g_value_get_int (value);
      break;
    case PROP_CHILD_SCALE:
      self->cscale = g_value_get_double (value);
      break;
    case PROP_CHILD_OFFSET:
      self->offset = g_value_get_double (value);
      break;

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

static int
visualid_symmetry_complexity (VisualID_Symmetry *symmetry, int recurse)
{
  int complexity = 0;

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

  return complexity;
}

static void
visualid_symmetry_class_init (VisualID_SymmetryClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);

  VISUALID_GLYPH_CLASS (class)->extend = symmetry_extend;
  VISUALID_GLYPH_CLASS (class)->render = (visualid_render_fn *) exec_symmetry;
  VISUALID_GLYPH_CLASS (class)->complexity_fn =
    (visualid_complexity_fn *) visualid_symmetry_complexity;

  gobject_class->get_property = symmetry_get_property;
  gobject_class->set_property = symmetry_set_property;
  gobject_class->constructed = symmetry_constructed;
  gobject_class->dispose = symmetry_dispose;

  g_object_class_install_property
    (gobject_class, PROP_CHILD_GLYPH,
     g_param_spec_object ("child-glyph",
                          "Child glyph",
                          "The glyph that should be used in the symmetry-generator",
                          VISUALID_TYPE_GLYPH,
                          G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_CHILD_COUNT,
     g_param_spec_int ("child-count",
                       "Child count",
                       "The number of mirrored glyphs",
                       2, 8, 2,
                       G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_CHILD_SCALE,
     g_param_spec_double ("child-scale",
                          "Child scale",
                          "Relative scale of child-glyphs",
                          .01, 1, .03,
                          G_PARAM_READWRITE));

  g_object_class_install_property
    (gobject_class, PROP_CHILD_OFFSET,
     g_param_spec_double ("child-offset",
                          "Child offset",
                          "Distance by which glyphs are offset from centre",
                          0, .5, .3,
                          G_PARAM_READWRITE));
}

static void
visualid_symmetry_init (VisualID_Symmetry *self)
{
}

