/*
    chart - chart compare of points progress
    Copyright (C) 2011-2023 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.

	Not Y2K1 compliant, date_str is taken without century
*/


#include "header.h"

#include "bfu.h"
#include "chart.h"
#include "fifo.h"
#include "hf.h"
#include "html.h"
#include "kbdbind.h"
#include "main.h"
#include "map.h"
#include "menu.h"
#include "tsdl.h"
#include "session.h"
#include "terminal.h"

#define TOP 20
#define BOTTOM (FONT_H + 4)
#define LEFT 13
#define RIGHT 20

static void chart_import_edi(void *xxx, char *filename){
	char *fname;
	fname = g_strdup(filename);
	z_filename(fname);
	if (z_strcasestr(fname, "TXT")){
		zg_ptr_array_foreach(struct band *, b, ctest->bands){
			char *c, *path, *f;
			c = g_strdup(filename);
			path = z_dirname(c);
			f = g_strdup_printf("%s/%c.txt", path, z_char_lc(b->bandchar));
			z_wokna(f);
			sw_chart_load_file(gses->ontop, f, 1);
			g_free(c);
			g_free(f);
		}
	}else{
		sw_chart_load_file(gses->ontop, filename, 0);
	}
	g_free(fname);
    
    sw_chart_recalc_extremes(gses->ontop, aband);
}

void sw_chart_choose(struct menu *menu){
	int i;
	char *filename;

	progress(VTEXT(T_LOADING_CHARTS));
	for (i = 0; i < menu->ni; i++){
		struct menu_item *mi = menu->items + i;
		if (!mi->checked) continue;
		filename = g_strdup_printf("%s/%s/c.txt", logs_dir, (char *)mi->data);
		chart_import_edi(NULL, filename);
		g_free(filename);
	}
	progress(NULL);
}

void menu_chart_add_contest(void *menudata, void *itdata){
    if (ctest == NULL){
        log_addf(VTEXT(T_NO_CONTEST_OPENED2));
        return;
    }
	contest_choose(sw_chart_choose);
}
void menu_chart_add_files(void *menudata, void *itdata){
	char *pwd = getcwd(NULL, 0);
    if (
#ifdef Z_HAVE_SDL
            !sdl || 
#endif                    
            zfiledlg_open(zfiledlg, zsel, chart_import_edi, NULL, "", "txt;edi") < 0){ 
        input_field(NULL, VTEXT(T_IMPORT_EDI), VTEXT(T_FILENAME), 
                    VTEXT(T_OK), VTEXT(T_CANCEL), gses, 
                    NULL, 150, pwd, 0, 0, NULL,
                    chart_import_edi, NULL, 1);
    }
    if (pwd) free(pwd);

}


int sw_chart_kbd_func(struct subwin *sw, struct event *ev, int fw){
    struct menu_item *mi = NULL;
    int max = 0, i;
    
//    if (!sdl) return 0;

    switch (kbd_action(KM_MAIN, ev)){
        case ACT_HOME:
			menu_chart_add_files(NULL, NULL);
            return 1;
		case ACT_INSERT:
			menu_chart_add_contest(NULL, NULL);
			return 1;
        case ACT_DELETE:
            mi = NULL;
            for (i = 0; i < sw->chbands->len; i++){
                struct chband *b;
                gchar *c, *tname;

                if (!mi) mi = new_menu(3);
                if (!mi) break;

                b = (struct chband*)g_ptr_array_index(sw->chbands, i);

				tname = zstr_shorten(b->tname, 20);
                c = g_strdup_printf("%-12s %-20s %-7s", b->pcall, tname, b->pband);
				g_free(tname);
                if (strlen(c) > max) max = strlen(c);
                add_to_menu(&mi, 
                        g_strdup(c), 
                        "", "", 
                        sw_chart_delete_band, b, 0);    
                g_free(c);
            }
            set_window_ptr(gses->win, (term->x-6-max)/2,(term->y-2-i)/2);
            if (mi) do_menu(mi, NULL);
            return 1;
    }
    

    
    return 0;
}


