/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Jean Le Feuvre - Cyril Concolato
 *			Copyright (c) Telecom ParisTech 2010-2012
 *					All rights reserved
 *
 *  This file is part of GPAC / 3GPP/MPEG Media Presentation Description input module
 *
 *  GPAC 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, or (at your option)
 *  any later version.
 *
 *  GPAC 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; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
#ifndef _MPD_H_
#define _MPD_H_

#include <gpac/constants.h>
#include <gpac/xml.h>
#include <gpac/media_tools.h>

#ifndef GPAC_DISABLE_CORE_TOOLS

/*TODO*/
typedef struct
{
	u32 dummy;
} GF_MPD_Metrics;

/*TODO*/
typedef struct
{
	u32 dummy;
} GF_MPD_ContentComponent;


//some elments are typically overloaded in XML, we keep the attributes / childrne nodes here. The attributes list is NULL if no extensions were found, otherwise it is a list of @GF_XMLAttribute.
//The children list is NULL if no extensions were found, otherwise it is a list of @GF_XMLNode
#define MPD_EXTENSIBLE	\
	GF_List *attributes;	\
	GF_List *children;	\
 
typedef struct
{
	MPD_EXTENSIBLE
} GF_MPD_ExtensibleVirtual;

typedef struct
{
	MPD_EXTENSIBLE

	char *scheme_id_uri; /*MANDATORY*/
	char *value;
	char *id;

} GF_MPD_Descriptor;

/*TODO*/
typedef struct
{
	u32 dummy;
} GF_MPD_Subset;

typedef struct
{
	u64 start_time;
	u32 duration; /*MANDATORY*/
	//may be -1 (FIXME this needs further testing)
	u32 repeat_count;
} GF_MPD_SegmentTimelineEntry;

typedef struct
{
	GF_List *entries;
} GF_MPD_SegmentTimeline;

typedef struct
{
	u64 start_range, end_range;
} GF_MPD_ByteRange;


typedef struct
{
	char *URL;
	char *service_location;
	GF_MPD_ByteRange *byte_range;
} GF_MPD_BaseURL;

typedef struct
{
	char *sourceURL;
	GF_MPD_ByteRange *byte_range;

	//GPAC internal - indicates the URL has already been solved
	Bool is_resolved;
} GF_MPD_URL;

typedef struct
{
	u32 num, den;
} GF_MPD_Fractional;

typedef struct
{
	u32 trackID;
	char *stsd;
	s64 mediaOffset;
} GF_MPD_ISOBMFInfo;

#define GF_MPD_SEGMENT_BASE	\
	u32 timescale;	\
	u64 presentation_time_offset;	\
	u32 time_shift_buffer_depth; /* expressed in milliseconds */	\
	GF_MPD_ByteRange *index_range;	\
	Bool index_range_exact;	\
	Double availability_time_offset;	\
	GF_MPD_URL *initialization_segment;	\
	GF_MPD_URL *representation_index;	\
 

typedef struct
{
	GF_MPD_SEGMENT_BASE
} GF_MPD_SegmentBase;

/*WARNING: duration is expressed in GF_MPD_SEGMENT_BASE@timescale unit*/
#define GF_MPD_MULTIPLE_SEGMENT_BASE	\
	GF_MPD_SEGMENT_BASE	\
	u64 duration;	\
	u32 start_number;	\
	GF_MPD_SegmentTimeline *segment_timeline;	\
	GF_MPD_URL *bitstream_switching_url;	\
 
typedef struct
{
	GF_MPD_MULTIPLE_SEGMENT_BASE
} GF_MPD_MultipleSegmentBase;

typedef struct
{
	char *media;
	GF_MPD_ByteRange *media_range;
	char *index;
	GF_MPD_ByteRange *index_range;
	u64 duration;
	/*HLS only*/
	char *key_url;
	bin128 key_iv;
	u64 hls_utc_start_time;
} GF_MPD_SegmentURL;

