/*
    rain.c - Rainscatter map
    Copyright (C) 2016-2022 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 "rain.h"

#include <libzia.h>
#include <zjson0.h>

#include "header.h"

#ifdef Z_HAVE_SDL

#include "main.h"
#include "fifo.h"
#include "map.h"
#include "rc.h"
#include "session.h"
#include "subwin.h"
#include "tsdl.h"

struct rain *grain;

#define MAXLAT (85.05112878 * M_PI / 180.0)

struct rain *init_rain(){
    struct rain *rain;
    int i;

    if (!zsdl) return NULL;
	if (!cfg->rain_enable) return NULL;

    rain = g_new0(struct rain, 1);
	rain->debug = cfg->rain_debug;

	rain->colors = g_new0(int, RAIN_COLORS);
	rain->colors[0] = z_makecol(56, 0, 112);
	rain->colors[1] = z_makecol(48, 0, 168);     // 0.1 mm/h
	rain->colors[2] = z_makecol(0, 0, 252);
	rain->colors[3] = z_makecol(0, 108, 192);
	rain->colors[4] = z_makecol(0, 160, 0);
	rain->colors[5] = z_makecol(0, 188, 0);	     // 1 mm/h
	rain->colors[6] = z_makecol(52, 216, 0);
	rain->colors[7] = z_makecol(156, 220, 0);
	rain->colors[8] = z_makecol(224, 220, 0);
	rain->colors[9] = z_makecol(252, 176, 0);    // 10 mm/h
	rain->colors[10] = z_makecol(252, 132, 0);
	rain->colors[11] = z_makecol(252, 88, 0);
	rain->colors[12] = z_makecol(252, 0, 0);
	rain->colors[13] = z_makecol(160, 0, 0);     // 100 mm/h
	rain->colors[14] = z_makecol(252, 252, 252);

	rain->bwcolors = g_new0(int, RAIN_COLORS);
    for (i = 0; i < RAIN_COLORS; i++){
        rain->bwcolors[i] = z_makecol((i + 1) * 8, (i + 1) * 8, (i + 1) * 8);
    }
    
	rain->maxqrb = cfg->rain_maxqrb;	// km
	rain->mincolor = cfg->rain_mincolor; // orange
	rain->minscpdist = cfg->rain_minscpdist; // km
	rain->scps = g_ptr_array_new_with_free_func((GDestroyNotify)free_rain_scp);
	rain->scplocs = g_string_sized_new(256);

	if (0 && cfg->rain_meteox){ // DISABLED
		struct rain_provider *provider = g_new0(struct rain_provider, 1);
		provider->rain = rain;
		provider->name = "meteox";
		provider->pal = rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1000, rain_meteox_timer, provider); 
		provider->h1 = -5.72 * M_PI / 180.0;
		provider->h2 = 18.72 * M_PI / 180.0;
		provider->w1 = 57.63 * M_PI / 180.0;
		provider->w2 = 43.1 * M_PI / 180.0;
		provider->hpx1 = 110;
		provider->hpx2 = 529;
		provider->wpx1 = 73;
		provider->wpx2 = 516;
		provider->hmult = (provider->h2 - provider->h1) / (provider->hpx2 - provider->hpx1);
	    provider->wmult = (provider->w2 - provider->w1) / (provider->wpx2 - provider->wpx1);

		provider->hash_func = rain_meteox_hash;
		provider->img2hw = rain_linear_img2hw;
		for (i = 0; i < RAIN_COLORS; i++) provider->hashes[i] = g_hash_table_new(g_direct_hash, g_direct_equal);
		provider->mask = 0xffffff80;
		provider->step = 0x80;

		provider->left = 0;
		provider->top = 25;
		provider->right = 550;
		provider->bottom = 550;

		provider->srccolslen = 15;
		provider->srccols = g_new0(int, provider->srccolslen);
 		provider->translation = g_new0(int, provider->srccolslen);
		for (i = 0; i < provider->srccolslen; i++) provider->translation[i] = i;

		provider->ofs_min = 120; // CEST or CET

		rain->meteox = provider;
	}

	if (0 && cfg->rain_wetteronline){ // DISABLED
		struct rain_provider *provider = g_new0(struct rain_provider, 1);
		provider->rain = rain;
		provider->name = "wetteronline";
		provider->pal = rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1000, rain_wetteronline_timer, provider); 
		provider->h1 = 1.62 * M_PI / 180.0;
		provider->h2 = 22.52 * M_PI / 180.0;
		provider->w1 = 54.88 * M_PI / 180.0;
		provider->w2 = 46.02 * M_PI / 180.0;
		provider->hpx1 = 32;
		provider->hpx2 = 469;
		provider->wpx1 = 167;
		provider->wpx2 = 458;
		provider->hmult = (provider->h2 - provider->h1) / (provider->hpx2 - provider->hpx1);
	    provider->wmult = (provider->w2 - provider->w1) / (provider->wpx2 - provider->wpx1);

		provider->hash_func = rain_wetteronline_hash;
		provider->img2hw = rain_linear_img2hw;
		for (i = 0; i < RAIN_COLORS; i++) provider->hashes[i] = g_hash_table_new(g_direct_hash, g_direct_equal);
		provider->mask = 0xffffff80;
		provider->step = 0x80;

		provider->left = 0;
		provider->top = 0;
		provider->right = 519;
		provider->bottom = 550;

		provider->srccolslen = 6;
		provider->srccols = g_new0(int, provider->srccolslen);
 		provider->translation = g_new0(int, provider->srccolslen);
		for (i = 0; i < provider->srccolslen; i++) provider->translation[i] = i;

		provider->ofs_min = -3; 

		rain->wetteronline = provider;
	}

	if (cfg->rain_chmi){
		struct rain_provider *provider = g_new0(struct rain_provider, 1);
		provider->rain = rain;
		provider->name = "chmi";
		provider->pal = rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1000, rain_chmi_timer, provider); 
		provider->h1 = 12.12 * M_PI / 180.0;
		provider->h2 = 18.84 * M_PI / 180.0;
		provider->w1 = 51.04 * M_PI / 180.0;
		provider->w2 = 48.58 * M_PI / 180.0;
		provider->hpx1 = 59;
		provider->hpx2 = 542;
		provider->wpx1 = 128;
		provider->wpx2 = 404;
		provider->hmult = (provider->h2 - provider->h1) / (provider->hpx2 - provider->hpx1);
	    provider->wmult = (provider->w2 - provider->w1) / (provider->wpx2 - provider->wpx1);

		provider->hash_func = rain_chmi_hash;
		provider->img2hw = rain_linear_img2hw;
		for (i = 0; i < RAIN_COLORS; i++) provider->hashes[i] = g_hash_table_new(g_direct_hash, g_direct_equal);
		provider->mask = 0xffffff80;
		provider->step = 0x80;

		provider->left = 0;
		provider->top = 82;
		provider->right = 597;
		provider->bottom = 459;

		provider->ofs_min = 10; // FIXME

		rain->chmi = provider;
	}

	if (cfg->rain_weatheronline){
		struct rain_provider *provider = g_new0(struct rain_provider, 1);
		provider->rain = rain;
		provider->name = "weatheronline";
		provider->pal = rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1000, rain_weatheronline_timer, provider); 
		provider->h1 = -5.6 * M_PI / 180.0; // lon
		provider->h2 = 37.62 * M_PI / 180.0; // lon
		provider->w1 = 35.96 * M_PI / 180.0;	 // lat
		provider->w2 = 55.75 * M_PI / 180.0;  // lat
		provider->hpx1 = 57;  // x
		provider->hpx2 = 410; // x
		provider->wpx1 = 438; // y
		provider->wpx2 = 181; // y
		provider->hmult = (provider->h2 - provider->h1) / (provider->hpx2 - provider->hpx1);
	    provider->wmult = (provider->w2 - provider->w1) / (provider->wpx2 - provider->wpx1);

		provider->hash_func = rain_weatheronline_hash;
		provider->img2hw = rain_weatheronline_img2hw;
		for (i = 0; i < RAIN_COLORS; i++) provider->hashes[i] = g_hash_table_new(g_direct_hash, g_direct_equal);
		provider->mask = 0xffffff80;
		provider->step = 0x80;

		provider->left = 0;
		provider->top = 0;
		provider->right = 519;
		provider->bottom = 550;

		provider->srccolslen = 13;
		provider->srccols = g_new0(int, provider->srccolslen);
 		provider->translation = g_new0(int, provider->srccolslen);
		for (i = 0; i < provider->srccolslen; i++) provider->translation[i] = i;

		rain->weatheronline = provider;
	}

	if (cfg->rain_rainviewer){
		struct rain_provider *provider = g_new0(struct rain_provider, 1);
		dbg("new rainviewer %p\n", provider);
		provider->rain = rain;
		provider->name = "rainviewer";
		provider->pal = rain->bwcolors;
		g_assert(provider->timer_id == 0);
		provider->timer_id = zselect_timer_new(zsel, 1000, rain_rainviewer_timer, provider);
		rain_rainviewer_init(provider);

		provider->hash_func = rain_rainviewer_hash;
		provider->img2hw = rain_rainviewer_img2hw;
		for (i = 0; i < RAIN_COLORS; i++) provider->hashes[i] = g_hash_table_new(g_direct_hash, g_direct_equal);
		provider->mask = 0xffffff80;
		provider->step = 0x80;
		rain->rainviewer = provider;
	}
	
    return rain;
}

static gboolean free_rain_hash(gpointer key, gpointer value, gpointer user_data){
	struct zbinbuf *zbb = (struct zbinbuf *)value;
	zbinbuf_free(zbb);
	return TRUE;
}

struct rain_scp *init_rain_scp(float kx, float ky){
	struct rain_scp *scp = g_new0(struct rain_scp, 1);
	scp->kx = kx;
	scp->ky = ky;

	double qrb, qtf, h, w;
	km2qrbqtf((int)roundf(kx), (int)roundf(ky), &qrb, &qtf);
	qrbqtf2hw(gses->myh, gses->myw, qrb, qtf, &h, &w);
	hw2loc(scp->wwl, h * 180.0 / M_PI, w * 180.0 / M_PI, 6);
	scp->qrb = (int)round(sqrt(kx*kx + ky*ky));
	//dbg("SCP %s\n", scp->wwl);
	return scp;
}


void free_rain_scp(struct rain_scp *scp)
{
	g_free(scp);
}


void rain_analyze_scps(struct rain *rain){
	if (!gses) return;

	g_ptr_array_free(rain->scps, TRUE);// with free func free_rain_scp
	rain->scps = g_ptr_array_new_with_free_func((GDestroyNotify)free_rain_scp);

	g_string_truncate(rain->scplocs, 0);
    
	if (rain->chmi) rain_add_scps(rain->chmi);
	if (rain->meteox) rain_add_scps(rain->meteox);
	if (rain->wetteronline) rain_add_scps(rain->wetteronline);
	if (rain->weatheronline) rain_add_scps(rain->weatheronline);
	if (rain->rainviewer) rain_add_scps(rain->rainviewer);
	
	if (rain->scplocs->len > 0){
		log_addf(TRANSLATE("Scatterpoints: %s"), rain->scplocs->str);
	}
}

void rain_add_scps(struct rain_provider *provider){
	int kx, ky, minkx, minky, maxkx, maxky;//, dummy;
	//double h, w;

	minkx = -provider->rain->maxqrb & provider->mask;
	minky = -provider->rain->maxqrb & provider->mask;
	maxkx = (provider->rain->maxqrb) & provider->mask;
	maxky = (provider->rain->maxqrb) & provider->mask;

	if (minkx < -32768) minkx = -32768;
	if (maxkx > 32767) maxkx = 32767;
	if (minky < -32768) minky = -32768;
	if (maxky > 32767) maxky = 32767;
	
	for (int ci = RAIN_COLORS - 1; ci >= provider->rain->mincolor; ci--){
		//GHashTable *hash = provider->hashes[ci];
	
		//int c = provider->pal[ci];
		for (ky = minky; ky <= maxky; ky += provider->step){
			for (kx = minkx; kx <= maxkx; kx += provider->step){

				gpointer key = provider->hash_func(kx, ky, ci);
				GHashTable *hash = provider->hashes[ci];
				struct zbinbuf *zbb = (struct zbinbuf *)g_hash_table_lookup(hash, key);
				//dbg("kx=%d   \tky=%d\n", kx, ky);
				if (zbb == NULL) continue;

				int i;
				for (i = 0; i <= zbb->len - (int)sizeof(struct rain_km); i += sizeof(struct rain_km)){
					struct rain_km *km = (struct rain_km *)(zbb->buf + i);
					if (rain_scp_exists(provider, km->kx, km->ky)) continue;

					struct rain_scp *scp = init_rain_scp(km->kx, km->ky);
					g_ptr_array_add(provider->rain->scps, scp);

					if (scp->qrb <= provider->rain->maxqrb){
						if (provider->rain->scplocs->len > 0) g_string_append(provider->rain->scplocs, " ");
						g_string_append(provider->rain->scplocs, scp->wwl);
					}
				}
			}
		}

	}
}

int rain_scp_exists(struct rain_provider *prov, float kx, float ky){
	int i;
	for (i = 0; i < prov->rain->scps->len; i++){
		struct rain_scp *scp = (struct rain_scp *)g_ptr_array_index(prov->rain->scps, i);
		float dist = sqrtf(powf(scp->kx - kx, 2) + powf(scp->ky - ky, 2));
		if (dist < prov->rain->minscpdist) return 1;
	}
	return 0;
}


void free_rain_provider(struct rain_provider *provider){
	int i;

    if (provider->timer_id) zselect_timer_kill(zsel, provider->timer_id);
	if (provider->img != NULL) {
		SDL_FreeSurface(provider->img);
		provider->img = NULL;
	}

	zhttp_free(provider->http);
	for (i = 0; i < RAIN_COLORS; i++) {
		GHashTable *hash = provider->hashes[i];
		g_hash_table_foreach_remove(hash, free_rain_hash, NULL);
        g_hash_table_destroy(hash);
	}

	g_free(provider->srccols);
	g_free(provider->translation);
	g_free(provider->title);
	g_free(provider->rv_path);
	g_free(provider->rv_oldpath);
	g_free(provider);
	dbg("freed rainviewer %p\n", provider);

}

void free_rain(struct rain *rain){
    if (!rain) return;

	if (rain->meteox) free_rain_provider(rain->meteox);
	if (rain->wetteronline) free_rain_provider(rain->wetteronline);
	if (rain->chmi) free_rain_provider(rain->chmi);
	if (rain->weatheronline) free_rain_provider(rain->weatheronline);
	if (rain->rainviewer) free_rain_provider(rain->rainviewer);

    g_free(rain->colors);
    g_free(rain->bwcolors);
    g_string_free(rain->scplocs, TRUE);
	g_ptr_array_free(rain->scps, TRUE);// with free func free_rain_scp
    g_free(rain);
}

static void rain_truncate_hash(gpointer key, gpointer value, gpointer user_data){
	struct zbinbuf *zbb = (struct zbinbuf *)value;
	zbinbuf_truncate(zbb, 0);
}


static void rain_provider_add(struct rain_provider *provider, int ix, int iy, int ci){
	double hh1, ww1, hh2, ww2;
	gpointer key;
	GHashTable *hash;
	struct zbinbuf *zbb;
	struct rain_km km;

    provider->img2hw(provider, ix - 1, iy - 1, &hh1, &ww1); 
    provider->img2hw(provider, ix + 1, iy + 1, &hh2, &ww2);// when +1 space between bars 

	//hh2 = hh1 + provider->hmult;
	//ww2 = ww1 + provider->wmult;


	hw2km_f(gses->myh, gses->myw, hh1, ww1, &km.kx1, &km.ky1);
	hw2km_f(gses->myh, gses->myw, hh2, ww1, &km.kx2, &km.ky2);
	hw2km_f(gses->myh, gses->myw, hh1, ww2, &km.kx3, &km.ky3);
	hw2km_f(gses->myh, gses->myw, hh2, ww2, &km.kx4, &km.ky4);
	km.kx = (km.kx1 + km.kx2) / 2;
	km.ky = (km.ky1 + km.ky2) / 2;

	key = provider->hash_func(km.kx1, km.ky1, ci);
	hash = provider->hashes[ci];
	zbb = (struct zbinbuf *)g_hash_table_lookup(hash, key);
	if (zbb == NULL) {
		zbb = zbinbuf_init();
		zbb->increment = 4096;
		g_hash_table_insert(hash, key, zbb);
	}
	zbinbuf_append_bin(zbb, &km, sizeof(struct rain_km));
}


void rain_provider_draw_colors(struct rain_provider *provider, SDL_Surface *surface){
	int i, c;
	Uint8 r, g, b;
	int w = 16;

	for (i = 0; i < RAIN_COLORS; i++){
		SDL_Rect rr;
		rr.x = i * w;
		rr.y = surface->h - w;
		rr.w = w;
		rr.h = w;
		SDL_GetRGB(provider->rain->colors[i], sdl->screen->format, &r, &g, &b);
		c = SDL_MapRGB(surface->format, r, g, b);
		SDL_FillRect(surface, &rr, c);
	}

	for (i = 0; i < provider->srccolslen; i++){
		SDL_Rect rr;
		rr.x = i * w;
		if (provider->translation) rr.x = provider->translation[i] * w;
		rr.y = surface->h - w - w;
		rr.w = w;
		rr.h = w;
		SDL_GetRGB(provider->srccols[i], sdl->screen->format, &r, &g, &b);
		c = SDL_MapRGB(surface->format, r, g, b);
		SDL_FillRect(surface, &rr, c);
	}
}

void rain_linear_img2hw(struct rain_provider *provider, float ix, float iy, double *h, double *w){
	*h = provider->h1 + (ix - provider->hpx1) * provider->hmult;
	*w = provider->w1 + (iy - provider->wpx1) * provider->wmult;
}


/*static void rain_provider_reload(struct rain_provider *provider, void (*handler)(void*)){
	if (!provider) return;

	if (provider->timer_id <= 0) return;

	int i;
	for (i = 0; i < RAIN_COLORS; i++) {
		GHashTable *hash = provider->hashes[i];
		g_hash_table_foreach(hash, rain_truncate_hash, NULL);
	}

	zselect_timer_kill(zsel, provider->timer_id);
	zselect_timer_new(zsel, rand()%2000, handler, provider);
} */

