#ifndef MPLE_H
#define MPLE_H

#include <glib.h>

/* I hate include files that aren't self-contained */
#include <time.h>
#include <id3tag.h>
#include <stdio.h>
#include <sys/stat.h>

#include <libnw.h>
#include <libnw_internals.h>

#define DEFAULT_PB_TEMPLATE "%s/esys/pblist%d.dat"
#define DEFAULT_MP_TEMPLATE "%s/esys/nw-mp3/mp%04x.dat"
#define DEFAULT_MASTER_PBLIST 1
#define MAX_TRACKS 40000 /* according to the manual */

/* On-disk data structures and magic */
#define PBLIST_MAGIC "\x00\x03\xce\xa0"
#define MPDAT_MAGIC  "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

/* This is used to find likely devices in HAL */
#define HAL_MAGIC "NETWORK  WALKMAN"

/* Header structure from pblist file [incomplete] */
typedef struct {
  guint8  signature[8]; /* "WMPLESYS", hence the name of the module */

  guint32 timestamp;    /* ctime of the ESYS folder */
  guint32 msn;          /* media serial number */
  guint32 magic;        /* no idea what this is */

  guint32 folders;      /* #folders */
  guint32 tracks;       /* number of tracks on the device */

  /* XORing the header 32 bits at a time should result in this
     checksum, or XORing including this should result in zero */
  guint32 checksum;     /* XOR checksum */
} pblist_hdr;

/* folder descriptor. complete. */
typedef struct {
  guint16 foldername[126];  /* name of this folder */
  guint32 offset;           /* offset from start of pblist file to
                                track list */
} pblist_folder;

#define FOLDER_OFFSET(x) (((pblist_folder *)x)->offset)

/* track pointer in pblist. complete. */
typedef struct {
  guint16 filename[128];
  guint16 title[128];
  guint16 artist[128];
} pblist_track;

/* Header structure from mp*.dat file. complete. */
typedef struct {
  guint8  signature[4];  /* "WMMP" */
  guint32 size;          /* size of file in bytes */
  guint32 time;          /* time in milliseconds */
  guint32 frames;        /* number of MP3 frames */
  guint32 msn;           /* media serial number */
  guint8  magic[12];
} mpdat_hdr;

/* and now, structures for passing the data around */
typedef enum _mple_aux_data {
  MPLE_MSN = 1,
  MPLE_TS  = 2,
  MPLE_MAGIC = 4,
} mple_aux_data;

/* PRUNE ME */
typedef struct _mple_device {
  nw_device_t devdata; /* NW common data */

  /* auxilliary information */
  mple_aux_data aux; /* whether this data is valid */
  guint32 msn;   /* media serial number */
  time_t timestamp; /* timestamp from master pblist, converted to time_t */
  guint32 magic; /* magic from pblist file */

  /* where to find files on the device */
  gchar *pbtemplate; /* e.g. "%s/esys/pblist%d.dat" */
  gchar *mptemplate; /* e.g. "%s/esys/nw-mp3/mp%04x.dat" */
  guint16 master_pblist; /* probably 1 */
} mple_device;

/* Actual API-like thing */
/* Initialise/Release the device structure */
nw_device_t *mple_dev_init( gchar * );
void mple_dev_free( nw_device_t * );
gboolean mple_dev_sync( nw_device_t * );

/* this is an attempt to cater for devices that differ in where they
   place files. */
void *mple_dev_set_pb_template( mple_device *, char * );
void *mple_dev_set_mp_template( mple_device *, char * );

/* Pull the entire contents of the pblist (directory) off the device */
GList *mple_parse_pblist( nw_device_t * );

/* add/del/move folder */
/* Since pos == 0 is reserved for "add at first available slot",
   folder indices are 1-based. */
/* - del nukes the ENTIRE folder including contents */
guint32 mple_add_folder( nw_device_t *, gchar *, guint32 pos );
gboolean mple_del_folder( nw_device_t *, guint32 pos );
guint32 mple_mv_folder( nw_device_t *, guint16 oldpos, guint16 newpos );
guint32 mple_ren_folder( nw_device_t *, guint32 pos, gchar * );

/* same interface for tracks, less 'find' */
/* ren allows rewriting all metadata for the track */
guint32 mple_add_track( nw_device_t *, char *filename, nw_track_t *, guint32 folder, guint32 pos );
gboolean mple_del_track( nw_device_t *, guint32 tracknum );
guint32 mple_mv_track( nw_device_t *, guint32 tracknum, guint32 folder, guint32 pos );
guint16 mple_ren_track( nw_device_t *, guint16 tracknum, pblist_track * );
gboolean mple_get_track_header( nw_device_t *, guint16 );

/* add an already-encoded track to a folder */
guint32 mple_add_to_folder( nw_device_t *, guint32 folder, nw_track_t *, guint32 tracknum, guint32 pos );

/* extract metadata from a file/filehandle into a pblist_track struct */
pblist_track *mple_get_metadata_FILE( FILE * );
pblist_track *mple_get_metadata_NAME( char * );

/* write a file to the device. returns the track number */
guint16 mple_write_file( nw_device_t *, char *, guint16, guint16 );

/* convert a WMMP file back into an MP3 and write it to a file
   descriptor. Doesn't restore ID3 data as yet.  */
gboolean mple_convert_wmmp( nw_track_t *, int /*fd_t*/ );

/* read the media serial number from the specified device */
gboolean mple_get_media_serial_number( nw_device_t * );

/* get a list of likely NW devices on the system */
GList *mple_get_nw_devs( gchar *, GList * );

/* pull aux data from track file */
guint32 mple_track_size( nw_track_t * );
guint32 mple_track_time( nw_track_t * );
guint32 mple_track_frames( nw_track_t * );

/* filesystem-like support */
typedef struct _MPLEFILE {
  NWFILE nwfile; /* NW common data */
  guint8 conv[256]; /* conversion array */
} MPLEFILE;

NWFILE *mple_open( nw_device_t *, guint32, guint32, const gchar *, char *);
int mple_close( NWFILE * );
int mple_seek( NWFILE *, long, int );
long mple_tell( NWFILE * );
size_t mple_read( void *, size_t, size_t, NWFILE *);
size_t mple_write( void *, size_t, size_t, NWFILE *);
int mple_stat( nw_device_t *, guint32, struct stat *buf );

/* convert mple strings into something more useful */
#include <iconv.h>
id3_utf8_t *utf8_from_utf16be( char *, size_t );
guint16 *utf8_to_utf16be( const id3_utf8_t *, size_t, size_t * );

/* convenience */
#define gchar_from_utf16be( c, s ) ((gchar *)utf8_from_utf16be( c, s ))
#define gchar_to_utf16be( c, s, l ) (utf8_to_utf16be( (id3_utf8_t *)c, s, l ))

#include <wchar.h>
gchar *mple_getstr( guint16 * );
wchar_t *guint16o_wchar( guint16 * );
#endif