typedef struct
{
	GF_MPD_MULTIPLE_SEGMENT_BASE
	/*list of segments - can be NULL if no segment*/
	GF_List *segment_URLs;

	char *xlink_href;
	Bool xlink_actuate_on_load;

	u32 consecutive_xlink_count;
} GF_MPD_SegmentList;

typedef struct
{
	GF_MPD_MULTIPLE_SEGMENT_BASE
	char *media;
	char *index;
	char *initialization;
	char *bitstream_switching;
} GF_MPD_SegmentTemplate;

typedef enum
{
	GF_MPD_SCANTYPE_UNKNWON,
	GF_MPD_SCANTYPE_PROGRESSIVE,
	GF_MPD_SCANTYPE_INTERLACED
} GF_MPD_ScanType;

/*MANDATORY:
	mime_type
	codecs
*/
#define GF_MPD_COMMON_ATTRIBUTES_ELEMENTS	\
	char *profiles;	\
	u32 width;	\
	u32 height;	\
	GF_MPD_Fractional *sar;	\
	GF_MPD_Fractional *framerate;	\
	u32 samplerate;	\
	char *mime_type;	\
	char *segmentProfiles;	\
	char *codecs;	\
	u32 maximum_sap_period;	\
	u32 starts_with_sap;	\
	Double max_playout_rate;	\
	Bool coding_dependency;	\
	GF_MPD_ScanType scan_type;	\
	GF_List *frame_packing;	\
	GF_List *audio_channels;	\
	GF_List *content_protection;	\
	GF_List *essential_properties;	\
	GF_List *supplemental_properties;	\
	GF_List *isobmf_tracks;	\
 
typedef struct {
	GF_MPD_COMMON_ATTRIBUTES_ELEMENTS
} GF_MPD_CommonAttributes;

typedef struct {
	GF_MPD_COMMON_ATTRIBUTES_ELEMENTS

	u32 level;
	char *dependecy_level;
	u32 bandwidth; /*MANDATORY if level set*/
	char *content_components;
} GF_MPD_SubRepresentation;

typedef struct
{
	Bool disabled;
	char *cached_init_segment_url;
	Bool owned_gmem;
	u64 init_start_range, init_end_range;
	u32 probe_switch_count;
	char *init_segment_data;
	u32 init_segment_size;
	char *key_url;
	bin128 key_IV;
	/*previous maximum speed that this representation can be played, or 0 if it has never been played*/
	Double prev_max_available_speed;
	/*after switch we may have some buffered segments of the previous representation; so codec stats at this moment is unreliable. we should wait after the codec reset*/
	Bool waiting_codec_reset;
} GF_DASH_RepresentationPlayback;

typedef struct {
	GF_MPD_COMMON_ATTRIBUTES_ELEMENTS

	char *id; /*MANDATORY*/
	u32 bandwidth; /*MANDATORY*/
	u32 quality_ranking;
	char *dependency_id;
	char *media_stream_structure_id;

	GF_List *base_URLs;
	GF_MPD_SegmentBase *segment_base;
	GF_MPD_SegmentList *segment_list;
	GF_MPD_SegmentTemplate *segment_template;

	GF_List *sub_representations;

	/*index of the next enhancement representation plus 1, 0 is reserved in case of the highest representation*/
	u32 enhancement_rep_index_plus_one;

	/*GPAC playback implementation*/
	GF_DASH_RepresentationPlayback playback;
	u32 m3u8_media_seq_min, m3u8_media_seq_max;
} GF_MPD_Representation;


typedef struct
{
	GF_MPD_COMMON_ATTRIBUTES_ELEMENTS

	u32 id;
	/*default value is -1: not set in MPD*/
	s32 group;

	char *lang;
	char *content_type;
	GF_MPD_Fractional *par;
	u32 min_bandwidth;
	u32 max_bandwidth;
	u32 min_width;
	u32 max_width;
	u32 min_height;
	u32 max_height;
	u32 min_framerate;
	u32 max_framerate;
	Bool segment_alignment;
	Bool bitstream_switching;
	Bool subsegment_alignment;
	Bool subsegment_starts_with_sap;

	GF_List *accessibility;
	GF_List *role;
	GF_List *rating;
	GF_List *viewpoint;
	GF_List *content_component;

	GF_List *base_URLs;
	GF_MPD_SegmentBase *segment_base;
	GF_MPD_SegmentList *segment_list;
	GF_MPD_SegmentTemplate *segment_template;

	GF_List *representations;

	char *xlink_href;
	Bool xlink_actuate_on_load;
} GF_MPD_AdaptationSet;


