/* This is -*- C -*- */
/* vim: set sw=2: */

/*
 * core.c
 *
 * Copyright (C) 2000 EMC Capital Management, Inc.
 * Copyright (C) 2001 The Free Software Foundation
 *
 * Developed by Jon Trowbridge <trow@gnu.org>
 *
 * This program 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 2 of the
 * License, or (at your option) any later version.
 *
 * 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; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */


#include <config.h>
#include "guppi-date-series-core.h"

#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>

#include <math.h>
#include <gnan.h>
#include <guppi-convenient.h>
#include <guppi-memory.h>

static GtkObjectClass *parent_class = NULL;

static void
guppi_date_series_core_finalize (GtkObject *obj)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (obj);

  guppi_unref0 (core->garray);

  if (parent_class->finalize)
    parent_class->finalize (obj);
}

/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

static void
ensure_contains (GuppiDateSeriesCore *core, const GDate *dt)
{
  double *data;
  gint i;

  if (core->size == 0) {

    guppi_garray_set_size (core->garray, 186);
    core->size = 1;
    core->start_date = *dt;
    core->end_date = *dt;
    data = (double *)guppi_garray_data (core->garray);
    data[0] = G_NAN;
    return;
  }

  i = (gint)g_date_julian ((GDate *) dt) - (gint)g_date_julian (&core->start_date);

  if (0 <= i && i < core->size)
    return;

  if (i < 0) {
    gint new_size, j;

    new_size = core->size - i;
    
    if (guppi_garray_size (core->garray) < new_size)
      guppi_garray_set_size (core->garray, new_size);

    data = (double *)guppi_garray_data (core->garray);

    for (j = core->size-1; j >= 0; --j)
      data[j-i] = data[j];
    for (j = 0; j<-i; ++j)
      data[j] = G_NAN;

    core->size = new_size;
    core->start_date = *dt;

    return;
  }

  if (i >= core->size) {
    gint new_size, j;

    new_size = i+1;

    if (guppi_garray_size (core->garray) < new_size)
      guppi_garray_set_size (core->garray, new_size);

    data = (double *)guppi_garray_data (core->garray);

    for (j = core->size; j < new_size; ++j)
      data[j] = G_NAN;

    core->size = new_size;
    core->end_date = *dt;

    return;
  }

  g_assert_not_reached ();
}

static GuppiData *
v_data_copy (GuppiData *d)
{
  g_assert_not_reached ();
  return NULL;
}

static gint
v_data_get_size_in_bytes (GuppiData *d)
{
  return -1;
}

static void
v_di_bounds (GuppiDateIndexed *di, GDate *start, GDate *end)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (di);

  if (start)
    *start = core->start_date;
  if (end)
    *end = core->end_date;
}

static gboolean
v_di_valid (GuppiDateIndexed *di, const GDate *dt)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (di);
  gint i;
  double *data;

  data = (double *)guppi_garray_data (core->garray);
  i = (gint)g_date_julian ((GDate *)dt) - (gint)g_date_julian(&core->start_date);

  return !g_isnan (data[i]);
}

static gboolean
v_di_step (GuppiDateIndexed *di, const GDate *dt,
	   gint delta, GDate *modified)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (di);
  gint i, N;
  double *data;

  data = (double *)guppi_garray_data (core->garray);
  i = (gint)g_date_julian ((GDate *)dt) - (gint)g_date_julian(&core->start_date);
  N = (gint)g_date_julian (&core->end_date) - (gint)g_date_julian(&core->start_date);

  while (delta && 0 <= i && i <= N) {
    
    if (!g_isnan (data[i])) {
      if (delta > 0)
	--delta;
      else 
	++delta;
    }
    
    i += (delta > 0 ? 1 : -1);
  }

  return delta == 0;
}

static gint
v_di_size (GuppiDateIndexed *di)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (di);
  gint i, N, count;
  double *data;

  data = (double *)guppi_garray_data (core->garray);
  N = (gint)g_date_julian (&core->end_date) - (gint)g_date_julian(&core->start_date);
  count = 0;

  for (i=0; i<=N; ++i)
    if (!g_isnan (data[i]))
      ++count;

  return count;
}

static void
v_di_bounds_hint (GuppiDateIndexed *di,
		  const GDate *start, const GDate *end)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (di);

  ensure_contains (core, (GDate *)start);
  ensure_contains (core, (GDate *)end);
}

static double
v_ds_get (GuppiDateSeries *ser, const GDate *dt)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (ser);
  double *data;
  gint i;

  data = (double *)guppi_garray_data (core->garray);
  i = (gint)g_date_julian ((GDate *) dt) - (gint)g_date_julian (&core->start_date);

  return data[i];
}

static void
v_ds_set (GuppiDateSeries *ser, const GDate *dt, double x)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (ser);
  double *data;
  gint i;

  ensure_contains (core, dt);

  data = (double *)guppi_garray_data (core->garray);
  i = (gint)g_date_julian ((GDate *) dt) - (gint)g_date_julian (&core->start_date);

  data[i] = x;
}

static void
v_ds_unset (GuppiDateSeries *ser, const GDate *dt)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (ser);
  double *data;
  gint i;

  data = (double *)guppi_garray_data (core->garray);
  i = (gint)g_date_julian ((GDate *) dt) - (gint)g_date_julian (&core->start_date);

  if (0 <= i && i < core->size)
    data[i] = G_NAN;
}