int sw_chart_mouse_func(struct subwin *sw, struct event *ev, int fw){
#ifdef Z_HAVE_SDL
    //int lmx, lmy;

    if (!sdl) return 0;
        
    sw->mx = ev->mx - sw->x * FONT_W;
    sw->my = ev->my - sw->y * FONT_H;

//    int t = sw->mint + ((lmx - sw->ch_left) * (sw->maxt - sw->mint)) / (sw->ch_right - sw->ch_left);
//    int v = sw->minv + ((sw->ch_bottom - lmy) * (sw->maxv - sw->minv)) / (sw->ch_bottom - sw->ch_top);
    sw->gdirty = 1;
    redraw_later();
#endif
    return 0;
}



void sw_chart_redraw(struct subwin *sw, struct band *band, int flags){
    int x, y;
    gchar *cc;
    int i;
#ifdef Z_HAVE_SDL
    int j, ox, ox2, oy, oy2, t, v, bt = 0, tajm;
    SDL_Rect area;
    char s[100];
    time_t now;
    struct tm utc;
    unsigned nowd, nowt;
	int stepv;
    char *oop;

	if (sdl && sw->screen){
        time(&now);
        gmtime_r(&now, &utc);
        nowd = utc.tm_mday + 100 * (utc.tm_mon + 1 + 100 * (utc.tm_year - 100)); // no century, was + 1900
        nowt = utc.tm_min + 60 * utc.tm_hour;
		bt = nowd;
        

        if (!sdl) return;
    //    dbg("sw_chart_redraw(%d) t=%d..%d v=%d..%d\n", sw->gdirty, sw->mint, sw->maxt, sw->minv, sw->maxv);
        sw->ch_top = TOP;
        sw->ch_bottom = sw->screen->h - BOTTOM;
        if (sw->maxv >= 10000){
            sprintf(s, "%dk", sw->maxv / 1000);
        }else{
            sprintf(s, "%d", sw->maxv);
        }
        sw->ch_left = LEFT + strlen(s) * FONT_W;
        sw->ch_right = sw->screen->w - RIGHT;
        if (sw->ch_top >= sw->ch_bottom) return;
        if (sw->ch_left >= sw->ch_right) return;
        if (sw->maxt <= sw->mint) return;
        if (sw->maxv <= sw->minv) return;
        
        if (flags & HTML_FOR_PHOTO){
            sw->mx = sw->ch_right;
            sw->my = sw->ch_top;
        }

        fill_area(sw->x, sw->y, sw->w, sw->h, 0);

        area.x = 0;
        area.y = 0;
        area.w = sw->screen->w;
        area.h = sw->screen->h;
        SDL_FillRect(sw->screen, &area, z_makecol(0, 0, 0));

        z_line(sw->screen, sw->ch_left, sw->ch_top, sw->ch_left, sw->ch_bottom, sdl->gr[13]);
        z_line(sw->screen, sw->ch_left, sw->ch_bottom, sw->ch_right, sw->ch_bottom, sdl->gr[13]);
        
        ox = -10000;
        ox2 = -10000;
        i = ((sw->mint + 59) / 60) * 60;
        for (; i <= sw->maxt; i+= 60){
            x = sw->ch_left + ( (i - sw->mint) * (sw->ch_right - sw->ch_left) ) / (sw->maxt - sw->mint);
            if (x > ox + 10){
                ox = x;
                z_line(sw->screen, x, sw->ch_bottom + 1, x, sw->ch_bottom + 4, sdl->gr[13]);
            }
            if (x > ox2 + FONT_W * 4){
                ox2 = x;
                sprintf(s, "%02d", (i / 60) % 24);
                zsdl_printf(sw->screen, x - FONT_W + 1, sw->ch_bottom + 4, sdl->gr[13], 0, 0, s); 
            }
        }
        
        oy = 10000;
        oy2 = 10000;
		stepv = 100;
		if (sw->maxv > 1000)    stepv = 100;
		if (sw->maxv > 2000)    stepv = 200;
		if (sw->maxv > 5000)    stepv = 500;
		if (sw->maxv > 10000)   stepv = 1000;
		if (sw->maxv > 20000)   stepv = 2000;
		if (sw->maxv > 50000)   stepv = 5000;
		if (sw->maxv > 100000)  stepv = 10000;
		if (sw->maxv > 200000)  stepv = 20000;
		if (sw->maxv > 500000)  stepv = 50000;
		if (sw->maxv > 1000000) stepv = 100000;
        i = ((sw->minv + stepv - 1) / stepv) * stepv;
        for (; i <= sw->maxv; i+= stepv){
            y = sw->ch_bottom - ( (i - sw->minv) * (sw->ch_bottom - sw->ch_top) ) / (sw->maxv - sw->minv);
            if (y < oy - 10){
                oy = y;
                z_line(sw->screen, sw->ch_left - 1, y, sw->ch_left - 4, y, sdl->gr[13]);
            }
            if (y < oy2 - FONT_H * 2){
                oy2 = y;
				if (stepv >= 1000)
					sprintf(s, "%dk", (i / 1000));
				else
					sprintf(s, "%d", i);

                zsdl_printf(sw->screen, sw->ch_left - 4 - strlen(s) * FONT_W, y - FONT_H / 2, sdl->gr[13], 0, 0, s); 
            }
        }


        for (i = 0; i < sw->chbands->len; i++){
            struct chband *b = (struct chband *)g_ptr_array_index(sw->chbands, i);
			if (b->band != band) continue;
            ox = sw->ch_left;
            oy = sw->ch_bottom;
            oop = "";
            b->mouse_value = 0;
            for (j = 0; j < b->chqsos->len; j++){
                struct chqso *q = (struct chqso *)g_ptr_array_index(b->chqsos, j);
                //dbg("time=%d value=%d\n", q->time, q->value);
                x = sw->ch_left + ( (q->time - sw->mint) * (sw->ch_right - sw->ch_left) ) / (sw->maxt - sw->mint);
                y = sw->ch_bottom - ( (q->value - sw->minv) * (sw->ch_bottom - sw->ch_top) ) / (sw->maxv - sw->minv);
                if (x < sw->mx) {
                    b->mouse_value = q->value;
                    b->op = q->op;
                }
                z_line(sw->screen, ox, oy, x, oy, b->color);
                z_line(sw->screen, x, oy, x, y, b->color);
                if (strcmp(q->op, oop) != 0){
                    z_putpixel(sw->screen, x - 1, oy - 1, b->color);
                    z_putpixel(sw->screen, x - 1, oy    , b->color);
                    z_putpixel(sw->screen, x - 1, oy + 1, b->color);
                    z_putpixel(sw->screen, x    , oy - 1, b->color);
                    z_putpixel(sw->screen, x    , oy + 1, b->color);
                    z_putpixel(sw->screen, x + 1, oy - 1, b->color);
                    z_putpixel(sw->screen, x + 1, oy    , b->color);
                    z_putpixel(sw->screen, x + 1, oy + 1, b->color);
                    z_putpixel(sw->screen, x    , oy + 2, b->color);
                    z_putpixel(sw->screen, x    , oy + 3, b->color);
                    z_putpixel(sw->screen, x    , oy + 4, b->color);
                }
                ox = x;
                oy = y;
                oop = q->op;
            }
            z_line(sw->screen, ox, oy, sw->ch_right, oy, b->color);
        }

        if (band){ 
            ox = sw->ch_left;
            oy = sw->ch_bottom;
            oop = "";
            v = 0;
            sw->ch_mouse_value = 0;
            for (i = 0; i < band->qsos->len; i++){
                struct qso *q = (struct qso *)g_ptr_array_index(band->qsos, i);

                if (i == 0) bt = atoi(q->date_str + 2); // no century
                if (q->error || q->dupe) continue;
                tajm = atoi(q->time_str);
                t = (atoi(q->date_str + 2) - bt) * 1440 + (tajm / 100) * 60 + tajm % 100;
                v += q->qsop;
                //dbg("time=%d value=%d\n", t, v);
                x = sw->ch_left + ( (t - sw->mint) * (sw->ch_right - sw->ch_left) ) / (sw->maxt - sw->mint);
                y = sw->ch_bottom - ( (v - sw->minv) * (sw->ch_bottom - sw->ch_top) ) / (sw->maxv - sw->minv);
                if (x < sw->mx) {
                    sw->ch_mouse_value = v;
                    sw->ch_op = q->operator_;
                }
                z_line(sw->screen, ox, oy, x, oy, sdl->gr[15]);
                z_line(sw->screen, x, oy, x, y, sdl->gr[15]);
                if (strcmp(q->operator_, oop) != 0){
                    z_putpixel(sw->screen, x - 1, oy - 1, sdl->gr[15]);
                    z_putpixel(sw->screen, x - 1, oy    , sdl->gr[15]);
                    z_putpixel(sw->screen, x - 1, oy + 1, sdl->gr[15]);
                    z_putpixel(sw->screen, x    , oy - 1, sdl->gr[15]);
                    z_putpixel(sw->screen, x    , oy + 1, sdl->gr[15]);
                    z_putpixel(sw->screen, x + 1, oy - 1, sdl->gr[15]);
                    z_putpixel(sw->screen, x + 1, oy    , sdl->gr[15]);
                    z_putpixel(sw->screen, x + 1, oy + 1, sdl->gr[15]);
                    z_putpixel(sw->screen, x    , oy + 2, sdl->gr[15]);
                    z_putpixel(sw->screen, x    , oy + 3, sdl->gr[15]);
                    z_putpixel(sw->screen, x    , oy + 4, sdl->gr[15]);
                }
                ox = x;
                oy = y;
                oop = q->operator_;
            }

            t = (nowd - bt) * 1440 + nowt;
			
				/*double ddt = t - sw->mint;
				double ddx = sw->ch_right - sw->ch_left;
				double dmt = sw->maxt - sw->mint;
				double dx = sw->ch_left + (ddt * ddx) / dmt;*/
			x = (int)((double)sw->ch_left + ( (t - sw->mint) * (double)(sw->ch_right - sw->ch_left) ) / (double)(sw->maxt - sw->mint));
				//x = dx;
			if (x < ox || x >= sw->screen->w) x = sw->screen->w - 1;
	//            dbg("nowd=%u nowt=%d bt=%d t=%d x=%d\n", nowd, nowt, bt, t, x);
			z_line(sw->screen, ox, oy, x, oy, sdl->gr[15]);
            
        }

        x = sw->ch_left + 10;
        y = sw->ch_top;
        if (band){
            cc = zstr_shorten(ctest->tname, 30);
            zsdl_printf(sw->screen, x, y, sdl->gr[13], 0, ZFONT_TRANSP, "%-12s %-30s %-7s  %-6s %d pts", ctest->pcall, cc, band->pband, sw->ch_op ? sw->ch_op : "", sw->ch_mouse_value); 
            g_free(cc);
            y += FONT_H;
        }
        for (i = 0; i < sw->chbands->len; i++){
			char *tname;
            struct chband *b = (struct chband *)g_ptr_array_index(sw->chbands, i);
			if (b->band != band) continue;
            tname = zstr_shorten(b->tname, 30);
            zsdl_printf(sw->screen, x, y, b->color, 0, ZFONT_TRANSP, "%-12s %-30s %-7s  %-6s %d pts", b->pcall, tname, b->pband, b->op ? b->op : "", b->mouse_value);
			g_free(tname);
            y += FONT_H;
        }
        t = sw->mint + ((sw->mx - sw->ch_left) * (sw->maxt - sw->mint)) / (sw->ch_right - sw->ch_left);
        v = sw->minv + ((sw->ch_bottom - sw->my) * (sw->maxv - sw->minv)) / (sw->ch_bottom - sw->ch_top);
        zsdl_printf(sw->screen, x, y, sdl->gr[13], 0, ZFONT_TRANSP, "%02d:%02d  %d pts", (t / 60) % 24, t % 60, v);

		/*{
			int	oldaa = zsdl->antialiasing;
			zsdl->antialiasing = 1;

			for (x = 1; x < 2; x++)
				z_line(sw->screen, 70 + x * 40, 100, 70, 300, z_makecol(0, 255, 255));

			zsdl->antialiasing = oldaa;
		} */
    }
    else
#endif
    {
        x = 1;
        y = 0;
        if (band){
            cc = g_strndup(ctest->tname, 30);
            clip_printf(sw, x, y, COL_NORM, "%-12s %-20s %-7s", ctest->pcall, cc, band->pband); 
            g_free(cc);
            y++;
        }
        for (i = 0; i < sw->chbands->len; i++){
            struct chband *b = (struct chband *)g_ptr_array_index(sw->chbands, i);
			if (b->band != band) continue;
            clip_printf(sw, x, y, COL(i % 7 + 0x41), "%-12s %-20s %-7s", b->pcall, b->tname, b->pband);
            y++;
        }
    }
}


