
#ifndef __tags_h__
#define __tags_h__

// i just wanted to make brief mention of how the tag stuff is laid out because of
// how gay the original bungie one is. A group in a tag file is a group. A group
// is also a type, so like all collections are in the collection group. The sub_group is really
// the four cc of the tag itself. We have renamed the sub_group field a more logical "id". 

#include <stdio.h>

#ifndef __myth_defs_h__
#include "myth_defs.h"
#endif

#ifndef __BYTE_SWAPPING_H__
#include "byte_swapping.h"
#endif

enum
{
	TAG_FILE_TYPE_AUTO = 0,
	TAG_FILE_TYPE_LOCAL,
	TAG_FILE_TYPE_PLUGIN,
	TAG_FILE_TYPE_MONOLITHIC,
};

struct tag_file;
struct tag_group;
struct tag_entry;

class TTagFileForm;
class TTagEditForm;
class XGFileSpecifier;
class XGDirectory;

typedef void (*tag_editor_proc)( tag_entry * );
typedef void (*tag_creator_proc)( tag_entry * );
typedef void (*tag_dependency_proc)( tag_entry *e );
typedef void (*tag_updater_proc)( tag_entry *e );

void begin_tag_dependency_generation();
void add_tag_dependency( tag type, tag id );
void end_tag_dependency_generation();

tag_file *get_next_tag_file( tag_file *f );
tag_file *get_tag_file( const XGFileSpecifier &fs, bool create = false );
void scan_directory_for_tag_files( XGDirectory &dir, bool recurse );
void close_tag_file( tag_file *f );
tag_file *revert_tag_file( tag_file *f );

TTagFileForm *get_file_form( tag_file *tf );
void set_file_form( tag_file *tf, TTagFileForm *form );

void tag_type_to_string(tag type, char* string);
tag tag_type_from_string( const char *string );
const char *tagstr( tag t );

tag_entry *add_tag_entry( tag_file *f, tag group, tag id, short version, const char *name,
					unsigned offset, unsigned length, const void *data,
					bool becomeowner );
void delete_tag_entry( tag_entry *e, bool delete_data );
tag_group *get_group_in_file( tag_file *f, tag t, bool create = false );
tag_entry *get_entry( tag type, tag id );
tag_entry *get_entry_in_group( tag_group *group, tag id );
tag_entry *get_entry_in_file( tag_file *file, tag type, tag id );


tag_group* get_next_group_in_file(tag_file *f, tag_group* g);
// if you pass null for the group, it returns the last group in the file.
tag_group* get_prev_group_in_file(tag_file* f, tag_group* g);

tag_entry* get_next_entry_in_group(tag_group* g, tag_entry* e);
// if you pass null for the entry, it returns the last entry in the group.
tag_entry* get_prev_entry_in_group(tag_group* g, tag_entry* e);
tag_entry* get_next_plugin_entry( tag_entry* e );

bool copy_tag_file_entries_to_another_file( tag_file* src_file, tag_file* dst_file );

char* get_entry_name(tag_entry* e);
int   set_entry_name(tag_entry* e, char* name);

// passing 0 for the filter will count everything.
// the only reason i can think of to not pass 0 would
// be to check for duplicates.
int get_group_count(tag_file* f, tag type_filter=0);
int get_entry_count(tag_group* g, tag id_filter=0);

tag_group* get_entry_group(tag_entry* e);
void set_entry_group(tag_entry* e, tag id);
tag get_entry_id(tag_entry* e);
void set_entry_id(tag_entry* e, tag id);
tag get_group_type(tag_group* g);
tag get_entry_type( tag_entry *e );

short get_entry_version( tag_entry* e );
void set_entry_version( tag_entry* e, short version);
bool update_entry_version( tag_entry *e );

tag_file *get_file( tag type, tag id );
tag_file *get_entry_file( tag_entry *e );
tag_file *get_group_file( tag_entry *e );

unsigned get_entry_size( tag_entry *e );
const void *get_entry_data( tag_entry *e, const void **data = 0, unsigned *length = 0 );
void set_entry_data( tag_entry *e, const void *data, unsigned length, bool becomeowner );
		// if becomeowner is false, data is copied internally; you must still free the memory pointed to by data
void release_entry_data( tag_entry *e );
		
TTagEditForm *get_entry_form( tag_entry *e );
void set_entry_form( tag_entry *e, TTagEditForm *form );

bool write_tags_to_file( XGFileSpecifier &fs, tag_file *f, bool update_internal, int tag_file_type = TAG_FILE_TYPE_AUTO );
	// returns true if successful
void rewrite_tag_file( tag_file *f );
	// Rewrites the entire file. Use when entries are added, deleted, or change in size.
void update_tag_file( tag_file *f );
	// First attempts to quickly update file if a few entries have changed contents
	// but not size and no entries added or deleted. Will call rewrite_tag_file for you
	// if entries were added, deleted, or changed in size.
XGFileSpecifier *get_tag_file_spec( tag_file *f );
tag_file *new_empty_tag_file();

bool is_file_readonly( tag_file *f );
bool is_file_modified( tag_file *f );
bool is_entry_modified( tag_entry *e );

bool load_tag_data_into_struct( char* string, tag_entry* e, void* struct_ptr, unsigned struct_length, unsigned struct_count, byte_swap_code* bs_codes );
bool save_tag_data_from_struct( char* string, tag_entry* e, void* struct_ptr, unsigned struct_length, unsigned struct_count, byte_swap_code* bs_codes );
bool tag_data_needs_update( char* string, tag_entry* e, void* struct_ptr, unsigned struct_length, unsigned struct_count, byte_swap_code* bs_codes, int size_must_match=1 );


tag get_indexed_type( int i );
const char *get_plural_name_for_type( tag type );
const char *get_singular_name_for_type( tag type );
tag_editor_proc get_type_editor( tag type, bool mod=FALSE );
tag_creator_proc get_type_creator( tag type );
short get_type_version( tag type );

tag new_tag_id_from_string( const char *string, tag unique_in_group, tag_file *unique_in_file );

tag_entry *find_duplicate_id( tag_entry *e, bool start_at_begining );

void announce_tag_change( tag group, tag id );
void announce_tag_change( tag_entry *e );

void copy_file_data_into_tag( tag_file* tf, XGFileSpecifier& fs );

#define LLADD( L, E ) \
	((E)->next = (L), (L) = (E), (E)->prev = 0, ((E)->next ? (E)->next->prev = (E) : 0))

#define LLDEL( L, E ) \
	( ((E)->next ? (E)->next->prev = (E)->prev : 0), ((E)->prev ? (E)->prev->next = (E)->next : (L) = (E)->next) )

#define LLAFTER( O, E ) \
	( (E)->next = (O)->next, (E)->prev = (O), ((O)->next ? (O)->next->prev = (E) : 0), (O)->next = (E) )

#endif