void rain_reload(){
	if (!grain) return;

	/*rain_provider_reload(grain->meteox, rain_meteox_timer);
	rain_provider_reload(grain->wetteronline, rain_wetteronline_timer);
	rain_provider_reload(grain->chmi, rain_chmi_timer);
	rain_provider_reload(grain->weatheronline, rain_weatheronline_timer);
	//rain_provider_reload(grain->rainviewer, rain_rainv);
	rain_analyze_scps(grain); // only truncate*/

	free_rain(grain);
	grain = init_rain();
}



/* meteox.com */
void rain_meteox_timer(void *arg){
    char *url;
    time_t tt;
    struct tm tm;
    struct rain_provider *provider = (struct rain_provider*)arg;

	provider->timer_id = 0;

    tt = time(NULL);
	tt += provider->ofs_min * 60;
    gmtime_r(&tt, &tm);
	tm.tm_min = (tm.tm_min / 15) * 15;

    g_free(provider->title);
	provider->title = g_strdup_printf("Meteox %02d:%02d", tm.tm_hour, tm.tm_min);

	dbg("rain_timer %d.%d.%d %d:%02d:%02d -> (%02d)\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, (tm.tm_min / 15) * 15); 

    url = g_strdup_printf("https://www.meteox.com/images.aspx?jaar=%04d&maand=%02d&dag=%02d&uur=%02d&minuut=%02d", 
        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
        tm.tm_hour, tm.tm_min);
    //url = g_strdup("http://www.meteox.com/images.aspx?jaar=2016&maand=07&dag=08&uur=20&minuut=00"); 


    provider->http = zhttp_init();
    dbg("meteox url = '%s'\n", url);
    zhttp_get(provider->http, zsel, url, rain_meteox_downloaded, provider);
    g_free(url);
	provider->timer_id = zselect_timer_new(zsel, 50*1000, rain_meteox_timer, provider); // restart when freezes
}

void rain_meteox_downloaded(struct zhttp *http){
    int remains;
    struct timeval tv;
    struct rain_provider *provider = (struct rain_provider*)http->arg;
    SDL_Surface *newimg;
    
    if (provider->timer_id) {
        zselect_timer_kill(zsel, provider->timer_id);
        provider->timer_id = 0;
    }
	
    dbg("rain_meteox_downloaded: got %d bytes\n", http->response->len - http->dataofs);

	if (provider->rain->debug) zbinbuf_write_to_file(http->response, "_meteox.gif", http->dataofs, http->response->len - http->dataofs);

	if (provider->img != NULL) {
		SDL_FreeSurface(provider->img);
		provider->img = NULL;
	}
	

	if (provider->ofs_min >= -60 && 
		(http->dataofs < 1 || strncmp(http->response->buf + http->dataofs, "\x89PNG\r\n\x1a\n", 8) != 0) // PNG is OK, GIF is error
       ){ // image not available, go to the past
		provider->ofs_min -= 15;
		dbg("set meteox_ofs_min to %d\n", provider->ofs_min);
		zhttp_free(http);
		provider->http = NULL;
        provider->pal = provider->rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1000, rain_meteox_timer, provider);
		sw_set_gdirty(SWT_MAP);
		return;
	}
	
    newimg = zpng_create(http->response->buf + http->dataofs, http->response->len - http->dataofs);
    if (newimg == NULL){
        dbg("Can't read bitmap\n");
        provider->pal = provider->rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_meteox_timer, provider);
    }else{
        if (provider->img != NULL) {
            SDL_FreeSurface(provider->img);
        }
        provider->img = newimg;
        provider->pal = provider->rain->colors;
        rain_meteox_load(provider);
		if (provider->rain->debug) {
			rain_provider_draw_colors(provider, provider->img);
			zpng_save(provider->img, "_meteox2.png", NULL);
		}
		rain_analyze_scps(provider->rain);

        //provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_meteox_timer, provider);
	    gettimeofday(&tv, NULL);
    	remains = 900000 - ((tv.tv_sec % 900) * 1000 + tv.tv_usec/1000);
	    provider->timer_id = zselect_timer_new(zsel, remains, rain_meteox_timer, provider);
    }
	zhttp_free(http);
	provider->http = NULL;
    sw_set_gdirty(SWT_MAP);


    //ST_STOP("rain_downloaded");
}