void sw_chart_check_bounds(struct subwin *sw){
#ifdef Z_HAVE_SDL
    if (!sdl) return;
#endif
}

void sw_chart_raise(struct subwin *sw){
#ifdef Z_HAVE_SDL
    sw->gdirty=1;      
#endif
}

void sw_chart_free_band(struct chband *b){
    int i;
    for (i = 0; i < b->chqsos->len; i++){
        struct chqso *q = (struct chqso *)g_ptr_array_index(b->chqsos, i);
        zg_free0(q->call);
        zg_free0(q->op);
        g_free(q);
    }
    g_ptr_array_free(b->chqsos, TRUE);
    zg_free0(b->pcall);
    zg_free0(b->pwwlo);
    zg_free0(b->tname);
    zg_free0(b->pband);
    zg_free0(b->filename);
    g_free(b);
}

void sw_chart_free(struct subwin *sw){
    int i;
    if (!sw->chbands) return;
    for (i = 0; i < sw->chbands->len; i++){
        struct chband *b = (struct chband*)g_ptr_array_index(sw->chbands, i);
        sw_chart_free_band(b);
    }
    g_ptr_array_free(sw->chbands, TRUE);
}

void sw_chart_delete_band(void *chb, void * unused){

	struct chband *b = (struct chband*)chb;
    g_ptr_array_remove(gses->ontop->chbands, b);
    sw_chart_free_band(b);
    redraw_later();
}


