/*
Input and output parsing tools.
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "logger.h"
#include "version.h"
#include "parsers.h"
#include "constants.h"
#include "geometry.h"

#include <math.h>


/* Parse basic command line arguments for programs */
int parse_basic_args(int argc, char **argv, const char *progname,
                     BASIC_ARGS *args, void (*print_help)(void))
{
    int bad_args = 0, parsed_args = 0, total_args = 1, i;
    char *params;

    /* Default values for options */
    args->verbose = 0;
    args->logtofile = 0;
    /* Parse arguments */
    for(i = 1; i < argc; i++)
    {
        if(argv[i][0] == '-')
        {
            switch(argv[i][1])
            {
                case 'h':
                    if(argv[i][2] != '\0')
                    {
                        log_error("invalid argument '%s'", argv[i]);
                        bad_args++;
                        break;
                    }
                    print_help();
                    return 2;
                case 'v':
                    if(argv[i][2] != '\0')
                    {
                        log_error("invalid argument '%s'", argv[i]);
                        bad_args++;
                        break;
                    }
                    if(args->verbose)
                    {
                        log_error("repeated option -v");
                        bad_args++;
                        break;
                    }
                    args->verbose = 1;
                    break;
                case 'l':
                {
                    if(args->logtofile)
                    {
                        log_error("repeated option -l");
                        bad_args++;
                        break;
                    }
                    params = &argv[i][2];
                    if(strlen(params) == 0)
                    {
                        log_error("bad input argument -l. Missing filename.");
                        bad_args++;
                    }
                    else
                    {
                        args->logtofile = 1;
                        args->logfname = params;
                    }
                    break;
                }
                case '-':
                {
                    params = &argv[i][2];
                    if(strcmp(params, "version"))
                    {
                        log_error("invalid argument '%s'", argv[i]);
                        bad_args++;
                    }
                    else
                    {
                        print_version(progname);
                        return 2;
                    }
                    break;
                }
                default:
                    log_error("invalid argument '%s'", argv[i]);
                    bad_args++;
                    break;
            }
        }
        else
        {
            if(parsed_args == 0)
            {
                args->inputfname = argv[i];
                parsed_args++;
            }
            else
            {
                log_error("invalid argument '%s'. Already given model file %s",
                          argv[i], args->inputfname);
                bad_args++;
            }
        }
    }
    /* Check if parsing went well */
    if(parsed_args > total_args)
    {
        log_error("%s: too many input arguments. given %d, max %d.",
                    progname, parsed_args, total_args);
    }
    if(bad_args > 0)
    {
        log_error("%d bad input argument(s)", bad_args);
        return 1;
    }
    if(parsed_args < total_args)
    {
        return 3;
    }
    return 0;
}