// colors are in image format
void rain_meteox_load(struct rain_provider *provider){
	int ix, iy, c, ci, i;
	SDL_Surface *img = provider->img;
	Uint8 r, g, b, a;

	provider->w = img->w;
	provider->h = img->h;

	for (i = 0; i < RAIN_COLORS; i++) {
		GHashTable *hash = provider->hashes[i];
		g_hash_table_foreach(hash, rain_truncate_hash, NULL);
	}

	provider->translation[0] = 0;
	provider->translation[1] = 1;
	provider->translation[2] = 3;
	provider->translation[3] = 5;
	provider->translation[4] = 6;
	provider->translation[5] = 7;
	provider->translation[6] = 8;
	provider->translation[7] = 9;
	provider->translation[8] = 10;
	provider->translation[9] = 11;
	provider->translation[10] = 11;
	provider->translation[11] = 12;
	provider->translation[12] = 12;
	provider->translation[13] = 13;
	provider->translation[14] = 13;

	
	for (iy = provider->top; iy < img->h && iy <= provider->bottom; iy++){
        for (ix = provider->left; ix < img->w && ix <= provider->right; ix++){
			c = z_getpixel(img, ix, iy);
			if (c == 0) continue;

			SDL_GetRGBA(c, img->format, &r, &g, &b, &a);
			if (r == g && b == 255){
                ci = (255 - r) / 27;
			}else if (r == 0 && g == 0){
				if (b > 150){
                    ci = 7;
				}else{
                    ci = 8;
				}
			}else if (g == 0 && b == 0){
				if (r > 211){
                    ci = 9;
				}else if (r > 187){
                    ci = 10;
				}else if (r > 159){
                    ci = 11;
				}else{
                    ci = 12;
				}
			}else if (r == g && b == 0){
                ci = 13;
			}else{
                ci = 14;
			}
			ci = provider->translation[ci];
			if (ci < 5) continue;
            if (provider->rain->debug) z_putpixel_fmt(img, ix, iy, provider->rain->colors[ci], sdl->screen->format);
			rain_provider_add(provider, ix, iy, ci);
		}
	}
}