int sw_chart_load_file(struct subwin *sw, char *filename, int quiet){
    int j, v;
    struct chband *b;
    FILE *f;
    GString *gs;
    char *val;
    gchar **items;
    int firstdate = -1;
    int tajm;
    GHashTable *hash;

//    if (!sdl) return;
    
    f = fopen(filename, "rt");
    if (!f){
        if (!quiet) log_addf(VTEXT(T_CANT_READ_S), filename);
        return -1;
    }
    hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    gs = g_string_sized_new(100);
    
    b = g_new0(struct chband, 1);
    g_ptr_array_add(sw->chbands, b);
    b->chqsos = g_ptr_array_new();
    b->filename = g_strdup(filename);
#ifdef Z_HAVE_SDL
    if (sdl){
		char *path;
		unsigned hash;
		int r, g, l;
		path = g_strdup(filename);
		z_dirname(path);
		hash = g_str_hash(path);
		r = 0x80 | ((hash >> 1) & 0x7f);
		g = 0x80 | ((hash >> 9) & 0x7f);
		l = 0x80 | ((hash >> 17) & 0x7f);
		if (r < g && r < l) r = 0;
		if (g < r && g < l) g = 0;
		if (l < r && l < g) l = 0;
		b->color = z_makecol(r, g, l);
		g_free(path);
//			sdl->termcol[(sw->chbands->len % 6) + 8];
	}
#endif

    zfile_fgets(gs, f, 0);
    while(zfile_fgets(gs, f, 0) != NULL){
        if (gs->str[0]=='[') break;
        val = strchr(gs->str, '=');
        *val='\0';
        val++;
//        dbg("key='%s'\n", gs->str);
        if (strcasecmp(gs->str, "pcall")==0){
            zg_free0(b->pcall);
            b->pcall = g_strdup(val);
        }else if (strcasecmp(gs->str, "pwwlo")==0){
            zg_free0(b->pwwlo);
            b->pwwlo = g_strdup(val);
        }else if (strcasecmp(gs->str, "tname")==0){
            zg_free0(b->tname);
            b->tname = g_strndup(val, 30);
        }else if (strcasecmp(gs->str, "pband")==0){
            zg_free0(b->pband);
            b->pband = g_strndup(val, 7);
			b->band = find_band_by_pband(b->pband);
        }
    }
    while(strncasecmp(gs->str, "[qsorecords", 11)!=0){
        items = NULL;
        if (zfile_fgets(gs, f, 0) == NULL) goto x;
        if (strncasecmp(gs->str, "qso", 3) == 0){
            items = g_strsplit_set(gs->str, "=;", 0);
            for (j = 0; j <= 4; j++) if (!items[j]) goto bado;
            g_hash_table_insert(hash, g_strdup(items[0]), g_strdup(items[2]));
        }
    bado:;
        g_strfreev(items);
    }
    v = 0;
    while(zfile_fgets(gs, f, 0) != NULL){
        struct chqso *q;
        char key[20], *hkey;
        if (gs->str[0]=='[') break;
//        dbg("qso='%s'\n", gs->str);
        items = g_strsplit(gs->str, ";", 0);
        for (j = 0; j <= 11; j++) if (!items[j]) goto badq;

        if (firstdate < 0) firstdate = atoi(items[0]);
        if (!strcasecmp(items[2], "ERROR")) continue;

        q = g_new0(struct chqso, 1);
        q->call = g_strdup(items[2]);
        tajm = atoi(items[1]);
        q->time = (atoi(items[0]) - firstdate) * 1440 + (tajm / 100) * 60 + tajm % 100;
        q->pts = atoi(items[10]);
        sprintf(key, "qso%d", b->chqsos->len);
        hkey = g_hash_table_lookup(hash, key);
        q->op = g_strdup(hkey ? hkey : "");
        v += q->pts;
        q->value = v;
        g_ptr_array_add(b->chqsos, q);
        //dbg("%d %d %d\n", q->time, q->pts, q->value);
    badq:;
        g_strfreev(items);
    }
    fclose(f);

x:;
    g_string_free(gs, TRUE);
    g_hash_table_destroy(hash);

    return 0;


/*    v = 0;
    for (i = 1400; i < 2400+1400; i += 20){
        struct chqso *q = g_new0(struct chqso, 1);
        q->time = i;
        q->value = v;
        v += rand() % 300;
        g_ptr_array_add(b->chqsos, q);
    } */
}