/* Parse command line arguments for tessh* programs */
int parse_tessb_args(int argc, char **argv, const char *progname,
                     TESSB_ARGS *args, void (*print_help)(const char *))
{
    int bad_args = 0, parsed_args = 0, total_args = 1,  parsed_order = 0,
        parsed_ratio1 = 0, parsed_ratio2 = 0, parsed_ratio3 = 0, i, nchar, nread;
    char *params;

    /* Default values for options */
    args->verbose = 0;
    args->logtofile = 0;
    args->lon_order = 2;
    args->lat_order = 2;
    args->r_order = 2;
    args->adaptative = 1;
    args->ratio1 = 0; /* zero means use the default for the program */
	args->ratio2 = 0;
	args->ratio3 = 0;
    /* Parse arguments */
    for(i = 1; i < argc; i++)
    {
        if(argv[i][0] == '-')
        {
            switch(argv[i][1])
            {
                case 'h':
                    if(argv[i][2] != '\0')
                    {
                        log_error("invalid argument '%s'", argv[i]);
                        bad_args++;
                        break;
                    }
                    print_help(progname);
                    return 2;
                case 'v':
                    if(argv[i][2] != '\0')
                    {
                        log_error("invalid argument '%s'", argv[i]);
                        bad_args++;
                        break;
                    }
                    if(args->verbose)
                    {
                        log_error("repeated option -v");
                        bad_args++;
                        break;
                    }
                    args->verbose = 1;
                    break;
                case 'l':
                {
                    if(args->logtofile)
                    {
                        log_error("repeated option -l");
                        bad_args++;
                        break;
                    }
                    params = &argv[i][2];
                    if(strlen(params) == 0)
                    {
                        log_error("bad input argument -l. Missing filename.");
                        bad_args++;
                    }
                    else
                    {
                        args->logtofile = 1;
                        args->logfname = params;
                    }
                    break;
                }
                case '-':
                {
                    params = &argv[i][2];
                    if(strcmp(params, "version"))
                    {
                        log_error("invalid argument '%s'", argv[i]);
                        bad_args++;
                    }
                    else
                    {
                        print_version(progname);
                        return 2;
                    }
                    break;
                }
                case 'a':
                    if(argv[i][2] != '\0')
                    {
                        log_error("invalid argument '%s'", argv[i]);
                        bad_args++;
                        break;
                    }
                    if(!args->adaptative)
                    {
                        log_error("repeated option -a");
                        bad_args++;
                        break;
                    }
                    args->adaptative = 0;
                    break;
                case 'o':
                {
                    if(parsed_order)
                    {
                        log_error("repeated option -o");
                        bad_args++;
                        break;
                    }
                    params = &argv[i][2];
                    nchar = 0;
                    nread = sscanf(params, "%d/%d/%d%n", &(args->lon_order),
                                &(args->lat_order), &(args->r_order), &nchar);
                    if(nread != 3 || *(params + nchar) != '\0')
                    {
                        log_error("bad input argument '%s'", argv[i]);
                        bad_args++;
                    }
                    parsed_order = 1;
                    break;
                }
                case 't':
                {
					//ELDAR BAYKIEV///////////////////////////////////////////////////////////////////
					switch(argv[i][2])
					{
						case '1':
						{
							if(parsed_ratio1)
							{
								log_error("repeated option -t1");
								bad_args++;
								break;
							}
							params = &argv[i][3];
							nchar = 0;
							nread = sscanf(params, "%lf%n", &(args->ratio1), &nchar);
							if(nread != 1 || *(params + nchar) != '\0')
							{
								log_error("bad input argument '%s'", argv[i]);
								bad_args++;
							}
							parsed_ratio1 = 1;
							break;
						}

						case '2':
						{
							if(parsed_ratio2)
							{
								log_error("repeated option -t2");
								bad_args++;
								break;
							}
							params = &argv[i][3];
							nchar = 0;
							nread = sscanf(params, "%lf%n", &(args->ratio2), &nchar);
							if(nread != 1 || *(params + nchar) != '\0')
							{
								log_error("bad input argument '%s'", argv[i]);
								bad_args++;
							}
							parsed_ratio2 = 1;
							break;
						}

						case '3':
						{
							if(parsed_ratio3)
							{
								log_error("repeated option -t3");
								bad_args++;
								break;
							}
							params = &argv[i][3];
							nchar = 0;
							nread = sscanf(params, "%lf%n", &(args->ratio3), &nchar);
							if(nread != 1 || *(params + nchar) != '\0')
							{
								log_error("bad input argument '%s'", argv[i]);
								bad_args++;
							}
							parsed_ratio3 = 1;
							break;
						}
					}
					//ELDAR BAYKIEV///////////////////////////////////////////////////////////////////
                }
                default:
                    log_error("invalid argument '%s'", argv[i]);
                    bad_args++;
                    break;
            }
        }
        else
        {
            if(parsed_args == 0)
            {
                args->modelfname = argv[i];
                parsed_args++;
            }
            else
            {
                log_error("invalid argument '%s'. Already given model file %s",
                          argv[i], args->modelfname);
                bad_args++;
            }
        }
    }
    /* Check if parsing went well */
    if(bad_args > 0 || parsed_args != total_args)
    {
        if(parsed_args < total_args)
        {
            log_error("%s: missing input file.",
                      progname, parsed_args, total_args);
        }
        if(parsed_args > total_args)
        {
            log_error("%s: too many input arguments. given %d, max %d.",
                      progname, parsed_args, total_args);
        }
        if(bad_args > 0)
        {
            log_error("%d bad input argument(s)", bad_args);
        }
        return 1;
    }
    return 0;
}