typedef struct
{
	char *ID;
	u64 start; /* expressed in ms, relative to the start of the MPD */
	u64 duration; /* expressed in ms*/
	Bool bitstream_switching;

	GF_List *base_URLs;
	GF_MPD_SegmentBase *segment_base;
	GF_MPD_SegmentList *segment_list;
	GF_MPD_SegmentTemplate *segment_template;

	GF_List *adaptation_sets;
	GF_List *subsets;
	char *xlink_href;
	Bool xlink_actuate_on_load;
} GF_MPD_Period;

typedef struct
{
	char *lang;
	char *title;
	char *source;
	char *copyright;
	char *more_info_url;
} GF_MPD_ProgramInfo;


typedef enum {
	GF_MPD_TYPE_STATIC,
	GF_MPD_TYPE_DYNAMIC,
} GF_MPD_Type;

typedef struct {
	MPD_EXTENSIBLE

	char *ID;
	char *profiles;	/*MANDATORY*/
	GF_MPD_Type type;
	u64 availabilityStartTime; /* expressed in milliseconds */	/*MANDATORY if type=dynamic*/
	u64 availabilityEndTime;/* expressed in milliseconds */
	u64 publishTime;/* expressed in milliseconds */
	u64 media_presentation_duration; /* expressed in milliseconds */	/*MANDATORY if type=static*/
	u32 minimum_update_period; /* expressed in milliseconds */
	u32 min_buffer_time; /* expressed in milliseconds */	/*MANDATORY*/

	u32 time_shift_buffer_depth; /* expressed in milliseconds */
	u32 suggested_presentation_delay; /* expressed in milliseconds */

	u32 max_segment_duration; /* expressed in milliseconds */
	u32 max_subsegment_duration; /* expressed in milliseconds */

	/*list of GF_MPD_ProgramInfo */
	GF_List *program_infos;
	/*list of GF_MPD_BaseURL */
	GF_List *base_URLs;
	/*list of strings */
	GF_List *locations;
	/*list of Metrics */
	GF_List *metrics;
	/*list of GF_MPD_Period */
	GF_List *periods;

	/*set during parsing*/
	const char *xml_namespace; /*won't be freed by GPAC*/
} GF_MPD;

GF_Err gf_mpd_init_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *base_url);
GF_Err gf_mpd_complete_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *base_url);

GF_MPD *gf_mpd_new();
void gf_mpd_del(GF_MPD *mpd);
/*frees a GF_MPD_SegmentURL structure (type-casted to void *)*/
void gf_mpd_segment_url_free(void *ptr);
void gf_mpd_segment_base_free(void *ptr);

void gf_mpd_period_free(void *_item);

GF_Err gf_mpd_write_file(GF_MPD const * const mpd, const char *file_name);


typedef struct _gf_file_get GF_FileDownload;
struct _gf_file_get
{
	GF_Err (*new_session)(GF_FileDownload *getter, char *url);
	void (*del_session)(GF_FileDownload *getter);
	const char *(*get_cache_name)(GF_FileDownload *getter);

	void *udta;
	/*created by udta after new_session*/
	void *session;
};

/*converts M3U8 to MPD - getter is optional (download will still be processed if NULL)*/
GF_Err gf_m3u8_to_mpd(const char *m3u8_file, const char *base_url, const char *mpd_file, u32 reload_count, char *mimeTypeForM3U8Segments, Bool do_import, Bool use_mpd_templates,
                      GF_FileDownload *getter, GF_MPD *mpd, Bool parse_sub_playlist);

GF_Err gf_m3u8_solve_representation_xlink(GF_MPD_Representation *rep, GF_FileDownload *getter, Bool *is_static, u64 *duration);