int sw_chart_recalc_extremes(struct subwin *sw, struct band *band){
#ifdef Z_HAVE_SDL
    int i, bt = 0, t, v, j, tajm;
    
    sw->mint = 1000000000;
    sw->maxt = 0;
    sw->minv = 0;
    sw->maxv = 0;

    if (band){
        v = 0;
        for (i = 0; i < band->qsos->len; i++){
            struct qso *q = (struct qso *)g_ptr_array_index(band->qsos, i);

            if (i == 0) bt = atoi(q->date_str + 2);
            if (q->error || q->dupe) continue;
            tajm = atoi(q->time_str);
            t = (atoi(q->date_str + 2) - bt) * 1440 + (tajm / 100) * 60 + tajm % 100;
            v += q->qsop;
            if (t < sw->mint) sw->mint = t;
            if (t > sw->maxt) sw->maxt = t;
            if (v < sw->minv) sw->minv = v;
            if (v > sw->maxv) sw->maxv = v * 1.1;
        }
    }
    for (i = 0; i < sw->chbands->len; i++){
        struct chband *b = (struct chband *)g_ptr_array_index(sw->chbands, i);
		if (b->band != band) continue;
        for (j = 0; j < b->chqsos->len; j++){
            struct chqso *q = (struct chqso *)g_ptr_array_index(b->chqsos, j);
            if (q->time < sw->mint) sw->mint = q->time;
            if (q->time > sw->maxt) sw->maxt = q->time;
            if (q->value < sw->minv) sw->minv = q->value;
            if (q->value > sw->maxv) sw->maxv = q->value;
        }
    }
	
	if (sw->mint != 1000000000) sw->mint = (sw->mint / 60) * 60;
	sw->maxt = ((sw->maxt + 59) / 60) * 60;
#endif
	return 0;
}

