/*
    zcor.c - Polar map data
    Copyright (C) 2002-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.

*/


#include <libziaint.h>


#ifdef Z_HAVE_SDL


#include "zcor.h"
//#include "cordata.h"

#include "zdebug.h"
#include "zloc.h"
#include "zthread.h"
#include "zselect.h"

#include <math.h>

struct zcor *gcor = NULL;
static struct zcor *gcor2 = NULL;

struct kmarray *kmarray_new(void){
    struct kmarray *kma;

    kma = g_new0(struct kmarray, 1);
    kma->size=100;
    kma->data=g_new0(struct kmpoint, kma->size);
    return kma;
}

void kmarray_add(GHashTable *hash, gpointer key, struct kmpoint *km){
	struct kmarray *kma;
	gpointer orig_key, value;
	
	if (g_hash_table_lookup_extended(hash, key, &orig_key, &value)){
		kma=(struct kmarray *)value;
	}else{
		kma=kmarray_new();
	 /*   dbg("new kma for %p: %p\n",key,  kma);*/
		g_hash_table_insert(hash, key, kma);
	}
		
    if (kma->len == kma->size){
        kma->size+=100; 
        kma->data = g_renew(struct kmpoint, kma->data, kma->size);
    }
    memcpy(&kma->data[kma->len], km, sizeof(struct kmpoint));    
    kma->len++;
}


#define TO_READ 1000


struct zcor *zcor_init(struct zselect *zsel, void(*callback)(void), const struct cpoint *file, int cor_items){
	struct zcor *cor;

    cor = g_new0(struct zcor, 1);
	cor->km = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_km_value);
	cor->wwl4 = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_km_value);
	cor->wwl2 = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_km_value);
	cor->az =   g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_km_value);
    cor->thread_break = 0;
	cor->file = file;
	cor->items = cor_items;
	cor->loc = g_strdup("");
	cor->zsel = zsel;
	cor->callback = callback;
    return cor;
}

void zcor_free(struct zcor *cor){

	if (!cor) return;


    if (cor->thread){
        cor->thread_break = 1;

        //dbg("join cor...\n");
        g_thread_join(cor->thread);
        //dbg("done\n");
        cor->thread = NULL;
    }
	g_free(cor->loc);
	g_hash_table_destroy(cor->km); 
	g_hash_table_destroy(cor->wwl4); 
	g_hash_table_destroy(cor->wwl2); 
	g_hash_table_destroy(cor->az); 
	
    g_free(cor);
}

void zcors_free(){
	zcor_free(gcor2);
	zcor_free(gcor);
}

struct zcor *zcor_calc(double myh, double myw, gchar *locator, struct zselect *zsel, void (*callback)(void), const struct cpoint *file, int cor_items){ 
  	struct zcor *cor = zcor_init(zsel, callback, file, cor_items);
	if (gcor) cor->nth = gcor->nth + 1;
    g_free(cor->loc);
	cor->loc = g_strdup(locator);
    cor->sw_myw = myw;
    cor->sw_myh = myh;
    cor->thread = g_thread_try_new("cor", zcor_thread_func, (gpointer)cor, NULL);
    if (!cor->thread) zinternal("Can't run cor thread");

    if (callback == NULL){
        g_thread_join(cor->thread);
		cor->thread = NULL;
    }
    return cor;
}

void zcors_recalc(double myh, double myw, gchar *locator, struct zselect *zsel, void (*callback)(void), const struct cpoint *file, int cor_items){ 
	char tmploc[10];
	hw2loc(tmploc, myh * 180.0/M_PI, myw * 180.0/M_PI, 6);
    //dbg("--------\ncor_recalc('%s')\n", tmploc);

	if (gcor && strcmp(gcor->loc, locator) == 0){
		//dbg("same locators as gcor\n");
		return;
	}
    if (gcor2 && strcmp(gcor2->loc, locator) == 0){
		//dbg("same locators as gcor2, wait for pending recalc\n");
		return;
	}
    if (gcor2) {
        zcor_free(gcor2);
    }

	gcor2 = zcor_calc(myh, myw, locator, zsel, callback, file, cor_items);
	if (callback == NULL) zcor_switch();
}