gpointer rain_meteox_hash(short kx, short ky, int ci){
	unsigned int key = ((unsigned short)kx) >> 7;
	key |= (((unsigned short)ky) >> 1) & 0xff00;
	key |= ((unsigned char)ci) << 16; 
	return GUINT_TO_POINTER(key); 
}



/* wetteronline.de */
void rain_wetteronline_timer(void *arg){
    char *url;
    time_t tt;
    struct tm tm;
    struct rain_provider *provider = (struct rain_provider*)arg;

	provider->timer_id = 0;

    tt = time(NULL);
	tt += provider->ofs_min * 60;
    gmtime_r(&tt, &tm);
	tm.tm_min = (tm.tm_min / 5) * 5;
    dbg("rain_timer %d.%d.%d %d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 

	g_free(provider->title);
	provider->title = g_strdup_printf("WtOnl. %02d:%02dZ", tm.tm_hour, tm.tm_min);

    url = g_strdup_printf("http://tucnak.vaiz.cz/wetteronline.php?y=%d&m=%d&d=%d&h=%d&mi=%d&t=%lld", 
        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
		tm.tm_hour, tm.tm_min, tl); 
	//url = g_strdup("https://www.wetteronline.de/?ireq=true&pid=p_radar_map&src=wmapsextract/vermarktung/global2maps/2018/08/02/0001/grey_flat/201808020730_0001.png");

    provider->http = zhttp_init();
    dbg("wetteronline url = '%s'\n", url);
    zhttp_get(provider->http, zsel, url, rain_wetteronline_downloaded, provider);
    g_free(url);
	provider->timer_id = zselect_timer_new(zsel, 50*1000, rain_wetteronline_timer, provider); // restart when freezes
}

void rain_wetteronline_downloaded(struct zhttp *http){
    int remains;
    struct timeval tv;
    struct rain_provider *provider = (struct rain_provider*)http->arg;
    SDL_Surface *newimg;
    
    if (provider->timer_id) {
        zselect_timer_kill(zsel, provider->timer_id);
        provider->timer_id = 0;
    }
	
	if (provider->rain->debug) zbinbuf_write_to_file(http->response, "_wetteronline.png", http->dataofs, http->response->len - http->dataofs);

	if (http->response->len <= http->dataofs + 8 ||
		strncmp(http->response->buf + http->dataofs, "\x89PNG\r\n\x1a\n", 8) != 0){ // image not available
		dbg("No PNG returned\n");
		provider->pal = provider->rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_wetteronline_timer, provider);
		goto x;
	}


    newimg = zpng_create(http->response->buf + http->dataofs, http->response->len - http->dataofs);
    if (newimg == NULL){
        dbg("Can't read bitmap\n");
        provider->pal = provider->rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_wetteronline_timer, provider);
    }else{
        if (provider->img != NULL) {
            SDL_FreeSurface(provider->img);
        }
        provider->img = newimg;
        provider->pal = provider->rain->colors;
        rain_wetteronline_load(provider);
		if (provider->rain->debug) {
			rain_provider_draw_colors(provider, provider->img);
			zpng_save(provider->img, "_wetteronline2.png", NULL);
		}
		rain_analyze_scps(provider->rain);

		//provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_wetteronline_timer, provider);
	    gettimeofday(&tv, NULL);
    	remains = 300000 - ((tv.tv_sec % 300) * 1000 + tv.tv_usec/1000) + 500;
	    provider->timer_id = zselect_timer_new(zsel, remains, rain_wetteronline_timer, provider);
    }
x:;
	zhttp_free(http);
	provider->http = NULL;
	sw_set_gdirty(SWT_MAP);

    //ST_STOP("rain_downloaded");
}

