/*
    zloc.c - Maidenhead WW locator functions
    Copyright (C) 2002-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 <glib.h>

#include <ctype.h>
#define _USE_MATH_DEFINES // for MSVC
#include <math.h>
#include <stdio.h>
#include <string.h>

#include <zloc.h>

#undef LOCDEBUG

#define sqr(x) ((x)*(x))       


static void tostr(GString *gs, double x){
    int a,b;

    x=x*180/M_PI;
    a=(int)x;
    if (x>0)
        x-=a;
    else
        x=a-x;
    x*=60;
    b=(int)x;
    if (x>0)
        x-=b;
    else
        x=b-x;
    x*=60;

    g_string_append_printf(gs, "%d.%d'%3.1f\"", a, b, x);
}


/******************* QRB **************************/

double qth(const char *qth,int width) {
    const char* c;
    char d;
    double res;
    
   /* dbg("qth('%s',%d)\n", qth, width);*/
    c=qth;
    while (1){
        d=tolower(*c);
        if (!d) return(-100);
        if (d>='a' || d<='r') break;
        c++;
    }
    if (width&1) c++;
    d=tolower(*c);
    if (strlen(c)<3) return(-100);
    
    res=(d-106)*M_PI/9.0;
    c+=2;

    
    if (!isdigit(*c)) return(-100);
    res+=(*c-'0')*M_PI/90.0;
    c+=2;
    
/*   dbg("strlen(c)=%d, strlen(qth)=%d\n", strlen(c), strlen(qth));  */
    
    if (strlen(qth)>=6){
        d=tolower(*c);
        if (d<'a' || d>'x') return(-100);
        res+=(d-97)*M_PI/2160.0;
	    res+=M_PI/4320.0;
    }else{
        if (width&2) res+=M_PI/180;
    }
    
    
    /*if (strlen(c)>=8){
        c+=2;
        if (isdigit(*c))
            res+=(*c-'0')*M_PI/21600.0;
        if (width&2) res+=M_PI/43200.0;
    }else{
        if (width&2) res+=M_PI/4320.0;
    } */
    
    if (width&1) res/=2;

    
    return(res);
}

/* compute QRB, not QSO points! */
int qrbqtf(char *myqth,char *recqth,double *qrb,double *qtf,GString *gs, int flags){
    double h1,w1,h2,w2;
    //char s1[20],s2[20],s3[20],s4[20];
    int ret;
    
    *qrb=-1;
    *qtf=-1;

    if ((h1=qth(myqth,0|flags))<-10) return(-1);
    if ((w1=qth(myqth,1|flags))<-10) return(-1);
    if ((h2=qth(recqth,0|flags))<-10) return(-1);
    if ((w2=qth(recqth,1|flags))<-10) return(-1);

    /*dbg("h1=%7.4f w1=%7.4f h2=%7.4f  w2=%7.4f \n",h1,w1,h2,w2);*/
#ifdef LOCDEBUG    
    fprintf(stderr, "%s->%s, h1=%7.9f w1=%7.9f h2=%7.9f  w2=%7.9f \n",myqth,recqth,h1,w1,h2,w2);
    fprintf(stderr, "h1=%7.9fdeg w1=%7.9fdeg h2=%7.9fdeg  w2=%7.9fdeg \n",h1*180.0/M_PI,w1*180.0/M_PI,h2*180.0/M_PI,w2*180.0/M_PI);
#endif    

    if (gs!=NULL){
		g_string_truncate(gs, 0);
        g_string_append_printf(gs, "From: ");
		tostr(gs, h1);
		g_string_append_printf(gs, "E  ");
		tostr(gs, w1);
		g_string_append_printf(gs, "N  to ");
		tostr(gs, h2);
		g_string_append_printf(gs, "E  ");
		tostr(gs, w2);
		g_string_append_printf(gs, "N");
    }

    ret = hw2qrbqtf(h1, w1, h2, w2, qrb, qtf);
    *qtf=*qtf * 180.0/M_PI;
	/*dbg("qrbqtf(%s->%s %5.3fkm %3.0fdeg\n", myqth, recqth, *qrb, *qtf);*/
    return ret;
}

/* compute QRB, not QSO points! */
int hw2qrbqtf(double h1, double w1, double h2, double w2, double *qrb, double *qtf){
    double dh,dw;
#ifdef LOCDEBUG    
    dbg("h1=%7.9f w1=%7.9f h2=%7.9f w2=%7.9f ",h1,w1,h2,w2);
#endif    
	double debil;
	

    dh=h2-h1;
	debil = sin(w1)*sin(w2)+cos(w1)*cos(w2)*cos(dh);
	if (debil < -1.0) debil = -1.0;
	if (debil > 1.0) debil = 1.0;
    *qrb=ZLOC_R_EARTH*acos(debil);
    dw=tan(w2)*cos(w1)-sin(w1)*cos(dh);
    
#ifdef LOCDEBUG
    fprintf(stderr, "dh=%7.9f qrb=%7.9f dw=%7.9f\n", dh, *qrb, dw);
#endif    
    *qtf=atan2(sin(dh),dw);
    if (*qtf<0) *qtf+=2*M_PI;
    return 0;
}

