/*
    zchart.c - XY charts for debugging
    Copyright (C) 2014-2020 Ladislav Vaiz <ok1zia@nagano.cz>

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

*/

#include <zchart.h>

#ifdef Z_HAVE_SDL

#include <zdebug.h>
#include <zmisc.h>
#include <zsdl.h>
#include <ztime.h>

#include <math.h>

static struct zzsdl *zsdl;

struct zchart *zchart_init(SDL_Surface *surface, SDL_Rect *area, int bgcolor){
	zsdl = zsdl_instance();

    struct zchart *chart = g_new0(struct zchart, 1);
	chart->surface = surface;
	memcpy(&chart->area, area, sizeof(SDL_Rect));
	chart->bgcolor = bgcolor;
	chart->sets = g_ptr_array_new();
	chart->mx = area->x + area->w - 1;
	chart->my = 0;
    return chart;
}

void zchart_free(struct zchart *chart){
	int i;

	for (i = 0; i < (int)chart->sets->len; i++){
		struct zchart_set *set = (struct zchart_set*)g_ptr_array_index(chart->sets, i);
		g_free(set->desc);
		g_free(set->fmt);
		g_array_free(set->values, TRUE);
	}
    g_free(chart);
}

void zchart_clear(struct zchart *chart){
	int i;

	for (i = 0; i < (int)chart->sets->len; i++){
		struct zchart_set *set = (struct zchart_set*)g_ptr_array_index(chart->sets, i);
		g_array_set_size(set->values, 0);
		set->minx = set->miny = DBL_MAX;
		set->maxx = set->maxy = DBL_MIN;
	}
}

struct zchart_set *zchart_add_set(struct zchart *chart, char *desc, int color){
	struct zchart_set *set = g_new0(struct zchart_set, 1);
	set->chart = chart;
	set->desc = g_strdup(desc);
	set->color = color;
	set->values = g_array_sized_new(FALSE, FALSE, sizeof(struct zchart_val), 1024);
	set->minx = set->miny = DBL_MAX;
	set->maxx = set->maxy = DBL_MIN;
	g_ptr_array_add(chart->sets, set);
	return set;
}

void zchart_add(struct zchart *chart, int setnr, double x, double y){
	struct zchart_val val;
	struct zchart_set *set;

	if (setnr < 0 || setnr >= (int)chart->sets->len) setnr = chart->sets->len - 1;
	if (setnr < 0) zchart_add_set(chart, "Some data", z_makecol(255, 255, 255));
	
	set = (struct zchart_set*)g_ptr_array_index(chart->sets, setnr);

	val.x = x;
	val.y = y;
	g_array_append_val(set->values, val);
	if (z_finite(x)){
		if (x < set->minx) set->minx = x;
		if (x > set->maxx) set->maxx = x;
	}
	if (z_finite(y)){
		if (y < set->miny) set->miny = y;
		if (y > set->maxy) set->maxy = y;
	}
};