//parse arguments for gradient calculator
int parse_gradcalc_args(int argc, char **argv, const char *progname, GRADCALC_ARGS *args, void (*print_help)(const char *))
{
	int bad_args = 0, parsed_args = 0, total_args = 5, i;
	char *params;

	 /* Default values for options */
	args->verbose = 0;
	args->logtofile = 0;

	args->gridbx_set = FALSE;
  	args->gridby_set = FALSE;
	args->gridbz_set = FALSE;
	args->out_set = -1;

	args->bz_NEU_NED = -1;
	args->bz_NEU_NED_set = FALSE;


	/* Parse arguments */
	for(i = 1; i < argc; i++)
	{
		if(argv[i][0] == '-')
		{
			switch(argv[i][1])
			{
				case 'h':
					if(argv[i][2] != '\0')
					{
						//log_error("invalid argument '%s'", argv[i]);
						printf("invalid argument '%s'\n", argv[i]);
						bad_args++;
						break;
					}
					print_help(progname);
					return 2;
				case '-':
               				{
					params = &argv[i][2];
					if(strcmp(params, "version"))
					{
						printf("invalid argument '%s'\n", argv[i]);
						bad_args++;
					}
					else
					{
						print_version(progname);
						return 2;
					}
					break;
					}
				case 'b':
					params = &argv[i][2];
					if(strlen(params) <= 1)
					{
						printf("bad input argument -b. Missing component and filename\n");
						bad_args++;
						break;
                    			}
					else
					{
						switch(argv[i][2])
						{
							case 'x':
								if(args->gridbx_set)
								{
									printf("invalid argument '%s', gridfile bx already set\n", argv[i]);
									bad_args++;
									break;
								}
								else
								{
				    					args->gridbx_set = 1;
									args->gridbx_fn = &argv[i][3];
								}
                                				break;
			    				case 'y':
								if(args->gridby_set)
								{
									printf("invalid argument '%s', gridfile by already set\n", argv[i]);
									bad_args++;
									break;
								}
								else
								{
									args->gridby_set = 1;
									args->gridby_fn = &argv[i][3];
								}
								break;
							case 'z':
								if(args->gridbz_set)
								{
									printf("invalid argument '%s', gridfile by already set\n", argv[i]);
									bad_args++;
									break;
								}
								else
								{
									args->gridbz_set = 1;
									args->gridbz_fn = &argv[i][3];
								}
								break;
							default:
								printf("invalid argument '%s', wrong component\n", argv[i]);
								bad_args++;
								break;
						}
                    			}
                    			break;
 				case 'c':
					params = &argv[i][2];

					if(args->bz_NEU_NED_set)
					{
						printf("invalid argument '%s', coordinate system is already set\n", argv[i]);
						bad_args++;
						break;
					}
					if(strlen(params) > 1)
					{
						printf("invalid argument '%s', specify coordinate system in the input grids\n", argv[i]);
						bad_args++;
						break;
					}
					if(argv[i][2] == '1')
					{
						args->bz_NEU_NED_set = 1;
						args->bz_NEU_NED = 1;
						break;
					}
					else if(argv[i][2] == '2')
					{
						args->bz_NEU_NED_set = 1;
						args->bz_NEU_NED = -1;
						break;
					}
					else
					{
						printf("invalid argument '%s', there are only NED (1) and NEU (2, default) coordinate systems\n", argv[i]);
						bad_args++;
						break;
        				}
                    			break;
				case 'o':
					params = &argv[i][2];

					if(args->out_set>=0)
					{
						printf("invalid argument '%s', output format is already set\n", argv[i]);
						bad_args++;
						break;
					}

					if(strlen(params) != 1)
					{
						printf("invalid argument '%s', specify output format\n", argv[i]);
						bad_args++;
						break;
					}
					//TODO Add check if it is integer
					args->out_set = atoi(params);
					break;
				default:
					printf("invalid argument '%s'\n", argv[i]);
					bad_args++;
					break;
			}
		}
	}

	if(parsed_args > total_args)
	{
		//log_error("%s: too many input arguments. given %d, max %d.", progname, parsed_args, total_args);
	}
	if(bad_args > 0)
	{
		//log_error("%d bad input argument(s)", bad_args);
		return 1;
	}
	if(parsed_args < total_args)
	{
		return 3;
	}
	return 0;
}