gpointer zcor_thread_func(gpointer arg){ 
    int i,j;
	const struct cpoint *cp;
    struct kmpoint km, kmfirst, kmlast;
	double h2, w2;
    gpointer hash, oldhash;
	int kx, ky, color;
    struct zcor *cor = (struct zcor *)arg;
	
	zg_thread_set_name("Libzia cor");
   /* dbg("recalc_cor(%s)\n", locator);*/
   /* return 0; */
//    ST_START;

    kmfirst.c=0;
    //dbg("cor_thread_func: items=%d h=%f w=%f\n", cor->items, cor->sw_myh, cor->sw_myw);
	g_hash_table_remove_all (cor->km); 
    oldhash=(gpointer)-1;
	for (i=0, cp=cor->file; i<cor->items; i++, cp++){
        if (cor->thread_break) {
			dbg("cor thread breaked\n");
			return NULL;
		}
        w2=(M_PI*(double)cp->w)/18000;
		h2=(M_PI*(double)cp->h)/18000;
		hw2km(cor->sw_myh, cor->sw_myw, h2, w2, &kx, &ky);
		hash=k2key(kx, ky);
		km.c=cp->c;
		km.kx=kx;
		km.ky=ky;
//        dbg("cp=%d %d %d  km=%d %d %d\n", cp->w, cp->h, cp->c, km.kx, km.ky, km.c);
		if (km.c<0) {
			memcpy(&kmfirst, &km, sizeof(kmfirst));
		}
		if (oldhash!=(gpointer)-1 && hash!=oldhash) {
			kmarray_add(cor->km, oldhash, &km);
			kmlast.c=kmfirst.c;
            kmarray_add(cor->km, hash, &kmlast);
		}    
			
		kmarray_add(cor->km, hash, &km);
		memcpy(&kmlast, &km, sizeof(kmlast));
		oldhash=hash;
	}
    
    /* large wwls (JN69) */
	g_hash_table_remove_all(cor->wwl4); 
    for (i=-89;i<90;i++){
        if (cor->thread_break) {
			dbg("cor thread breaked\n");
			return NULL;
		}
        if (i%10==0) color=-128;
        else color=-127;
        oldhash=(gpointer)-1;
        km.c=color;
        for (j=0;j<=360;j++){
            w2=(M_PI*i)/180;
            h2=(M_PI*j)/180;
            hw2km(cor->sw_myh, cor->sw_myw, h2, w2, &kx, &ky);
            hash=k2key(kx, ky);
            km.kx=kx;
            km.ky=ky;
            if (oldhash!=(gpointer)-1 && hash!=oldhash) {
                kmarray_add(cor->wwl4, oldhash, &km);
                kmlast.c=color;
                kmarray_add(cor->wwl4, hash, &kmlast);
                km.c=0;
            }
            
            kmarray_add(cor->wwl4, hash, &km);
			memcpy(&kmlast, &km, sizeof(kmlast));
            
            oldhash=hash;
            km.c=0;
        }
    }
    for (j=0;j<360;j+=2){
        if (cor->thread_break) {
			dbg("cor thread breaked\n");
			return NULL;
		}
        oldhash=(gpointer)-1;
        if (j%20==0) color=-128;
        else color=-127;
        km.c=color;
        for (i=-89;i<=89;i++){
            w2=(M_PI*i)/180;
            h2=(M_PI*j)/180;
            hw2km(cor->sw_myh, cor->sw_myw, h2, w2, &kx, &ky);
            hash=k2key(kx, ky);
            km.kx=kx;
            km.ky=ky;
            if (oldhash!=(gpointer)-1 && hash!=oldhash) {
                kmarray_add(cor->wwl4, oldhash, &km);
                kmlast.c=color;
                kmarray_add(cor->wwl4, hash, &kmlast);
                km.c=0;
            }
            kmarray_add(cor->wwl4, hash, &km);
			memcpy(&kmlast, &km, sizeof(kmlast));
            oldhash=hash;
            km.c=0;
        }
    } 
    /* very large wwls (JN) */
	g_hash_table_remove_all(cor->wwl2); 
    for (i=-80;i<90;i+=10){
        if (cor->thread_break) {
			dbg("cor thread breaked\n");
			return NULL;
		}
        if (i%10==0) color=-128;
        else color=-127;
        oldhash=(gpointer)-1;
        km.c=color;
        for (j=0;j<=360;j++){
            w2=(M_PI*i)/180;
            h2=(M_PI*j)/180;
            hw2km(cor->sw_myh, cor->sw_myw, h2, w2, &kx, &ky);
            hash=k2key(kx, ky);
            km.kx=kx;
            km.ky=ky;
            if (oldhash!=(gpointer)-1 && hash!=oldhash) {
                kmarray_add(cor->wwl2, oldhash, &km);
                kmlast.c=color;
                kmarray_add(cor->wwl2, hash, &kmlast);
                km.c=0;
            }
            
            kmarray_add(cor->wwl2, hash, &km);
			memcpy(&kmlast, &km, sizeof(kmlast));
            
            oldhash=hash;
            km.c=0;
        }
    }
    for (j=0;j<360;j+=20){
        if (cor->thread_break) {
			dbg("cor thread breaked\n");
			return NULL;
		}
        oldhash=(gpointer)-1;
        if (j%10==0) color=-128;
        else color=-127;
        km.c=color;
        for (i=-89;i<=89;i++){
            w2=(M_PI*i)/180;
            h2=(M_PI*j)/180;
            hw2km(cor->sw_myh, cor->sw_myw, h2, w2, &kx, &ky);
            hash=k2key(kx, ky);
            km.kx=kx;
            km.ky=ky;
            if (oldhash!=(gpointer)-1 && hash!=oldhash) {
                kmarray_add(cor->wwl2, oldhash, &km);
                kmlast.c=color;
                kmarray_add(cor->wwl2, hash, &kmlast);
                km.c=0;
            }
            kmarray_add(cor->wwl2, hash, &km);
			memcpy(&kmlast, &km, sizeof(kmlast));
            oldhash=hash;
            km.c=0;
        }
    } 

	g_hash_table_remove_all(cor->az); 
	for (j = 0; j < 360; j += 15){
        if (cor->thread_break) {
			dbg("cor thread breaked\n");
			return NULL;
		}
        oldhash=(gpointer)-1;
		color = j % 90 ? -127 : -128; 
		km.c = color;
		for (i = 50; i < (int)(ZLOC_R_EARTH * M_PI); i += 50){
			kx = (int)(i * cos(j * M_PI / 180.0));
			ky = (int)(i * sin(j * M_PI / 180.0));

            hash=k2key(kx, ky);
            km.kx=kx;
            km.ky=ky;
            if (oldhash!=(gpointer)-1 && hash!=oldhash) {
                kmarray_add(cor->az, oldhash, &km);
                kmlast.c=color;
                kmarray_add(cor->az, hash, &kmlast);
                km.c=0;
            }
            kmarray_add(cor->az, hash, &km);
			memcpy(&kmlast, &km, sizeof(kmlast));
            oldhash=hash;
            km.c=0;
		}
	}
//    ST_STOP;
   // sleep(5); 

    if (cor->zsel) zselect_msg_send(cor->zsel, "COR");
	//dbg("cor finished\n");
	return NULL;
}