int chart_reload(void){
#ifdef Z_HAVE_SDL
    int i;
    struct subwin *sw=NULL;
    if (!sdl) return 0;
    /*dbg("maps_reload\n");*/
    
    for (i=0;i<gses->subwins->len;i++){
        sw=(struct subwin *)g_ptr_array_index(gses->subwins, i);
        if (sw->type!=SWT_CHART) continue;
        sw_chart_recalc_extremes(sw, aband);
    }
    if (gses && gses->ontop && gses->ontop->type==SWT_CHART) sw_chart_redraw(gses->ontop, aband, 0);
#endif
    return 0;
}


int save_chart_to_file(char *filename){
    int i, j, k;
    int ret = 0;
    struct subwin *subwin;
    struct chband *chband;
    FILE *f;
	GString *gs;

    f = fopen(filename, "wb");
    if (!f){
        log_addf(VTEXT(T_CANT_WRITE_S), filename);
        return errno;
    }

    j = 0;
	gs = g_string_new("");
    for (i = 0; i < gses->subwins->len; i++){
        subwin = (struct subwin *) g_ptr_array_index(gses->subwins, i);
        if (subwin->type != SWT_CHART) continue;
        for (k = 0; k < subwin->chbands->len; k++){
            chband = (struct chband*)g_ptr_array_index(subwin->chbands, k);
			
			zg_string_eprintfa("e", gs, "%d;%s\r\n", j, chband->filename);
        }
        j++;
    }
	fprintf(f, "%s", gs->str);
	g_string_free(gs, TRUE);
    fclose(f);
    
    return ret;
}