GF_MPD_SegmentList *gf_mpd_solve_segment_list_xlink(GF_MPD *mpd, GF_XMLNode *root);

GF_Err gf_mpd_init_smooth_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *default_base_url);

void gf_mpd_delete_segment_list(GF_MPD_SegmentList *segment_list);

void gf_mpd_getter_del_session(GF_FileDownload *getter);


/*get the number of base URLs for the given representation. This cumuluates all base URLs at MPD, period, AdaptationSet and Representation levels*/
u32 gf_mpd_get_base_url_count(GF_MPD *mpd, GF_MPD_Period *period, GF_MPD_AdaptationSet *set, GF_MPD_Representation *rep);

typedef enum
{
	GF_MPD_RESOLVE_URL_MEDIA,
	GF_MPD_RESOLVE_URL_INIT,
	GF_MPD_RESOLVE_URL_INDEX,
	//same as GF_MPD_RESOLVE_URL_MEDIA but does not replace $Time$ and $Number$
	GF_MPD_RESOLVE_URL_MEDIA_TEMPLATE,
} GF_MPD_URLResolveType;

/*resolves a URL based for a given segment, based on the MPD url, the type of resolution
	base_url_index: 0-based index of the baseURL to use
	item_index: current downloading index of the segment
	nb_segments_removed: number of segments removed when purging the MPD after updates (can be 0). The start number will be offset by this value
*/
GF_Err gf_mpd_resolve_url(GF_MPD *mpd, GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set, GF_MPD_Period *period, const char *mpd_url, u32 base_url_index, GF_MPD_URLResolveType resolve_type, u32 item_index, u32 nb_segments_removed,
                          char **out_url, u64 *out_range_start, u64 *out_range_end, u64 *segment_duration, Bool *is_in_base_url, char **out_key_url, bin128 *key_iv);

/*get duration of the presentation*/
Double gf_mpd_get_duration(GF_MPD *mpd);

/*get the duration of media segments*/
void gf_mpd_resolve_segment_duration(GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set, GF_MPD_Period *period, u64 *out_duration, u32 *out_timescale, u64 *out_pts_offset, GF_MPD_SegmentTimeline **out_segment_timeline);

/*get the start_time from the segment index of a period/set/rep*/
GF_Err gf_mpd_get_segment_start_time_with_timescale(s32 in_segment_index,
	GF_MPD_Period const * const in_period, GF_MPD_AdaptationSet const * const in_set, GF_MPD_Representation const * const in_rep,
	u64 *out_segment_start_time, u64 *out_opt_segment_duration, u32 *out_opt_scale);

typedef enum {
	MPD_SEEK_PREV,    /*will return the segment containing the requested time*/
	MPD_SEEK_NEAREST, /*the nearest segment start time, may be the previous or the next one*/
} MPDSeekMode;


/*returns the segment index in the given period for the given time*/
GF_Err gf_mpd_seek_in_period(Double seek_time, MPDSeekMode seek_mode,
	GF_MPD_Period const * const in_period, GF_MPD_AdaptationSet const * const in_set, GF_MPD_Representation const * const in_rep,
	u32 *out_segment_index, Double *out_opt_seek_time);

/*get the index and the period for a given time in the MPD*/
GF_EXPORT
GF_Err gf_mpd_seek_to_time(Double seek_time, MPDSeekMode seek_mode,
	GF_MPD const * const in_mpd, GF_MPD_AdaptationSet const * const in_set, GF_MPD_Representation const * const in_rep,
	GF_MPD_Period **out_period, u32 *out_segment_index, Double *out_opt_seek_time);

/*get duration of the presentation*/
Double gf_mpd_get_duration(GF_MPD *mpd);

/*get the duration of media segments in seconds*/
void gf_mpd_resolve_segment_duration(GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set, GF_MPD_Period *period, u64 *out_duration, u32 *out_timescale, u64 *out_pts_offset, GF_MPD_SegmentTimeline **out_segment_timeline);

#endif /*GPAC_DISABLE_CORE_TOOLS*/

#endif // _MPD_H_