static gboolean
v_ds_get_many (GuppiDateSeries *ser, const GDate *dt, gint count, double *buf,
	       gint *retval)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (ser);
  double *data;
  gint i, j;
  
  data = (double *)guppi_garray_data (core->garray);
  i = (gint)g_date_julian ((GDate *) dt) - (gint)g_date_julian (&core->start_date);
  j = 0;

  while (count && 0 <= i && i < core->size) {
    if (!g_isnan (data[i])) {
      buf[j] = data[i];
      ++j;
      if (count > 0)
	--count;
      else
	++count;
    }
    
    if (count > 0)
      ++i;
    else 
      --i;
  }

  if (retval)
    *retval = j;

  return TRUE;
}

static gboolean
v_ds_get_range (GuppiDateSeries *ser, const GDate *sd, const GDate *ed,
		double *timecode, double *buf, gint bufsize, gint *retval)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (ser);
  double *data;
  gint i, N, j, jul;

  data = (double *)guppi_garray_data (core->garray);
  jul = (gint)g_date_julian (&core->start_date);
  i = (gint)g_date_julian ((GDate *) sd) - jul;
  N = (gint)g_date_julian ((GDate *) ed) - jul;
  j = 0;

  while (j < bufsize && 0 <= i && i <= N) {
    if (!g_isnan (data[i])) {
      if (timecode)
	timecode[j] = jul + i;
      if (buf)
	buf[j] = data[i];
      ++j;
    }
    ++i;
  }

  if (retval)
    *retval = j;

  return TRUE;
}

static gboolean
v_ds_get_bounds (GuppiDateSeries *ser, const GDate *sd, const GDate *ed,
		 double *min, double *max, gboolean *retval)
{
  GuppiDateSeriesCore *core = GUPPI_DATE_SERIES_CORE (ser);
  double *data;
  gint i, N;
  double m = 0, M = 0;
  gboolean first = TRUE;

  data = (double *)guppi_garray_data (core->garray);
  i = (gint)g_date_julian ((GDate *) sd) - (gint)g_date_julian (&core->start_date);
  N = (gint)g_date_julian ((GDate *) ed) - (gint)g_date_julian (&core->start_date);

  for (i=0; i<=N; ++i) {
    if (!g_isnan (data[i])) {
      if (first) {
	m = M = data[i];
	first = FALSE;
      } else {
	double x = data[i];
	if (x < m) m = x;
	if (x > M) M = x;
      }
    }
  }

  if (min) *min = m;
  if (max) *max = M;

  if (retval)
    *retval = !first;

  return TRUE;
}

/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

static void
guppi_date_series_core_class_init (GuppiDateSeriesCoreClass *klass)
{
  double d;
  GtkObjectClass* object_class = (GtkObjectClass *)klass;
  GuppiDataClass *data_class = GUPPI_DATA_CLASS (klass);
  GuppiDateIndexedClass *di_class = GUPPI_DATE_INDEXED_CLASS (klass);
  GuppiDateSeriesClass *ds_class = GUPPI_DATE_SERIES_CLASS (klass);

  parent_class = gtk_type_class (GUPPI_TYPE_DATE_SERIES);

  object_class->finalize = guppi_date_series_core_finalize;

  data_class->copy = v_data_copy;
  data_class->get_size_in_bytes = v_data_get_size_in_bytes;
  data_class->is_leaf_type = TRUE;

  di_class->bounds = v_di_bounds;
  di_class->valid = v_di_valid;
  di_class->step = v_di_step;
  di_class->size = v_di_size;
  di_class->bounds_hint = v_di_bounds_hint;

  ds_class->get = v_ds_get;
  ds_class->set = v_ds_set;
  ds_class->unset = v_ds_unset;
  ds_class->get_many = v_ds_get_many;
  ds_class->get_range = v_ds_get_range;
  ds_class->get_bounds = v_ds_get_bounds;

  d = G_NAN;
  g_assert (g_isnan (d));
}

static void
guppi_date_series_core_init (GuppiDateSeriesCore *obj)
{
  g_date_clear (&obj->start_date, 1);
  g_date_clear (&obj->end_date, 1);
  obj->size = 0;
  obj->garray = guppi_garray_new (sizeof (double));
}

GtkType
guppi_date_series_core_get_type (void)
{
  static GtkType guppi_date_series_core_type = 0;
  if (!guppi_date_series_core_type) {
    static const GtkTypeInfo guppi_date_series_core_info = {
      "GuppiDateSeriesCore",
      sizeof (GuppiDateSeriesCore),
      sizeof (GuppiDateSeriesCoreClass),
      (GtkClassInitFunc)guppi_date_series_core_class_init,
      (GtkObjectInitFunc)guppi_date_series_core_init,
      NULL, NULL, (GtkClassInitFunc)NULL
    };
    guppi_date_series_core_type = gtk_type_unique (GUPPI_TYPE_DATE_SERIES, &guppi_date_series_core_info);
  }
  return guppi_date_series_core_type;
}

GuppiDateSeries *
guppi_date_series_core_new (void)
{
  return GUPPI_DATE_SERIES (guppi_type_new (guppi_date_series_core_get_type ()));
}


syntax highlighted by Code2HTML, v. 0.9.1