// colors are in image format
void rain_wetteronline_load(struct rain_provider *provider){
	int ix, iy, c, ci, i;
	SDL_Surface *img = provider->img;
	
	provider->srccols[0] = (int)SDL_MapRGB(img->format, 170, 255, 255);
	provider->srccols[1] = (int)SDL_MapRGB(img->format, 83, 210, 255);
	provider->srccols[2] = (int)SDL_MapRGB(img->format, 42, 170, 255);
	provider->srccols[3] = (int)SDL_MapRGB(img->format, 28, 126, 217);
	provider->srccols[4] = (int)SDL_MapRGB(img->format, 153, 51, 153);
	provider->srccols[5] = (int)SDL_MapRGB(img->format, 255, 0, 255);

	provider->translation[0] = 2;
	provider->translation[1] = 4;
	provider->translation[2] = 6;
	provider->translation[3] = 8;
	provider->translation[4] = 11;
	provider->translation[5] = 12;
	
	provider->w = img->w;
	provider->h = img->h;
	
	for (i = 0; i < RAIN_COLORS; i++) {
		GHashTable *hash = provider->hashes[i];
		g_hash_table_foreach(hash, rain_truncate_hash, NULL);
	}

    for (iy = provider->top; iy < img->h && iy <= provider->bottom; iy++){
        for (ix = provider->left; ix < img->w && ix <= provider->right; ix++){
			Uint8 r, g, b;
			c = z_getpixel(img, ix, iy);
			
			for (i = -1; i > -10 && ix + i >= 0; i--){
				c = z_getpixel(img, ix + i, iy);
				SDL_GetRGB(c, img->format, &r, &g, &b);
				if (r >= 28 || g >= 51 || b >= 153) break;
			}
			
			for (ci = 0; ci < provider->srccolslen; ci++){
				if (c == provider->srccols[ci]){
					ci = provider->translation[ci];
					if (ci < 5) break;

					rain_provider_add(provider, ix, iy, ci);
					if (provider->rain->debug) z_putpixel_fmt(img, ix, iy, provider->rain->colors[ci], sdl->screen->format); 
					break;
				}
			}
		}
	}
}

gpointer rain_wetteronline_hash(short kx, short ky, int ci){
	unsigned int key = ((unsigned short)kx) >> 7;
	key |= (((unsigned short)ky) >> 1) & 0xff00;
	key |= ((unsigned char)ci) << 16; 
	return GUINT_TO_POINTER(key); 
}



/* chmi.cz */
void rain_chmi_timer(void *arg){
    char *url;
    time_t tt;
    struct tm tm;
    struct rain_provider *provider = (struct rain_provider*)arg;

	provider->timer_id = 0;

    tt = time(NULL);
    gmtime_r(&tt, &tm);
	dbg("%02d:%02dZ: ", tm.tm_hour, tm.tm_min);

	tt += provider->ofs_min * 60;
    gmtime_r(&tt, &tm);
	tm.tm_min = (tm.tm_min / 10) * 10;

	g_free(provider->title);
	provider->title = g_strdup_printf("CHMI   %02d:%02dZ", tm.tm_hour, tm.tm_min);

    //dbg("rain_timer %d.%d.%d %d.%02d.%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 

    url = g_strdup_printf("https://www.chmi.cz/files/portal/docs/meteo/rad/inca-cz/data/czrad-z_max3d/pacz2gmaps3.z_max3d.%04d%02d%02d.%02d%02d.0.png", 
        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
		tm.tm_hour, tm.tm_min); 
    //url = g_strdup_printf("http://portal.chmi.cz/files/portal/docs/meteo/rad/inca-cz/data/czrad-z_max3d/pacz2gmaps3.z_max3d.20160708.1800.0.png");

	provider->http = zhttp_init();
    dbg("CHMI url = '%s'\n", url);
    zhttp_get(provider->http, zsel, url, rain_chmi_downloaded, provider);
    g_free(url);
	provider->timer_id = zselect_timer_new(zsel, 50*1000, rain_chmi_timer, provider); // restart when freezes
}

void rain_chmi_downloaded(struct zhttp *http){
    struct rain_provider *provider = (struct rain_provider*)http->arg;
    SDL_Surface *newimg;
    

	if (http->state == ZHTTPST_ERROR && http->errorstr){
		dbg("ERROR rain_chmi_downloaded: %s\n", http->errorstr);
	}

    if (provider->timer_id) {
        zselect_timer_kill(zsel, provider->timer_id);
        provider->timer_id = 0;
    }
	
    dbg("rain_chmi_downloaded: got %d bytes\n", http->response->len - http->dataofs);

	if (provider->rain->debug) zbinbuf_write_to_file(http->response, "_chmi.png", http->dataofs, http->response->len - http->dataofs);

	if (http->status == 404){
	    provider->ofs_min -= 10;
		dbg("ERROR 404, set ofs_min to %d\n", provider->ofs_min);
		zhttp_free(http);
		provider->http = NULL;
        provider->pal = provider->rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1000, rain_chmi_timer, provider);
		sw_set_gdirty(SWT_MAP);

		return;
	}

    newimg = zpng_create(http->response->buf + http->dataofs, http->response->len - http->dataofs);
    if (newimg == NULL){
        dbg("Can't read CHMI bitmap\n");
        provider->pal = provider->rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_chmi_timer, provider);
    }else{
        dbg("CHMI bitmap loaded, %dx%d\n", newimg->w, newimg->h); 
        if (provider->img != NULL) {
            SDL_FreeSurface(provider->img);
        }
        provider->img = newimg;
        provider->pal = provider->rain->colors;
        rain_chmi_load(provider);
		rain_analyze_scps(provider->rain);

		//provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_chmi_timer, provider);
		provider->timer_id = zselect_timer_new(zsel, 9 * 60 * 1000, rain_chmi_timer, provider);
    } 
	zhttp_free(http);
	provider->http = NULL;
	sw_set_gdirty(SWT_MAP);

}

void rain_chmi_load(struct rain_provider *provider){
	int ix, iy, c, ci, i;
	SDL_Surface *img = provider->img;
	int red = SDL_MapRGB(sdl->screen->format, 252, 0, 0);
	
	provider->w = img->w;
	provider->h = img->h;

	for (i = 0; i < RAIN_COLORS; i++) {
		GHashTable *hash = provider->hashes[i];
		g_hash_table_foreach(hash, rain_truncate_hash, NULL);
	}


	for (iy = provider->top; iy < img->h && iy <= provider->bottom; iy++){
		for (ix = provider->left; ix < img->w && ix <= provider->right; ix++){
			c = z_getpixel_fmt(img, ix, iy, sdl->screen->format);// seems that R and G is swapped in image
			if (ix < 401 && iy > 414){
				for (i = -1; c == red && i > -10 && ix + i >= 0; i--){
					c = z_getpixel_fmt(img, ix + i, iy, sdl->screen->format);
				}
			} 
			for (ci = 0; ci < RAIN_COLORS; ci++){
				if (c == provider->rain->colors[ci]){
					if (ci < 5) break;
					rain_provider_add(provider, ix, iy, ci/*9*/);
					break;
				}
			}
		}
	}
}

gpointer rain_chmi_hash(short kx, short ky, int ci){
	unsigned int key = ((unsigned short)kx) >> 7;
	key |= (((unsigned short)ky) >> 1) & 0xff00;
	key |= ((unsigned char)ci) << 16; 
	return GUINT_TO_POINTER(key); 
}


/* weatheronline.co.uk */
void rain_weatheronline_timer(void *arg){
    char *url;
    time_t tt;
    struct tm tm;
    struct rain_provider *provider = (struct rain_provider*)arg;

	provider->timer_id = 0;

    tt = time(NULL);
	tt = ((tt + 1800) / 3600) * 3600;
    gmtime_r(&tt, &tm);

	g_free(provider->title);
	provider->title = g_strdup_printf("WO-EU. %02d:%02dZ", tm.tm_hour, tm.tm_min);

    url = g_strdup(/*"http://maps.nagano.cz/weatheronline_cal3.png"*/ "http://maps.vaiz.cz/eueu.php");

    provider->http = zhttp_init();
    dbg("weatheronline url = '%s'\n", url);
    zhttp_get(provider->http, zsel, url, rain_weatheronline_downloaded, provider);
    g_free(url);
	provider->timer_id = zselect_timer_new(zsel, 50*1000, rain_weatheronline_timer, provider); // restart when freezes
}