void zchart_redraw(struct zchart *chart){
	int setnr;
	int crosscolor = z_makecol(96, 96, 96);
	
	SDL_FillRect(chart->surface, &chart->area, chart->bgcolor);
	z_line(chart->surface, chart->area.x, chart->my, chart->area.x + chart->area.w - 1, chart->my, crosscolor);
	z_line(chart->surface, chart->mx, chart->area.y, chart->mx, chart->area.y + chart->area.h - 1, crosscolor);
	
	for (setnr = 0; setnr < (int)chart->sets->len; setnr++){
		int i, oldx = 0, oldy = 0;
		double oldd = NAN;
		struct zchart_set *set = (struct zchart_set*)g_ptr_array_index(chart->sets, setnr);
		
		for (i = 0; i < (int)set->values->len; i++){
			int x, y;
			struct zchart_val val;
	
			val = g_array_index(set->values, struct zchart_val, i);
			x = chart->area.x + (int)((val.x - set->minx) * (chart->area.w - 1) / (set->maxx - set->minx));
			y = chart->area.y + chart->area.h - 1 - (int)((val.y - set->miny) * (chart->area.h - 1) / (set->maxy - set->miny));
			if (z_finite(val.x) && z_finite(val.y) && z_finite(oldd)){
				z_line(chart->surface, oldx, oldy, x, y, set->color);
				//dbg("setnr=%d %d %d   %d %d\n", setnr, oldx, x, oldy, y);
			}
			oldx = x;
			oldy = y;
			oldd = 0.0;
			if (!z_finite(val.x)) oldd = val.x;
			if (!z_finite(val.y)) oldd = val.y;
		} 
	}

	for (setnr = 0; setnr < (int)chart->sets->len; setnr++){
		int x, y, i, j, tw, th;
		char s[256];
		double a, b, c, d;
		struct zchart_val val;
		GString *gs = g_string_sized_new(256);
		GString *gs2 = g_string_sized_new(256);
		struct zchart_set *set = (struct zchart_set*)g_ptr_array_index(chart->sets, setnr);

		val.x = (chart->mx - chart->area.x) * (set->maxx - set->minx) / (chart->area.w - 1) + set->minx;
		val.y = (chart->area.y + chart->area.h - 1 - chart->my) * (set->maxy - set->miny) / (chart->area.h - 1) +  set->miny;

		y = chart->area.y + chart->area.h - 1 - (int)((val.y - set->miny) * (chart->area.h - 1) / (set->maxy - set->miny));
	
		g_string_append_printf(gs, "%s ", set->desc);
		a = set->maxx;
		if (a < 0) a *= 10;
		b = set->minx;
		if (b < 0) b *= 10;
		c = Z_MAX(Z_ABS(a), Z_ABS(b));
		c = log10(c);
		if (c < 0) {
			d = -c + 4;
			c = 1;
		}else{
			c = ceil(c);
			d = 4 - c;
			if (d < 0) d = 0;
		}
		if (d > 0) c++;
		g_snprintf(s, sizeof(s), "%%%d.%df", (int)(c + d), (int)d);
		g_string_append_printf(gs, s, val.x);

		a = set->maxy;
		if (a < 0) a *= 10;
		b = set->miny;
		if (b < 0) b *= 10;
		c = Z_MAX(Z_ABS(a), Z_ABS(b));
		c = log10(c);
		if (c < 0) {
			d = -c + 4;
			c = 1;
		}else{
			c = ceil(c);
			d = 4 - c;
			if (d < 0) d = 0;
		}
		if (d > 0) c++;
		g_snprintf(s, sizeof(s), " %%%d.%df", (int)(c + d), (int)d);
		g_string_append_printf(gs, s, val.y);
		
		 
		g_string_append_printf(gs2, gs->str, val.x, val.y);
		tw = gs2->len * zsdl->font_w;
		th = zsdl->font_h;
		
		for (x = chart->area.x; x < chart->area.x + chart->area.w - tw; x += zsdl->font_w){
			for (y = chart->area.y; y < chart->area.y + chart->area.h - th; y += zsdl->font_h){
				for (i = 0; i < tw; i++){
					for (j = 0; j < th; j++){
						int c = z_getpixel(chart->surface, x + i, y + j);
						if (c != chart->bgcolor && c != crosscolor){
							y += j;
							goto occupied;
						}

					}
				}
				// free space
				zsdl_printf(chart->surface, x, y, set->color, 0, ZFONT_TRANSP, "%s", gs->str);
				goto drawn;
				occupied:;
			}
		}
drawn:;		
		g_string_free(gs, TRUE);
		g_string_free(gs2, TRUE);
	}
  
}

void zchart_mouse(struct zchart *chart, int mx, int my){
	if (!chart) return;

	if (mx < chart->area.x) mx = chart->area.x;
	if (mx >= chart->area.x + chart->area.w) mx = chart->area.x + chart->area.w - 1;
	if (my < chart->area.y) my = chart->area.y;
	if (my >= chart->area.y + chart->area.h) my = chart->area.y + chart->area.h - 1;

	chart->mx = mx;
	chart->my = my;
}



#endif

