/*
* Raspberry PI / Zero AddOn Board specially for Ham Radio Applications
* ====================================================================
* Author: DJ0ABR
*
*   (c) DJ0ABR
*   www.dj0abr.de
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program; if not, write to the Free Software
*   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* 
kmhelper.cpp
===========

useful functions

*
*/

#include "../qo100trx.h"
#include <pwd.h>

int keeprunning = 1;    // set to 0 at program end to exit all processes
char configfile[512] = {0};

// check if it is already running
int isRunning(char *prgname)
{
    int num = 0;
    char s[256];
    sprintf(s,"ps -e | grep %s",prgname);
    
    FILE *fp = popen(s,"r");
    if(fp)
    {
        // gets the output of the system command
        while (fgets(s, sizeof(s)-1, fp) != NULL) 
        {
            if(strstr(s,prgname) && !strstr(s,"grep"))
            {
                if(++num == 2)
                {
                    printf("%s is already running, do not start twice !\n",prgname);
                    pclose(fp);
                    return 1;
                }
            }
        }
        pclose(fp);
    }
    return 0;
}

void (*sigfunc)();

// signal handler
void sighandler(int signum)
{
    //printf("\n\nprogram stopped by signal\n");

    (*sigfunc)();
}

void install_signal_handler(void (*signalfunction)())
{
    sigfunc = signalfunction;
    // signal handler, mainly used if the user presses Ctrl-C
    struct sigaction sigact;
    sigact.sa_handler = sighandler;
	sigemptyset(&sigact.sa_mask);
	sigact.sa_flags = 0;
	sigaction(SIGINT, &sigact, NULL);
	sigaction(SIGTERM, &sigact, NULL);
	sigaction(SIGQUIT, &sigact, NULL);
    sigaction(SIGABRT, &sigact, NULL); // assert() error
    
    //sigaction(SIGSEGV, &sigact, NULL);
    
    // switch off signal 13 (broken pipe)
    // instead handle the return value of the write or send function
    signal(SIGPIPE, SIG_IGN);
}

void getParameters(int argc, char *argv[])
{
	int opt;
	while ((opt = getopt(argc, argv, "c:")) != -1)
    {
        switch (opt)
        {
        case 'c':
            // specify config file name
            if(optarg && strlen(optarg) > 2 && strlen(optarg) < (sizeof(configfile)-1))
            {
                strcpy(configfile,optarg);
            }
            else
            {
                printf("invalid Config File Parameter [-c Configfilename]: %s\n", optarg);
                exit(0);
            }
            break;
        }
    }
}

// run external program and return it's output
char *runProgram(char *parameter, int maxlen)
{
    //printf("run:<%s>\n",parameter);
    FILE *fp = popen(parameter,"r");
    if(fp)
    {
        *parameter = 0;
        while (fgets(parameter+strlen(parameter), maxlen-1-strlen(parameter), fp) != NULL) 
        {
            //printf("returns:<%s>\n", parameter);
        }
        pclose(fp);
        return parameter;
    }
    else
        printf("ERROR: cannot run: %s\n",parameter);
    return NULL;
}

void showbitstring(char* title, uint8_t* data, int totallen, int anz)
{
    printf("%s. len %d: ", title, totallen);
    for (int i = 0; i < anz; i++)
        printf("%01X ", data[i]);
    printf("\n");
}

void showbytestring(char *title, uint8_t *data, int totallen, int anz)
{
    printf("%s. len % 4d: ",title, totallen);
    for(int i=0; i<anz; i++)
        printf("%02X ",data[i]);
    printf("\n");
}

void showbytestring16(char *title, uint16_t *data, int anz)
{
    printf("%s. len %d: ",title,anz);
    for(int i=0; i<anz; i++)
        printf("%04X ",data[i]);
    printf("\n");
}

void showbytestring32(char* title, uint32_t* data, int anz)
{
    printf("%s. len %d: ", title, anz);
    for (int i = 0; i < anz; i++)
        printf("%08X ", data[i]);
    printf("\n");
}

void showbytestringf(char* title, float* data, int totallen, int anz)
{
    printf("%s. len %d: ", title, totallen);
    for (int i = 0; i < anz; i++)
        printf("%7.4f ", data[i]);
    printf("\n");
}

// get own IP adress
char* ownIP()
{
    static char ip[20] = { 0 };

    struct ifaddrs* ifaddr, * ifa;
    int s;
    char host[NI_MAXHOST];

    if (getifaddrs(&ifaddr) == -1)
    {
        printf("cannot read own IP address, getifaddrs faield. Check Networking\n");
        return ip;
    }


    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
    {
        if (ifa->ifa_addr == NULL)
            continue;

        s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);

        if (ifa->ifa_addr->sa_family == AF_INET)
        {
            if (s != 0)
            {
                printf("getnameinfo() failed: %s\n", gai_strerror(s));
                printf("cannot read own IP address, getnameinfo failed: %s. Check Networking\n", gai_strerror(s));
                break;
            }
            if (strncmp(host, "127", 3) != 0)
            {
                strcpy(ip, host);
                break;
            }
        }
    }

    freeifaddrs(ifaddr);
    return ip;
}