void rain_weatheronline_downloaded(struct zhttp *http){
    int remains;
    struct timeval tv;
    struct rain_provider *provider = (struct rain_provider*)http->arg;
    SDL_Surface *newimg;
    
    if (provider->timer_id) {
        zselect_timer_kill(zsel, provider->timer_id);
        provider->timer_id = 0;
    }
	
	if (provider->rain->debug) zbinbuf_write_to_file(http->response, "_weatheronline.png", http->dataofs, http->response->len - http->dataofs);

	if (http->response->len <= http->dataofs + 8 ||
		strncmp(http->response->buf + http->dataofs, "\x89PNG\r\n\x1a\n", 8) != 0){ // image not available
		dbg("No PNG returned\n");
		provider->pal = provider->rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_wetteronline_timer, provider);
		goto x;
	}

    newimg = zpng_create(http->response->buf + http->dataofs, http->response->len - http->dataofs);
    if (newimg == NULL){
        dbg("Can't read bitmap\n");
        provider->pal = provider->rain->bwcolors;
		provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_weatheronline_timer, provider);
    }else{
        if (provider->img != NULL) {
            SDL_FreeSurface(provider->img);
        }
        provider->img = newimg;
        provider->pal = provider->rain->colors;
        rain_weatheronline_load(provider);
		if (provider->rain->debug) {
			rain_provider_draw_colors(provider, provider->img);
			zpng_save(provider->img, "_weatheronline2.png", NULL);
		}
		rain_analyze_scps(provider->rain);

		//provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_weatheronline_timer, provider);
	    gettimeofday(&tv, NULL);
    	remains = 300000 - ((tv.tv_sec % 300) * 1000 + tv.tv_usec/1000) + 500;
	    provider->timer_id = zselect_timer_new(zsel, remains, rain_weatheronline_timer, provider);
    }
x:;
	zhttp_free(http);
	provider->http = NULL;
	sw_set_gdirty(SWT_MAP);

    //ST_STOP("rain_downloaded");
}

// colors are in image format
void rain_weatheronline_load(struct rain_provider *provider){
	int ix, iy, c, ci, i;
	SDL_Surface *img = provider->img;
	
	provider->srccols[0] = (int)SDL_MapRGB(img->format,  250, 250, 243); // 0-0.25 mm/h
	//provider->srccols[0] = (int)SDL_MapRGB(img->format,  148, 148, 148); // calibration test
	provider->srccols[1] = (int)SDL_MapRGB(img->format,  252, 255, 193); // 0.25-1 
	provider->srccols[2] = (int)SDL_MapRGB(img->format,  251, 255,  92); // 1-2
	provider->srccols[3] = (int)SDL_MapRGB(img->format,  160, 214,  38); // 2-5
	provider->srccols[4] = (int)SDL_MapRGB(img->format,   69, 195, 121); // 5-10
	provider->srccols[5] = (int)SDL_MapRGB(img->format,    0, 214, 216); // 10-15
	provider->srccols[6] = (int)SDL_MapRGB(img->format,   17, 161, 214); // 15-20
	provider->srccols[7] = (int)SDL_MapRGB(img->format,    9, 50,  253); // 20-30
	provider->srccols[8] = (int)SDL_MapRGB(img->format,  164,  77, 196); // 30-50
	provider->srccols[9] = (int)SDL_MapRGB(img->format,  228,  75, 209); // 50-80
	provider->srccols[10] = (int)SDL_MapRGB(img->format, 238,  39,   8); // 80-100
	provider->srccols[11] = (int)SDL_MapRGB(img->format, 156,  28,  13); // 100-150
	provider->srccols[12] = (int)SDL_MapRGB(img->format,  99,  21,  14); // 150-inf.

	provider->translation[0] = 3;
	provider->translation[1] = 4;
	provider->translation[2] = 5; // 1 mm/h
	provider->translation[3] = 6;
	provider->translation[4] = 8;
	provider->translation[5] = 9; // 10 mm/h
	provider->translation[6] = 10;
	provider->translation[7] = 10;
	provider->translation[8] = 11;
	provider->translation[9] = 12;
	provider->translation[10] =	12;
	provider->translation[11] = 13; // 100 mm/h
	provider->translation[12] =	14;
	
	provider->w = img->w;
	provider->h = img->h;
	
	for (i = 0; i < RAIN_COLORS; i++) {
		GHashTable *hash = provider->hashes[i];
		g_hash_table_foreach(hash, rain_truncate_hash, NULL);
	}

    for (iy = provider->top; iy < img->h && iy <= provider->bottom; iy++){
        for (ix = provider->left; ix < img->w && ix <= provider->right; ix++){
			Uint8 r, g, b;
            int found = 0;

			c = z_getpixel(img, ix, iy);
			/*if (ix == 60 && iy == 60){
				int a =0 ;
			} */
			
            i = 0;
            do {
				SDL_GetRGB(c, img->format, &r, &g, &b);
                if (ix + i < 0) break;
                if (i < -10) break;
                i--;
				c = z_getpixel(img, ix + i, iy);
				SDL_GetRGB(c, img->format, &r, &g, &b);
            } while (r <= 57 && (r == g && r == b && g == b));
			/*for (i = -1; i > -10 && ix + i >= 0; i--){
				c = z_getpixel(img, ix + i, iy);
				SDL_GetRGB(c, img->format, &r, &g, &b);
				if (r >= 28 || g >= 51 || b >= 153) break;
			}*/ 
			
			for (ci = 0; ci < provider->srccolslen; ci++){
				if (c == provider->srccols[ci]){
                    found = 1;
					ci = provider->translation[ci];
					if (ci < 5) break;

					rain_provider_add(provider, ix, iy, ci);
					if (provider->rain->debug) z_putpixel_fmt(img, ix, iy, provider->rain->colors[ci], sdl->screen->format); 
					break;
				}
			}
            if (!found){
                int r = z_r(img, c);
                int g = z_g(img, c);
                int b = z_b(img, c);
                if (r == g && g == b && b == r) continue;
                if (r == 0 && g == 0 && b == 0) continue;
                if (r == 57 && g == 57 && b == 57) continue;
                if (r == 148 && g == 148 && b == 147) continue;
                if (r == 148 && g == 148 && b == 148) continue;
                if (r == 129 && g == 129 && b == 129) continue;
                if (r == 200 && g == 200 && b == 200) continue;
                //dbg("unknown color at [%d,%d] = %d, %d, %d\n", ix, iy, r, g, b);
            }
		}
	}
}

gpointer rain_weatheronline_hash(short kx, short ky, int ci){
	unsigned int key = ((unsigned short)kx) >> 7;
	key |= (((unsigned short)ky) >> 1) & 0xff00;
	key |= ((unsigned char)ci) << 16; 
	return GUINT_TO_POINTER(key); 
}


