internal MetadataToVorbisCommentAdapter(MetadataDictionary metadata)
        {
            Contract.Requires(metadata != null);

            var day   = 0;
            var month = 0;
            var year  = 0;

            foreach (var item in metadata)
            {
                switch (item.Key)
                {
                case "Day":
                    day = int.Parse(item.Value, CultureInfo.InvariantCulture);
                    break;

                case "Month":
                    month = int.Parse(item.Value, CultureInfo.InvariantCulture);
                    break;

                case "Year":
                    year = int.Parse(item.Value, CultureInfo.InvariantCulture);
                    break;

                default:
                    string mappedKey;
                    if (_map.TryGetValue(item.Key, out mappedKey))
                    {
                        this[mappedKey] = item.Value;
                    }
                    break;
                }
            }

            // The date field should contain either a full date, or just the year:
            if (day > 0 && month > 0 && year > 0)
            {
                Contract.Assume(month <= 12);
                this["DATE"] = new DateTime(year, month, day).ToShortDateString();
            }
            else if (year > 0)
            {
                this["DATE"] = year.ToString(CultureInfo.InvariantCulture);
            }

            if (metadata.CoverArt != null)
            {
                this["METADATA_BLOCK_PICTURE"] = new MetadataBlockPicture(metadata.CoverArt).ToString();
            }
        }
        internal VorbisCommentToMetadataAdapter(VorbisComment vorbisComment)
        {
            var commentLengths = new int[vorbisComment.Comments];

            Marshal.Copy(vorbisComment.CommentLengths, commentLengths, 0, commentLengths.Length);

            var commentPtrs = new IntPtr[vorbisComment.Comments];

            Marshal.Copy(vorbisComment.UserComments, commentPtrs, 0, commentPtrs.Length);

            for (var i = 0; i < vorbisComment.Comments; i++)
            {
                var commentBytes = new byte[commentLengths[i]];
                Marshal.Copy(commentPtrs[i], commentBytes, 0, commentLengths[i]);

                string[] comment = Encoding.UTF8.GetString(commentBytes).Split(new[] { '=' }, 2);

                Contract.Assert(comment.Length == 2);

                // The track number and count may be packed into the same comment:
                switch (comment[0])
                {
                case "TRACKNUMBER":
                    string[] segments = comment[1].Split('/');
                    base["TrackNumber"] = segments[0];
                    if (segments.Length > 1)
                    {
                        base["TrackCount"] = segments[1];
                    }
                    break;

                case "DATE":
                case "YEAR":
                    // The DATE comment may contain a full date, or only the year:
                    DateTime result;
                    if (DateTime.TryParse(comment[1], CultureInfo.CurrentCulture,
                                          DateTimeStyles.NoCurrentDateDefault, out result) && result.Year >= 1000)
                    {
                        base["Day"]   = result.Day.ToString(CultureInfo.InvariantCulture);
                        base["Month"] = result.Month.ToString(CultureInfo.InvariantCulture);
                        base["Year"]  = result.Year.ToString(CultureInfo.InvariantCulture);
                    }
                    else
                    {
                        base["Year"] = comment[1];
                    }
                    break;

                case "METADATA_BLOCK_PICTURE":
                    var picture = new MetadataBlockPicture(comment[1]);
                    if (picture.Type == PictureType.CoverFront || picture.Type == PictureType.Other)
                    {
                        try
                        {
                            CoverArt = new CoverArt(picture.Data);
                        }
                        catch (UnsupportedCoverArtException)
                        { }
                    }
                    break;

                case "COVERART":
                    try
                    {
                        // Deprecated way to store cover art:
                        CoverArt = new CoverArt(Convert.FromBase64String(comment[1]));
                    }
                    catch (UnsupportedCoverArtException)
                    { }
                    break;

                default:
                    string mappedKey;
                    if (_map.TryGetValue(comment[0], out mappedKey))
                    {
                        base[mappedKey] = comment[1];
                    }
                    break;
                }
            }
        }