char* cleanStr(char *s)
{
    if(s[0] > ' ') 
    {
        // remove trailing crlf
        for(size_t j=0; j<strlen(s); j++)
            if(s[j] == '\n' || s[j] == '\r') s[j] = 0;
        return s;    // nothing to do
    }

    for(size_t i=0; i<strlen(s); i++)
    {
        if(s[i] >= '0')
        {
            // i is on first character
            memmove(s,s+i,strlen(s)-i);
            s[strlen(s)-i] = 0;
            // remove trailing crlf
            for(size_t j=0; j<strlen(s); j++)
                if(s[j] == 'n' || s[j] == '\r') s[j] = 0;
            return s;
        }
    }
    return NULL;   // no text found in string
}

char *getword(char *s, int idx)
{
    if(idx == 0)
    {
        for(size_t i=0; i<strlen(s); i++)
        {
            if(s[i] < '0')
            {
                s[i] = 0;
                return s;
            }
        }
        return NULL;
    }

    for(size_t j=0; j<strlen(s); j++)
    {
        if(s[j] > ' ')
        {
            char *start = s+j;
            for(size_t k=0; k<strlen(start); k++)
            {
                if(start[k] == ' ' || start[k] == '\r' || start[k] == '\n')
                {
                    start[k] = 0;
                    return start;
                }
            }   
            return start;
        }
    }

    return NULL;
}

// read the value of an element from the config file
// Format:
// # ... comment
// ElementName-space-ElementValue
// the returned value is a static string and must be copied somewhere else
// before this function can be called again
char *getConfigElement(char *elemname)
{
    if(strlen(configfile) < 2) return NULL;

    static char s[501];
    int found = 0;
    char fn[1024];
    
    if(strlen(configfile) > 512)
    {
        printf("config file path+name too long: %s\n",configfile);
        exit(0);
    }
    strcpy(fn,configfile);
    
    if(fn[0] == '~')
    {
        struct passwd *pw = getpwuid(getuid());
        const char *homedir = pw->pw_dir;
        sprintf(fn,"%s%s",homedir,configfile+1);
    }

    //printf("read Configuration file %s\n",fn);
    FILE *fr = fopen(fn,"rb");
    if(!fr) 
    {
        printf("!!! Configuration file %s not found !!!\n",fn);
        exit(0);
    }

    while(1)
    {
        if(fgets(s,500,fr) == NULL) break;
        // remove leading SPC or TAB
        if(cleanStr(s) == 0) continue;
        // check if its a comment
        if(s[0] == '#') continue;
        // get word on index
        char *p = getword(s,0);
        if(!p) break;
        if(strcmp(p, elemname) == 0)
        {
            char val[500];
            if(*(s+strlen(p)+1) == 0) continue;
            p = getword(s+strlen(p)+1,1);
            if(!p) break;
            // replace , with .
            char *pkomma = strchr(p,',');
            if(pkomma) *pkomma = '.';
            strcpy(val,p);
            strcpy(s,val);
            found = 1;
            break;
        }

    }

    fclose(fr);
    if(found == 0) return NULL;
    return s;
}

void getConfigElement_double(char *elemname, double *pv, double multiplier)
{
    char *p = getConfigElement(elemname);
	if(p) *pv = atof(p) * multiplier;
}

void getConfigElement_longlong(char *elemname, long long *pv, double multiplier)
{
    char *p = getConfigElement(elemname);
	if(p) *pv = (long long)(atof(p) * multiplier);
}

void getConfigElement_int(char *elemname, int *pv, double multiplier)
{
    char *p = getConfigElement(elemname);
    if(p) *pv = (int)(atof(p) * multiplier);
}

float measure_samplerate(int id, int samp, int prt)
{
static unsigned long lastus[10];
static unsigned long samples[10];
static int f[10];
static int cnt[10];
static int first=1;

    if(first)
    {
        first = 0;
        for(int i=0; i<10; i++)
        {
            f[i] = 1;
            cnt[i] = 0;
        }
    }

    // measure sample rate
    struct timeval  tv;
    gettimeofday(&tv, NULL);
    if(f[id])
    {
        f[id] = 0;
        lastus[id] = tv.tv_sec * 1000000 + tv.tv_usec;
        samples[id] = samp;
    }
    samples[id] += samp;
    unsigned long tnow = tv.tv_sec * 1000000 + tv.tv_usec;
    
    float sr = 0;
    if(++(cnt[id]) >= prt)
    {
        cnt[id] = 0;
        sr = (samples[id]*1e6)/(tnow - lastus[id]);
        printf("%d: %.6f\n",id,sr/1e6);
    }
    return sr;
}

int measure_maxval(double v, int anz)
{
static double max = - 99999999;  
static int cnt = 0;  

    v = fabs(v);
    if(v > max)
    {
        max = v;
        //printf("maxval: %f\n",max);
    }

    if(++cnt >= anz)
    {
        printf("maxval: %f\n",max);
        max = - 99999999; 
        cnt = 0; 
        return 1;
    }
    return 0;
}