void rain_weatheronline_img2hw(struct rain_provider *provider, float ix, float iy, double *h, double *w){
	double dx = ix - 234;
	double dy = iy + 280;
	double d = sqrt(dx * dx + dy * dy) / 12.6 - 4.75;
	double a = atan2(dy, dx);
	*w = 90 - d;
	*h = 90 - (a * 180 / M_PI) + 9.5;
	*h *= 27.5 / 22.0;

	*w *= M_PI / 180.0;
	*h *= M_PI / 180.0;
}

/* www.rainviewer.com */
void rain_rainviewer_init(struct rain_provider *provider){
	if (gses == NULL || provider == NULL) return;

	provider->rv_size = 256;
	double qrb = cfg->rain_maxqrb; // TODO
	double qtf;
	double maxh = 0, maxw = 0;
	for (qtf = 0.0; qtf <= 180.0; qtf += 30.0){
		double h, w;
		qrbqtf2hw(/*gses->myh*/ 0, gses->myw, qrb, qtf * M_PI / 180.0, &h, &w);
		double dw = fabs(w - gses->myw);
		if (h > maxh) maxh = h;
		if (dw > maxw) maxw = dw;
		//dbg("h=%3.1f  w=%3.1f\n", h * 180.0 / M_PI, w * 180 / M_PI);
	}
	dbg("qrb=%1.0f  maxh=%3.1f  maxw=%3.1f\n", qrb, maxh * 180.0 / M_PI, maxw * 180 / M_PI);

	provider->rv_zoom = tile_googlezoom(2 * maxh, 2 * maxw);
	provider->rv_pow = pow(2, provider->rv_zoom);
	provider->rv_mul = provider->rv_size * provider->rv_pow;
	dbg("zoom=%d  pow=%d  mul=%d\n", provider->rv_zoom, provider->rv_pow, provider->rv_mul);
	//provider->rv_zoom = 0;

	rain_rainviewer_hw2himg(provider, gses->myh, gses->myw, &provider->rv_mypx, &provider->rv_mypy);
	dbg("mypx=%d  mypy=%d\n", provider->rv_mypx, provider->rv_mypy);
	provider->rv_px0 = provider->rv_mypx - (provider->rv_size -1) / 2.0;
	provider->rv_py0 = provider->rv_mypy - (provider->rv_size - 1) / 2.0;
	dbg("px0=%d  py0=%d\n", provider->rv_px0, provider->rv_py0);


	double h, w;
	rain_rainviewer_img2hw(provider, 0, 0, &h, &w);
	dbg("0 0 = %3.2f %3.2f\n", h * 180.0 / M_PI, w * 180.0 / M_PI);
	rain_rainviewer_img2hw(provider, 128 * provider->rv_pow, 128 * provider->rv_pow, &h, &w);
	dbg("128 128 = %3.2f %3.2f\n", h * 180.0 / M_PI, w * 180.0 / M_PI);
	rain_rainviewer_img2hw(provider, 256 * provider->rv_pow, 256 * provider->rv_pow, &h, &w);
	dbg("256 256 = %3.2f %3.2f\n", h * 180.0 / M_PI, w * 180.0 / M_PI);
	dbg("\n");

	/*rain_rainviewer_img2hw(provider, 4352, 2560, &h, &w);
	dbg("4352 2560 = %3.2f %3.2f\n", h * 180.0 / M_PI, w * 180.0 / M_PI);
	rain_rainviewer_img2hw(provider, 4608, 2816, &h, &w);
	dbg("4608 2816 = %3.2f %3.2f\n", h * 180.0 / M_PI, w * 180.0 / M_PI);

	int ix, iy;
	rain_rainviewer_hw2himg(provider, 11.25 * M_PI / 180.0, 55.776575 * M_PI / 180.0, &ix, &iy);
	dbg("%d %d\n", ix, iy);
	rain_rainviewer_hw2himg(provider, 22.5 * M_PI / 180.0, 48.922497 * M_PI / 180.0, &ix, &iy);
	dbg("%d %d\n", ix, iy);	   */



	/*double h, w;
	rain_rainviewer_img2hw(provider, gx, gy, &h, &w);
	h *= 180 / M_PI;
	w *= 180 / M_PI;*/

	provider->hpx1 = provider->rv_px0;  // x
	provider->hpx2 = provider->rv_px0 + provider->rv_size - 1; // x
	provider->wpx1 = provider->rv_py0 + provider->rv_size - 1; // y
	provider->wpx2 = provider->rv_py0; // y

	provider->left = provider->rv_px0;
	provider->top = provider->rv_py0;
	provider->right = provider->rv_px0 + provider->rv_size - 1;
	provider->bottom = provider->rv_py0 + provider->rv_size - 1;


	rain_rainviewer_img2hw(provider, provider->hpx1, provider->wpx1+1, &provider->h1, &provider->w1);
	rain_rainviewer_img2hw(provider, provider->hpx2+1, provider->wpx2, &provider->h2, &provider->w2);
	dbg("dh = %3.2f,  dw = %3.2f \n", fabs(provider->h2 - provider->h1) * 180.0 / M_PI, fabs(provider->w2 - provider->w1) * 180.0 / M_PI);
	dbg("h = %3.2f ... %3.2f   (my %3.2f)\n", provider->h1 * 180.0 / M_PI, provider->h2 * 180.0 / M_PI, gses->myh * 180.0 / M_PI);
	dbg("w = %3.2f ... %3.2f   (my %3.2f) \n", provider->w1 * 180.0 / M_PI, provider->w2 * 180.0 / M_PI, gses->myw * 180.0 / M_PI);

	provider->hmult = (provider->h2 - provider->h1) / (provider->hpx2 - provider->hpx1);
	provider->wmult = (provider->w2 - provider->w1) / (provider->wpx2 - provider->wpx1);



}

int tile_googlezoom(double dh, double dw)
{
	double hexp, wexp, gzh, gzw;
	int gz;

	hexp = 2 * M_PI / fabs(dh);
	if (hexp < 2.0)
		gzh = 0;
	else
		gzh = log(hexp) / log(2);
	//dbg("hexp=%4.2f  gzh=%4.2f \n", hexp, gzh);

	//wexp = M_PI / fabs(dw);
	wexp = ((2 * MAXLAT) * M_PI) / fabs(dw);
	if (wexp < 2.0)
		gzw = 0;
	else
		gzw = log(wexp) / log(2);
	//dbg("wexp=%4.2f  gzw=%4.2f \n", wexp, gzw);

	gz = (int)floor(Z_MIN(gzw, gzh));
	//dbg("gz=%d\n", gz);
	if (gz > 20) gz = 20;

	return (int)gz;
}

void rain_rainviewer_timer(void *arg){
	char *url;
	struct rain_provider *provider = (struct rain_provider*)arg;

	provider->timer_id = 0;
	
	url = "https://api.rainviewer.com/public/weather-maps.json";

	provider->http = zhttp_init();
	dbg("rainviewer %p url = '%s'\n", provider, url);
	zhttp_get(provider->http, zsel, url, rain_rainviewer_downloaded_json, provider);

	if (provider->timer_id) {
		zselect_timer_kill(zsel, provider->timer_id);
		provider->timer_id = 0;
	}
	provider->timer_id = zselect_timer_new(zsel, 60 * 1000, rain_rainviewer_timer, provider);
}

