/// <summary>
        /// Writes the tags to the specified file.
        /// </summary>
        /// <param name="fileHandle">The handle to the file to which to write the tags.</param>
        internal void WriteToFile(IntPtr fileHandle)
        {
            IntPtr tagsPtr = NativeMethods.MP4TagsAlloc();

            NativeMethods.MP4TagsFetch(tagsPtr, fileHandle);
            NativeMethods.MP4Tags tags = tagsPtr.ReadStructure <NativeMethods.MP4Tags>();
            if (this.Title != tags.name)
            {
                NativeMethods.MP4TagsSetName(tagsPtr, this.Title);
            }

            if (this.Artist != tags.artist)
            {
                NativeMethods.MP4TagsSetArtist(tagsPtr, this.Artist);
            }

            if (this.Album != tags.album)
            {
                NativeMethods.MP4TagsSetAlbum(tagsPtr, this.Album);
            }

            if (this.AlbumArtist != tags.albumArtist)
            {
                NativeMethods.MP4TagsSetAlbumArtist(tagsPtr, this.AlbumArtist);
            }

            if (this.Grouping != tags.grouping)
            {
                NativeMethods.MP4TagsSetGrouping(tagsPtr, this.Grouping);
            }

            if (this.Composer != tags.composer)
            {
                NativeMethods.MP4TagsSetComposer(tagsPtr, this.Composer);
            }

            if (this.Comment != tags.comment)
            {
                NativeMethods.MP4TagsSetComments(tagsPtr, this.Comment);
            }

            if (this.Genre != tags.genre)
            {
                NativeMethods.MP4TagsSetGenre(tagsPtr, this.Genre);
            }

            if (this.GenreType != tags.genreType.ReadShort())
            {
                tagsPtr.WriteShort(this.GenreType, NativeMethods.MP4TagsSetGenreType);
            }

            if (this.ReleaseDate != tags.releaseDate)
            {
                NativeMethods.MP4TagsSetReleaseDate(tagsPtr, this.ReleaseDate);
            }

            if (this.Tempo != tags.tempo.ReadShort())
            {
                tagsPtr.WriteShort(this.Tempo, NativeMethods.MP4TagsSetTempo);
            }

            if (this.IsCompilation != tags.compilation.ReadBoolean())
            {
                tagsPtr.WriteBoolean(this.IsCompilation, NativeMethods.MP4TagsSetCompilation);
            }

            if (this.TVShow != tags.tvShow)
            {
                NativeMethods.MP4TagsSetTVShow(tagsPtr, this.TVShow);
            }

            if (this.TVNetwork != tags.tvNetwork)
            {
                NativeMethods.MP4TagsSetTVNetwork(tagsPtr, this.TVNetwork);
            }

            if (this.EpisodeId != tags.tvEpisodeID)
            {
                NativeMethods.MP4TagsSetTVEpisodeID(tagsPtr, this.EpisodeId);
            }

            if (this.SeasonNumber != tags.tvSeason.ReadInt())
            {
                tagsPtr.WriteInt(this.SeasonNumber, NativeMethods.MP4TagsSetTVSeason);
            }

            if (this.EpisodeNumber != tags.tvEpisode.ReadInt())
            {
                tagsPtr.WriteInt(this.EpisodeNumber, NativeMethods.MP4TagsSetTVEpisode);
            }

            if (this.Description != tags.description)
            {
                NativeMethods.MP4TagsSetDescription(tagsPtr, this.Description);
            }

            if (this.LongDescription != tags.longDescription)
            {
                NativeMethods.MP4TagsSetLongDescription(tagsPtr, this.LongDescription);
            }

            if (this.Lyrics != tags.lyrics)
            {
                NativeMethods.MP4TagsSetLyrics(tagsPtr, this.Lyrics);
            }

            if (this.SortName != tags.sortName)
            {
                NativeMethods.MP4TagsSetSortName(tagsPtr, this.SortName);
            }

            if (this.SortArtist != tags.sortArtist)
            {
                NativeMethods.MP4TagsSetSortArtist(tagsPtr, this.SortArtist);
            }

            if (this.SortAlbum != tags.sortAlbum)
            {
                NativeMethods.MP4TagsSetSortAlbum(tagsPtr, this.SortAlbum);
            }

            if (this.SortAlbumArtist != tags.sortAlbumArtist)
            {
                NativeMethods.MP4TagsSetSortAlbumArtist(tagsPtr, this.SortAlbumArtist);
            }

            if (this.SortComposer != tags.sortComposer)
            {
                NativeMethods.MP4TagsSetSortComposer(tagsPtr, this.SortComposer);
            }

            if (this.SortTVShow != tags.sortTVShow)
            {
                NativeMethods.MP4TagsSetSortTVShow(tagsPtr, this.SortTVShow);
            }

            if (this.Copyright != tags.copyright)
            {
                NativeMethods.MP4TagsSetCopyright(tagsPtr, this.Copyright);
            }

            if (this.EncodingTool != tags.encodingTool)
            {
                NativeMethods.MP4TagsSetEncodingTool(tagsPtr, this.EncodingTool);
            }

            if (this.EncodedBy != tags.encodedBy)
            {
                NativeMethods.MP4TagsSetEncodedBy(tagsPtr, this.EncodedBy);
            }

            if (this.PurchasedDate != tags.purchasedDate)
            {
                NativeMethods.MP4TagsSetPurchaseDate(tagsPtr, this.PurchasedDate);
            }

            if (this.IsPodcast != tags.podcast.ReadBoolean())
            {
                tagsPtr.WriteBoolean(this.IsPodcast, NativeMethods.MP4TagsSetPodcast);
            }

            if (this.Keywords != tags.keywords)
            {
                NativeMethods.MP4TagsSetKeywords(tagsPtr, this.Keywords);
            }

            if (this.Category != tags.category)
            {
                NativeMethods.MP4TagsSetCategory(tagsPtr, this.Category);
            }

            if (this.IsHDVideo != tags.hdVideo.ReadEnumValue <HDKind>(HDKind.SD))
            {
                byte?HDKindValue = this.IsHDVideo == HDKind.SD ? null : (byte?)this.IsHDVideo;
                tagsPtr.WriteByte(HDKindValue, NativeMethods.MP4TagsSetHDVideo);
            }

            if (this.MediaType != tags.mediaType.ReadEnumValue <MediaKind>(MediaKind.NotSet))
            {
                byte?mediaTypeValue = this.MediaType == MediaKind.NotSet ? null : (byte?)this.MediaType;
                tagsPtr.WriteByte(mediaTypeValue, NativeMethods.MP4TagsSetMediaType);
            }

            if (this.ContentRating != tags.mediaType.ReadEnumValue <ContentRating>(ContentRating.NotSet))
            {
                byte?contentRatingValue = this.ContentRating == ContentRating.NotSet ? null : (byte?)this.ContentRating;
                tagsPtr.WriteByte(contentRatingValue, NativeMethods.MP4TagsSetContentRating);
            }

            if (this.IsGapless != tags.gapless.ReadBoolean())
            {
                tagsPtr.WriteBoolean(this.IsGapless, NativeMethods.MP4TagsSetGapless);
            }

            if (this.MediaStoreAccount != tags.itunesAccount)
            {
                NativeMethods.MP4TagsSetITunesAccount(tagsPtr, this.MediaStoreAccount);
            }

            if (this.MediaStoreAccountType != tags.iTunesAccountType.ReadEnumValue <MediaStoreAccountKind>(MediaStoreAccountKind.NotSet))
            {
                byte?accountTypeValue = this.MediaStoreAccountType == MediaStoreAccountKind.NotSet ? null : (byte?)this.MediaStoreAccountType;
                tagsPtr.WriteByte(accountTypeValue, NativeMethods.MP4TagsSetITunesAccountType);
            }

            if (this.MediaStoreCountry != tags.iTunesCountry.ReadEnumValue <Country>(Country.None))
            {
                int?countryValue = this.MediaStoreCountry == Country.None ? null : (int?)this.MediaStoreCountry;
                tagsPtr.WriteInt(countryValue, NativeMethods.MP4TagsSetITunesCountry);
            }

            if (this.ContentId != tags.contentID.ReadInt())
            {
                tagsPtr.WriteInt(this.ContentId, NativeMethods.MP4TagsSetContentID);
            }

            if (this.ArtistId != tags.artistID.ReadInt())
            {
                tagsPtr.WriteInt(this.ArtistId, NativeMethods.MP4TagsSetArtistID);
            }

            if (this.PlaylistId != tags.playlistID.ReadLong())
            {
                tagsPtr.WriteLong(this.PlaylistId, NativeMethods.MP4TagsSetPlaylistID);
            }

            if (this.GenreId != tags.genreID.ReadInt())
            {
                tagsPtr.WriteInt(this.GenreId, NativeMethods.MP4TagsSetGenreID);
            }

            if (this.ComposerId != tags.composerID.ReadInt())
            {
                tagsPtr.WriteInt(this.ComposerId, NativeMethods.MP4TagsSetComposerID);
            }

            if (this.Xid != tags.xid)
            {
                NativeMethods.MP4TagsSetXID(tagsPtr, this.Xid);
            }

            this.WriteTrackInfo(tagsPtr, tags.track);
            this.WriteDiscInfo(tagsPtr, tags.disk);

            // If the artwork has been edited, there are two possibilities:
            // First we are replacing an existing piece of artwork with another; or
            // second, we are deleting the artwork that already existed.
            if (this.isArtworkEdited)
            {
                if (this.artwork != null)
                {
                    this.WriteArtwork(tagsPtr);
                }
                else if (this.ArtworkCount != 0)
                {
                    NativeMethods.MP4TagsRemoveArtwork(tagsPtr, 0);
                }
            }

            NativeMethods.MP4TagsStore(tagsPtr, fileHandle);
            NativeMethods.MP4TagsFree(tagsPtr);

            RatingInfo info = ReadRawAtom <RatingInfo>(fileHandle);

            if (this.RatingInfo != info)
            {
                WriteRawAtom <RatingInfo>(fileHandle, this.RatingInfo);
            }

            // TODO: Implement an equality comparison for MovieInfo, so
            // as to only write the atom to the file if it's been modified.
            // MovieInfo movieInfo = ReadRawAtom<MovieInfo>(fileHandle);
            // if (this.MovieInfo != movieInfo)
            // {
            //    WriteRawAtom<MovieInfo>(fileHandle, this.MovieInfo);
            // }
            WriteRawAtom <MovieInfo>(fileHandle, this.MovieInfo);
        }
        /// <summary>
        /// Reads the tags from the specified file.
        /// </summary>
        /// <param name="fileHandle">The handle to the file from which to read the tags.</param>
        /// <returns>A new instance of a <see cref="MetadataTags"/> object containing the values
        /// in the metadata tags for the file.</returns>
        internal static MetadataTags ReadFromFile(IntPtr fileHandle)
        {
            IntPtr tagPtr = NativeMethods.MP4TagsAlloc();

            NativeMethods.MP4TagsFetch(tagPtr, fileHandle);
            NativeMethods.MP4Tags tags        = tagPtr.ReadStructure <NativeMethods.MP4Tags>();
            MetadataTags          managedTags = new MetadataTags();

            managedTags.Title       = tags.name;
            managedTags.Artist      = tags.artist;
            managedTags.AlbumArtist = tags.albumArtist;
            managedTags.Album       = tags.album;
            managedTags.Grouping    = tags.grouping;
            managedTags.Composer    = tags.composer;
            managedTags.Comment     = tags.comment;
            managedTags.Genre       = tags.genre;
            managedTags.ReadTrackInfo(tags.track);
            managedTags.ReadDiskInfo(tags.disk);
            managedTags.Tempo         = tags.tempo.ReadShort();
            managedTags.IsCompilation = tags.compilation.ReadBoolean();
            managedTags.Copyright     = tags.copyright;
            managedTags.EncodingTool  = tags.encodingTool;
            managedTags.EncodedBy     = tags.encodedBy;

            // Tags specific to TV Episodes.
            managedTags.EpisodeNumber = tags.tvEpisode.ReadInt();
            managedTags.SeasonNumber  = tags.tvSeason.ReadInt();
            managedTags.EpisodeId     = tags.tvEpisodeID;
            managedTags.TVNetwork     = tags.tvNetwork;
            managedTags.TVShow        = tags.tvShow;

            managedTags.Description     = tags.description;
            managedTags.LongDescription = tags.longDescription;
            managedTags.Lyrics          = tags.lyrics;

            managedTags.SortName        = tags.sortName;
            managedTags.SortArtist      = tags.sortArtist;
            managedTags.SortAlbumArtist = tags.sortAlbumArtist;
            managedTags.SortAlbum       = tags.sortAlbum;
            managedTags.SortComposer    = tags.sortComposer;
            managedTags.SortTVShow      = tags.sortTVShow;

            managedTags.ArtworkCount = tags.artworkCount;
            managedTags.ReadArtwork(tags.artwork);

            managedTags.IsPodcast = tags.podcast.ReadBoolean();
            managedTags.Keywords  = tags.keywords;
            managedTags.Category  = tags.category;

            managedTags.IsHDVideo     = tags.hdVideo.ReadEnumValue <HDKind>(HDKind.SD);
            managedTags.MediaType     = tags.mediaType.ReadEnumValue <MediaKind>(MediaKind.NotSet);
            managedTags.ContentRating = tags.contentRating.ReadEnumValue <ContentRating>(ContentRating.NotSet);
            managedTags.IsGapless     = tags.gapless.ReadBoolean();

            managedTags.MediaStoreAccount     = tags.itunesAccount;
            managedTags.MediaStoreCountry     = tags.iTunesCountry.ReadEnumValue <Country>(Country.None);
            managedTags.MediaStoreAccountType = tags.iTunesAccountType.ReadEnumValue <MediaStoreAccountKind>(MediaStoreAccountKind.NotSet);
            managedTags.ContentId             = tags.contentID.ReadInt();
            managedTags.ArtistId   = tags.artistID.ReadInt();
            managedTags.PlaylistId = tags.playlistID.ReadInt();
            managedTags.GenreId    = tags.genreID.ReadInt();
            managedTags.ComposerId = tags.composerID.ReadInt();
            managedTags.Xid        = tags.xid;

            NativeMethods.MP4TagsFree(tagPtr);

            managedTags.RatingInfo = ReadRawAtom <RatingInfo>(fileHandle);
            managedTags.MovieInfo  = ReadRawAtom <MovieInfo>(fileHandle);
            return(managedTags);
        }