void hw2km(double h1, double w1, double h2, double w2, int *kx, int *ky){
	double qrb, qtf;
    
	hw2qrbqtf(h1, w1, h2, w2, &qrb, &qtf);
/*    dbg("qrb=%f qtf=%f\n", qrb, qtf);*/
    *kx = (int)(  qrb*sin(qtf));
    *ky = (int)(- qrb*cos(qtf));
}

void hw2km_d(double h1, double w1, double h2, double w2, double *kx, double *ky){
	double qrb, qtf;
    
	hw2qrbqtf(h1, w1, h2, w2, &qrb, &qtf);
/*    dbg("qrb=%f qtf=%f\n", qrb, qtf);*/

    *kx =   qrb*sin(qtf);
    *ky = - qrb*cos(qtf);
}

void hw2km_f(double h1, double w1, double h2, double w2, float *fkx, float *fky){
	double kx, ky;
	hw2km_d(h1, w1, h2, w2, &kx, &ky);

	/*if (ikx < -32768) ikx = -32768;
	if (ikx > 32767) ikx = 32767;
	if (iky < -32768) iky = -32768;
	if (iky > 32767) iky = 32767;*/

	*fkx = (float)kx;
	*fky = (float)ky;
}

void km2qrbqtf(int kx, int ky, double *qrb, double *qtf){
    int dx, dy;
    
    dx=kx;
    dy=ky;
    
	if (qrb){
		*qrb=sqrt((double)(sqr(dx)+sqr(dy)));
	}
	if (qtf){
		*qtf=atan2((double)dx,(double)-dy);   
		if (*qtf<0) *qtf+=2*M_PI;
	}
/*    dbg("km2qrbqtf(%d, %d)=%d, %d\n",kx, ky, (int)*qrb, (int)(*qtf*180.0/M_PI));*/
}

/*************** REVERSE QRB ***************************/
#define EPSILON 0.00000000005 
/* qtf in radian */
int qrbqtf2hw(double h1, double w1, double qrb, double qtf, double *h2, double *w2){
    double dh,  d;

    if (qrb>ZLOC_R_EARTH*M_PI) return -1;
    d = qrb/ZLOC_R_EARTH;
    
    *w2 = asin (sin(w1)*cos(d) + cos(w1)*sin(d)*cos(qtf));
    if (fabs(cos(*w2))< EPSILON) {
        *h2 = 0;
    }else{
        dh = atan2(sin(qtf)*sin(d)*cos(w1), 
                   cos(d)-sin(w1)*sin(*w2) );
        *h2 = fmod (M_PI +h1+dh, 2*M_PI)-M_PI;
    }
    return 0;
}

/*************** WWL RING *****************************/

int qthwr(char *qth,int width) {
    char *c,d;
    int res;
    
    c=qth;
    while (1){
        d=tolower(*c);
        if (!d) return(-1000);
        if (d>='a' || d<='r') break;
        c++;
    }
    if (width) c++;
    d=tolower(*c);
    if (strlen(c)<5) return(-1000);
    
    res=(d-106)*10;
    c+=2;

    if (!isdigit(*c)) return(-1000);
    res+=(*c-'0');
    
    return(res);
}

char *mkwwl4(char *buf, int w, int h){
/*   dbg("mkwwl4(%d, %d)\n", w, h);*/
   w+=90;
   h+=90;
   while (w<0) w+=180;
   while (h<0) h+=180;
   w%=180;
   h%=180;

   buf[0]='A'+ h/10;
   buf[1]='A'+ w/10;
   buf[2]='0'+ h%10;
   buf[3]='0'+ w%10;
   buf[4]='\0';
   
   return buf;
}

/* degrees */
char *hw2loc(char *buf, double h, double w, int len){
	int i = 0;

	// JN69QR76BB
	// h = 13.39209;
	// w = 49.733556;
	
    h+=180;
    w+=90;
    
    h=fmod(h,360.0);
    w=fmod(w,180.0);

    buf[i++]='A'+ (int)(h/20.0);
    buf[i++]='A'+ (int)(w/10.0);
	if (len <= 2) goto x;

    h=fmod(h,20);
    w=fmod(w,10);
    buf[i++]='0'+ (int)(h/2);
    buf[i++]='0'+ (int)(w);
	if (len <= 4) goto x;

    h=fmod(h,2);
    w=fmod(w,1);
    h*=12;
    w*=24;
    buf[i++]='A'+ (int)(h);
    buf[i++]='A'+ (int)(w);
	if (len <= 6) goto x;

	h=fmod(h, 1) * 10;
	w=fmod(w, 1) * 10;
	buf[i++]='0'+ (int)(h);
    buf[i++]='0'+ (int)(w);
	if (len <= 8) goto x;

	h=fmod(h, 1) * 24;
	w=fmod(w, 1) * 24;
	buf[i++]='A'+ (int)(h);
    buf[i++]='A'+ (int)(w);

x:;
    buf[i]='\0';
    return buf;
}