void rain_rainviewer_downloaded_json(struct zhttp *http){
	struct rain_provider *provider = (struct rain_provider*)http->arg;

	
	if (provider->timer_id) {
		zselect_timer_kill(zsel, provider->timer_id);
		provider->timer_id = 0;
	}

	if (http->state == ZHTTPST_ERROR && http->errorstr){
		dbg("ERROR rain_rainviewer_downloaded_json: %s\n", http->errorstr);
	} else {
		dbg("rain_rainviewer_downloaded_json: got %d bytes\n", http->response->len - http->dataofs);

		if (http_is_content_type(http, "application/json")) {

			if (provider->rain->debug) zbinbuf_write_to_file(http->response, "_rainviewer.json", http->dataofs, http->response->len - http->dataofs);

			char* json = g_new(char, http->response->len + 1);
			zbinbuf_getstr(http->response, http->dataofs, json, http->response->len + 1);
			zhttp_free(http);
			provider->http = NULL;

			rain_rainviewer_process_json(provider, json);
			g_free(json);

		}
	}

	if (provider->timer_id) {
		zselect_timer_kill(zsel, provider->timer_id);
		provider->timer_id = 0;
	}
	provider->timer_id = zselect_timer_new(zsel, 60 * 1000, rain_rainviewer_timer, provider);
}

void rain_rainviewer_process_json(struct rain_provider *provider, const char *json){
	int len = strlen(json);
	int i;
	time_t tt = 0;
	for (i = 0;; i++){
		char key[246];
		g_snprintf(key, sizeof(key), "radar.past.%d.time", i);
		char *s = zjson0_get_str(json, len, key);
		if (s == NULL) break;

		tt = (time_t)strtoll(s, NULL, 10);
		g_free(s);
		//dbg("%d  tt=%lld\n", i, (long long)tt);

		g_snprintf(key, sizeof(key), "radar.past.%d.path", i);
		s = zjson0_get_str(json, len, key);
		if (s == NULL) break;

		g_free(provider->rv_path);
		provider->rv_path = s; 
	}
	char *host = zjson0_get_str(json, len, "host");
	struct tm tm;
	gmtime_r(&tt, &tm);
	//dbg("rainviewer latest = %lld = %02d:%02dZ\n", (long long)tt,  tm.tm_hour, tm.tm_min);

	if (host != NULL && (provider->rv_oldpath == NULL || strcmp(provider->rv_path, provider->rv_oldpath) != 0)){
		
		g_free(provider->title);
		provider->title = g_strdup_printf("RainV. %02d:%02dZ", tm.tm_hour, tm.tm_min);


		char *url = g_strdup_printf("%s%s/%d/%d/%6.4f/%6.4f/0/0_0.png", host, provider->rv_path, provider->rv_size, provider->rv_zoom, gses->myw * 180.0 / M_PI, gses->myh * 180.0 / M_PI);
		//url = g_strdup("https://tile.openstreetmap.org/0/0/0.png");
		//url = g_strdup("https://tile.openstreetmap.org/5/17/10.png");

		provider->http = zhttp_init();
		dbg("rainviewer %02d:%02dZ = '%s'\n", tm.tm_hour, tm.tm_min, url);
		zhttp_get(provider->http, zsel, url, rain_rainviewer_downloaded_image, provider);
		g_free(url);
	}
    g_free(host);
	g_free(provider->rv_oldpath);
	provider->rv_oldpath = g_strdup(provider->rv_path);

}

void rain_rainviewer_downloaded_image(struct zhttp *http){
	struct rain_provider *provider = (struct rain_provider*)http->arg;
	SDL_Surface *newimg;
	
	if (provider->timer_id) {
		zselect_timer_kill(zsel, provider->timer_id);
		provider->timer_id = 0;
	}

	if (http->state == ZHTTPST_ERROR && http->errorstr){
		dbg("ERROR rain_rainviewer_downloaded_image: %s\n", http->errorstr);
		zhttp_free(http);
		provider->http = NULL;
		g_assert(provider->timer_id == 0);
		provider->timer_id = zselect_timer_new(zsel, 60 * 1000, rain_rainviewer_timer, provider);
		g_free(provider->rv_oldpath);
		provider->rv_oldpath = NULL;
		return;
	}

	if (http_is_content_type(http, "image/png")){
		dbg("rain_rainviewer_downloaded_image: got %d bytes\n", http->response->len - http->dataofs);

		if (provider->rain->debug) zbinbuf_write_to_file(http->response, "_rainviewer.png", http->dataofs, http->response->len - http->dataofs);


		newimg = zpng_create(http->response->buf + http->dataofs, http->response->len - http->dataofs);
		if (newimg == NULL){
			dbg("Can't read rainviewer bitmap\n");
			provider->pal = provider->rain->bwcolors;
			g_free(provider->rv_oldpath);
			provider->rv_oldpath = NULL;
		}
		else{
			dbg("rainviewer bitmap loaded, %dx%d\n", newimg->w, newimg->h);
			if (provider->img != NULL) {
				SDL_FreeSurface(provider->img);
			}
			provider->img = newimg;
			provider->pal = provider->rain->colors;
			rain_rainviewer_load(provider);
			rain_analyze_scps(provider->rain);
		}
		zhttp_free(http);
		provider->http = NULL;
		sw_set_gdirty(SWT_MAP);
	}
	g_assert(provider->timer_id == 0);
	provider->timer_id = zselect_timer_new(zsel, 1 * 60 * 1000, rain_rainviewer_timer, provider);

}


void rain_rainviewer_load(struct rain_provider *provider){
	int ix, iy, c, ci, i;
	SDL_Surface *img = provider->img;

	provider->w = img->w;
	provider->h = img->h;

	for (i = 0; i < RAIN_COLORS; i++) {
		GHashTable *hash = provider->hashes[i];
		g_hash_table_foreach(hash, rain_truncate_hash, NULL);
	}


	for (iy = 0; iy < img->h; iy++){
		for (ix = 0; ix < img->w; ix++){
			c = z_getpixel_fmt(img, ix, iy, sdl->screen->format);

			int red = z_r(img, c);
			ci = (red - 36) / 4;
			//if (ci < 0) continue;
			if (ci < 5) continue;
			if (ci >= RAIN_COLORS) ci = RAIN_COLORS - 1;

			rain_provider_add(provider, provider->rv_px0 + ix, provider->rv_py0 + iy, ci/*5*/);
		}
	}
}

gpointer rain_rainviewer_hash(short kx, short ky, int ci){
	unsigned int key = ((unsigned short)kx) >> 7;
	key |= (((unsigned short)ky) >> 1) & 0xff00;
	key |= ((unsigned char)ci) << 16;
	return GUINT_TO_POINTER(key);
}

// y = (ln(tan(pi/4 + (x*pi/180)/2)))  * 90/pi for x = -85 .. 85

void rain_rainviewer_hw2himg(struct rain_provider *provider, double h, double w, int *x, int *y){
	*x = provider->rv_mul * (h + M_PI) / (2 * M_PI);
	*y = provider->rv_mul * (M_PI - log(tan(M_PI_4 + w / 2))) / (2 * M_PI);
}

void rain_rainviewer_img2hw(struct rain_provider *provider, float x, float y, double *h, double *w){
	*h = (x * 2 * M_PI / provider->rv_mul) - M_PI;
	*w = 2 * (atan(exp(M_PI - y * 2 * M_PI / provider->rv_mul)) - M_PI_4);
}





#endif