/* Strip trailing spaces and newlines from the end of a string */
void strstrip(char *str)
{
    int i;
    for(i = strlen(str) - 1; i >= 0; i--)
    {
        if(str[i] != ' ' && str[i] != '\n' && str[i] != '\r' && str[i] != '\0')
            break;
    }
    str[i + 1] = '\0';
}


/* Read a single tesseroid from a string */
int gets_mag_tess(const char *str, MAG_TESSEROID *tess)
{
    double w, e, s, n, top, bot, dens, suscept, Bx, By, Bz, Rx, Ry, Rz;
    int nread, nchars;

    nread = sscanf(str, "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf%n", &w, &e, &s,
                    &n, &top, &bot, &dens, &suscept, &Bx, &By, &Bz, &nchars);
    if(nread != 11 || str[nchars] != '\0')
    {
        return 1;
    }
    tess->w = w;
    tess->e = e;
    tess->s = s;
    tess->n = n;
    tess->r1 = MEAN_EARTH_RADIUS + bot;
    tess->r2 = MEAN_EARTH_RADIUS + top;
    tess->density = dens;
	tess->suscept = suscept;
	tess->Bx = Bx;
	tess->By = By;
	tess->Bz = Bz;

  tess->cos_a1 = cos(PI/2.0-DEG2RAD*(w+e)*0.5);
  tess->sin_a1 = sin(PI/2.0-DEG2RAD*(w+e)*0.5);
  tess->cos_b1 = cos(DEG2RAD*(s+n)*0.5);
  tess->sin_b1 = sin(DEG2RAD*(s+n)*0.5);
    return 0;
}

//ELDAR BAYKIEV////////////////////////////////
MAG_TESSEROID * read_mag_tess_model(FILE *modelfile, int *size)
{
    MAG_TESSEROID *model, *tmp;
    int buffsize = 300, line, badinput = 0, error_exit = 0;
    char sbuff[10000];

    /* Start with a single buffer allocation and expand later if necessary */
    model = (MAG_TESSEROID *)malloc(buffsize*sizeof(MAG_TESSEROID));
    if(model == NULL)
    {
        log_error("problem allocating initial memory to load tesseroid model.");
        return NULL;
    }
    *size = 0;
    for(line = 1; !feof(modelfile); line++)
    {
        if(fgets(sbuff, 10000, modelfile) == NULL)
        {
            if(ferror(modelfile))
            {
                log_error("problem encountered reading line %d.", line);
                error_exit = 1;
                break;
            }
        }
        else
        {
            /* Check for comments and blank lines */
            if(sbuff[0] == '#' || sbuff[0] == '\r' || sbuff[0] == '\n')
            {
                continue;
            }
            if(*size == buffsize)
            {
                buffsize += buffsize;
                tmp = (MAG_TESSEROID *)realloc(model, buffsize*sizeof(MAG_TESSEROID));
                if(tmp == NULL)
                {
                    /* Need to free because realloc leaves unchanged in case of
                       error */
                    free(model);
                    log_error("problem expanding memory for tesseroid model.\nModel is too big.");
                    return NULL;
                }
                model = tmp;
            }
            /* Remove any trailing spaces or newlines */
            strstrip(sbuff);
            if(gets_mag_tess(sbuff, &model[*size]))
            {
                log_warning("bad/invalid tesseroid at line %d.", line);
                badinput = 1;
                continue;
            }
            (*size)++;
        }
    }
    if(badinput || error_exit)
    {
        free(model);
        return NULL;
    }
    /* Adjust the size of the model */
    if(*size != 0)
    {
        tmp = (MAG_TESSEROID *)realloc(model, (*size)*sizeof(MAG_TESSEROID));
        if(tmp == NULL)
        {
            /* Need to free because realloc leaves unchanged in case of
                error */
            free(model);
            log_error("problem freeing excess memory for tesseroid model.");
            return NULL;
        }
        model = tmp;
    }
    return model;
}


/* Read a single rectangular prism from a string */