char *x2gramin(char *buf, int size, double x, char *signs){
    double xx;
    char sign;

    sign=x<0?signs[1]:signs[0];
    x = fabs(fmod(x, 360)); 
    xx = fmod(x, 1)*60.0;
    g_snprintf(buf, size, "%3d%c%02d", (int)x, sign, (int)xx);    
    return buf;
}


int qsopwr(char *myqth, char *recqth){
    int h1, h2, w1, w2, dh1, dh2, dh, dw, d;
    
    if ((h1=qthwr(myqth,0))<=-1000) return(-1);
    if ((w1=qthwr(myqth,1))<=-1000) return(-1);
    if ((h2=qthwr(recqth,0))<=-1000) return(-1);
    if ((w2=qthwr(recqth,1))<=-1000) return(-1);

    /*dbg("h1=%d  w1=%d  h2=%d  %w2=%d \n",h1,w1,h2,w2);*/

    
    dh1 = h1 - h2; if (dh1 < 0) dh1 += 180; 
    dh2 = h2 - h1; if (dh2 < 0) dh2 += 180; 
/*    dw1 = w1 - w2; if (dw1 < 0) dw1 += 180; 
    dw2 = w2 - w1; if (dw2 < 0) dw2 += 180; */

    dh = dh1 < dh2 ? dh1 : dh2;
/*    dw = dw1 < dw2 ? dw1 : dw2;*/
    dw = w2 - w1;
    if (dw < 0) dw = -dw;

    d = dh < dw ? dw : dh;
    
    return d+2;
}


char *compute_wwl4(char *s, double h, double w){
    int hi, wi;
    
    if (h < -180.0 ||
        h >  180.0 ||
        w <  -90.0 ||
        w >   90.0) {
            g_strlcpy(s, "", 5);
            return s;
    }
      
    h += 180.0;
    w +=  90.0;
    
    hi = (int)(h/20);
    s[0] = 'A' + hi;

    wi = (int)(w/10);
    s[1] = 'A' + wi;
        
    hi = (int)(h/2); hi %= 10;
    wi = (int)w;     wi %= 10;
    
    s[2] = '0' + hi;
    s[3] = '0' + wi;
    s[4] = '\0';
    
    return s;
}

//ctest ? ctest->pwwlo : cfg->pwwlo, 

void qrb_qtf_int(char *mywwl, char *wwl, int *qrb_int, int *qtf_int){
    double qrb, qtf;

    qrbqtf(mywwl, wwl, &qrb, &qtf, NULL, 0); 
    *qrb_int=(int)qrb;
    *qtf_int=(int)qtf;
	/*dbg("qrb_qtf_int(%s->%s %5dkm %3ddeg\n", ctest->pwwlo, wwl, *qrb_int, *qtf_int);*/
}


/* Compute points from QRB */
int iaru_round(double qrb){
    return ((int)(qrb+1.0));
}

/* s = "60AA", my = "JN69UN". Returns JO60AA. Must be enough space in s. All must be uppercase */
void z_nearest_wwl(char *s, char *my){
	char loc[8], minloc[8];
	double qrb, qtf, minqrb;
	int i;
	int dh[] = {-1, -1, -1,   0,  0,  +1, +1, +1};
	int dw[] = {-1,  0, +1,  -1, +1,  -1,  0, +1};
	
	if (!s || strlen(s) < 4 || 
		!my || strlen(my) < 6) {
		g_strlcpy(s, "", 2);
		return;
	}

	g_snprintf(minloc, sizeof(minloc), "%c%c%s", my[0], my[1], s);
	qrbqtf(my, minloc, &minqrb, &qtf, NULL, 0);

	for (i = 0; i < 8; i++){
		char h = my[0] + dh[i];
		char w = my[1] + dw[i];

		if (h < 'A') h = 'R';
		if (h > 'R') h = 'A';
		if (w < 'A') continue;
		if (h > 'R') continue;
		
		g_snprintf(loc, sizeof(loc), "%c%c%s", h, w, s);
		qrbqtf(my, loc, &qrb, &qtf, NULL, 0);
		if (qrb > minqrb) continue;
		minqrb = qrb;
		g_strlcpy(minloc, loc, sizeof(minloc));
	}
	g_strlcpy(s, minloc, 7);
}

#ifdef MAIN

int main(int argc,char *argv[]){
    char s[100],my[100]="jn69pv",rec[100]="jo60lj";
    double qrb,qtf;
    GString *gs = g_string_new("");

    while (1) {
        printf("\n zadej svuj lokator (%s) :",my);
        fgets(s,90,stdin);
        s[strlen(s)-1]='\0';
        if (strcmp(s,"k")==0) return(1);
        if (strlen(s)>0) strcpy(my,s);
        printf(" zadej prijaty lokator (%s) :",rec);
        fgets(s,90,stdin);
        s[strlen(s)-1]='\0';
        if (strcmp(s,"k")==0) return(1);
        if (strlen(s)>0) strcpy(rec,s);
        qrbqtf(my,rec,&qrb,&qtf, gs, 0);
        printf(" %s \n qrb:%8.1f km   qtf:%6.2f \n",gs->str,qrb,qtf);
        printf("WWL ring: %d ", qsopwr(my,rec));
    }
} 

#endif

