/*
 * gps_parser.h -- Header for gps parsing (.gpx and .tcx) and processing
 * Copyright (C) 2011-2021 Meltytech, LLC
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
#ifndef GPS_PARSER_H
#define GPS_PARSER_H

#include <framework/mlt.h>
#include <cmath>
#include <QXmlStreamReader>
#include <QFile>

#define GPS_UNINIT -9999

typedef struct
{
	double lat, lon, speed, total_dist, ele, hr, bearing;
	int64_t time; //epoch milliseconds
} gps_point_raw;

typedef struct gps_point_raw_list {
	gps_point_raw gp;
	struct gps_point_raw_list *next;
} gps_point_ll;

typedef struct
{
	double lat, lon, speed, total_dist, ele, hr, bearing;
	int64_t time;
	double d_elev, elev_up, elev_down, dist_up, dist_down, dist_flat;
} gps_point_proc;

//0 is a valid value for many fields, use GPS_UNINIT (-9999) to differentiate missing values
static const gps_point_raw uninit_gps_raw_point = {
	.lat=GPS_UNINIT, .lon=GPS_UNINIT, .speed=GPS_UNINIT, .total_dist=GPS_UNINIT,
	.ele=GPS_UNINIT, .hr=GPS_UNINIT, .bearing=GPS_UNINIT,
	.time=GPS_UNINIT
	};

static const gps_point_proc uninit_gps_proc_point = {
	.lat=GPS_UNINIT, .lon=GPS_UNINIT, .speed=GPS_UNINIT,
	.total_dist=GPS_UNINIT, .ele=GPS_UNINIT, .hr=GPS_UNINIT, .bearing=GPS_UNINIT, 
	.time=GPS_UNINIT,
	.d_elev=GPS_UNINIT, .elev_up=GPS_UNINIT, .elev_down=GPS_UNINIT,
	.dist_up=GPS_UNINIT, .dist_down=GPS_UNINIT, .dist_flat=GPS_UNINIT
	};

//structure used to ease argument passing between filter and GPS parser api
typedef struct
{
	gps_point_raw* gps_points_r; //raw gps data from file
	gps_point_proc* gps_points_p; //processed gps data
	gps_point_raw** ptr_to_gps_points_r; //if malloc is needed
	gps_point_proc** ptr_to_gps_points_p;
	int* gps_points_size;
	int* last_searched_index; //used to cache repeated searches
	int64_t* first_gps_time;
	int64_t* last_gps_time;
	char* interpolated;
	int* swap180;
	//read only:
	int64_t gps_proc_start_t; //process only points after this time (epoch miliseconds)
	int last_smooth_lvl;
	char* last_filename; //gps file fullpath
	mlt_filter filter;
} gps_private_data;

#define has_valid_location(x) (((x).lat)!=GPS_UNINIT && ((x).lon)!=GPS_UNINIT)
#define has_valid_location_ptr(x) ((x) && ((x)->lat)!=GPS_UNINIT && ((x)->lon)!=GPS_UNINIT)

#define MATH_PI 3.14159265358979323846
#define to_rad(x) ((x)*MATH_PI/180.0)
#define to_deg(x) ((x)*180.0/MATH_PI)

int64_t datetimeXMLstring_to_mseconds(const char* text, char* format = NULL);
void mseconds_to_timestring (int64_t seconds, char* format, char* result);
double distance_haversine_2p (double p1_lat, double p1_lon, double p2_lat, double p2_lon);
double distance_equirectangular_2p (double p1_lat, double p1_lon, double p2_lat, double p2_lon);
double bearing_2p (double p1_lat, double p1_lon, double p2_lat, double p2_lon);
void get_first_gps_time(gps_private_data gdata);
void get_last_gps_time(gps_private_data gdata);
double get_avg_gps_time_ms(gps_private_data gdata);
int get_max_gps_diff_ms(gps_private_data gdata);
int binary_search_gps(gps_private_data gdata, int64_t video_time, bool force_result = false);
int decimals_needed(double x);
double convert_distance_to_format(double x, const char* format);
double convert_speed_to_format(double x, const char* format);
const char* bearing_to_compass(double x);
void recalculate_gps_data(gps_private_data gdata);
void process_gps_smoothing(gps_private_data gdata, char do_processing);

double weighted_middle_double (double v1, int64_t t1, double v2, int64_t t2, int64_t new_t, int max_gps_diff_ms);
int64_t weighted_middle_int64 (int64_t v1, int64_t t1, int64_t v2, int64_t t2, int64_t new_t, int max_gps_diff_ms);
gps_point_proc weighted_middle_point_proc(gps_point_proc* p1, gps_point_proc* p2, int64_t new_t, int max_gps_diff_ms);

void qxml_parse_gpx(QXmlStreamReader &reader, gps_point_ll **gps_list, int *count_pts);
void qxml_parse_tcx(QXmlStreamReader &reader, gps_point_ll **gps_list, int *count_pts);
int qxml_parse_file(gps_private_data gdata);

#define swap_180_if_needed(lon) (pdata->swap_180 ? get_180_swapped(lon) : lon)
double get_180_swapped(double lon);

int time_val_between_indices_proc(int64_t time_val, gps_point_proc* gp, int i, int size, int max_gps_diff_ms, bool force_result);
int time_val_between_indices_raw(int64_t time_val, gps_point_raw* gp, int i, int size, int max_gps_diff_ms, bool force_result);



//TODO: this is copied from src\framework\mlt_producer.c 
/*
 * Boost implementation of timegm()
 * (C) Copyright Howard Hinnant
 * (C) Copyright 2010-2011 Vicente J. Botet Escriba
 */

static inline int32_t is_leap(int32_t year)
{
	if(year % 400 == 0)
		return 1;
	if(year % 100 == 0)
		return 0;
	if(year % 4 == 0)
		return 1;
	return 0;
}

static inline int32_t days_from_0(int32_t year)
{
	year--;
	return 365 * year + (year / 400) - (year/100) + (year / 4);
}

static inline int32_t days_from_1970(int32_t year)
{
	const int days_from_0_to_1970 = days_from_0(1970);
	return days_from_0(year) - days_from_0_to_1970;
}

static inline int32_t days_from_1jan(int32_t year,int32_t month,int32_t day)
{
	static const int32_t days[2][12] =
	{
		{ 0,31,59,90,120,151,181,212,243,273,304,334},
		{ 0,31,60,91,121,152,182,213,244,274,305,335}
	};
	return days[is_leap(year)][month-1] + day - 1;
}

static inline time_t internal_timegm(struct tm const *t)
{
	int year = t->tm_year + 1900;
	int month = t->tm_mon;
	if(month > 11)
	{
		year += month/12;
		month %= 12;
	}
	else if(month < 0)
	{
		int years_diff = (-month + 11)/12;
		year -= years_diff;
		month+=12 * years_diff;
	}
	month++;
	int day = t->tm_mday;
	int day_of_year = days_from_1jan(year,month,day);
	int days_since_epoch = days_from_1970(year) + day_of_year;

	time_t seconds_in_day = 3600 * 24;
	time_t result = seconds_in_day * days_since_epoch + 3600 * t->tm_hour + 60 * t->tm_min + t->tm_sec;

	return result;
}

/* End of Boost implementation of timegm(). */


#endif /* GPS_PARSER_H */