int load_chart_from_file(gchar *filename){
    FILE *f;
    char s[402];
    struct zstring *zs;
    int i, j, k;
    char *c;
    struct subwin *subwin;

    f = fopen(filename, "rt");
    if (!f){
        return -1;
    }

    while((fgets(s, 400, f))!=NULL){
        chomp(s);

        zs = zstrdup(s);
        k = atoi(ztokenize(zs, 1));
        c = ztokenize(zs, 0);

        j = 0;
        for (i = 0; i < gses->subwins->len; i++){
            subwin = (struct subwin *) g_ptr_array_index(gses->subwins, i);
            if (subwin->type != SWT_CHART) continue;
            if (j == k){
                sw_chart_load_file(subwin, c, 0);
            }
            j++;
        }

        zfree(zs);

    }
    fclose(f);
    //z_ptr_array_qsort(qrv->qrvs, qrv->sort);
    return 0;
}

void chart_clear_all(){
    zg_ptr_array_foreach(struct subwin *, subwin, gses->subwins){
        //dbg("title=%s\n", subwin->title);
        if (!subwin->chbands) continue;
     
        int i;
        for (i = subwin->chbands->len - 1; i >= 0; i--) {
            struct chband* chband = (struct chband*)g_ptr_array_index(subwin->chbands, i);
            sw_chart_free_band(chband);
            g_ptr_array_remove_index(subwin->chbands, i);
        }
    }
    redraw_later();
}