void zcor_switch(){
	zcor_free(gcor);
	gcor = gcor2;
	gcor2 = NULL;
}

void zcor_read_handler(int n, char **items){
//    dbg("cor_read_handler\n");
    if (!gcor2 || !gcor2->thread) return;

	zcor_switch();
 	if (gcor->callback != NULL) gcor->callback();
	//cor_dump(cor);

}



// gboolean free_km_item(gpointer key, gpointer value, gpointer user_data){
// 	struct kmarray *kma;

// 	/* key is int, not freeed */
// 	kma=(struct kmarray *) value;
//    	if (!kma) return 0;
// 	g_free(kma->data);
// 	g_free(kma);
//     return 1;
// }

void free_km_value(gpointer value){
    struct kmarray *kma = (struct kmarray *) value;
   	if (!kma) return;
	g_free(kma->data);
	g_free(kma);
}

gpointer k2key(int kx, int ky){
	return GUINT_TO_POINTER((kx & COR_KM_MASK) | ( (ky & COR_KM_MASK) << 16)) ; 
}


static void zcor_dump1(gpointer key, gpointer value, gpointer data){
	FILE *f = (FILE *)data;
	struct kmarray *kma = (struct kmarray *)value;
	int i;

	fprintf(f, "hash=%p\n", key);
	for (i = 0; i < kma->len; i++){
		fprintf(f, "%d %d %d\n", (int)kma->data[i].c, (int)kma->data[i].kx, (int)kma->data[i].ky);
	}
}

void zcor_dump(struct zcor *cor){
	FILE *f = fopen("cordump.txt", "wt");
	if (!f) return;
	fprintf(f, "cor_dump start\n");
	g_hash_table_foreach(cor->km, zcor_dump1, f);
	fprintf(f, "cor_dump end\n");
	fclose(f);
}


#endif
