/// <summary>
        ///    Creates a deep copy of the current instance.
        /// </summary>
        /// <returns>
        ///    A new <see cref="Frame" /> object identical to the
        ///    current instance.
        /// </returns>
        public override Frame Clone()
        {
            UnsynchronisedLyricsFrame frame =
                new UnsynchronisedLyricsFrame(description,
                                              language, encoding);

            frame.text = text;
            return(frame);
        }
        /// <summary>
        ///    Gets a specified comments frame from the specified tag,
        ///    trying to to match the description and language but
        ///    accepting an incomplete match.
        /// </summary>
        /// <param name="tag">
        ///    A <see cref="Tag" /> object to search in.
        /// </param>
        /// <param name="description">
        ///    A <see cref="string" /> specifying the description to
        ///    match.
        /// </param>
        /// <param name="language">
        ///    A <see cref="string" /> specifying the ISO-639-2 language
        ///   code to match.
        /// </param>
        /// <returns>
        ///    A <see cref="UnsynchronisedLyricsFrame" /> object
        ///    containing the matching frame, or <see langword="null" />
        ///    if a match wasn't found.
        /// </returns>
        /// <remarks>
        ///    <para>The method tries matching with the following order
        ///    of precidence:</para>
        ///    <list type="number">
        ///       <item><term>The first frame with a matching
        ///       description and language.</term></item>
        ///       <item><term>The first frame with a matching
        ///       language.</term></item>
        ///       <item><term>The first frame with a matching
        ///       description.</term></item>
        ///       <item><term>The first frame.</term></item>
        ///    </list>
        /// </remarks>
        public static UnsynchronisedLyricsFrame GetPreferred(Tag tag,
                                                             string description,
                                                             string language)
        {
            // This is weird, so bear with me. The best thing we can
            // have is something straightforward and in our own
            // language. If it has a description, then it is
            // probably used for something other than an actual
            // comment. If that doesn't work, we'd still rather have
            // something in our language than something in another.
            // After that all we have left are things in other
            // languages, so we'd rather have one with actual
            // content, so we try to get one with no description
            // first.

            int best_value = -1;
            UnsynchronisedLyricsFrame best_frame = null;

            foreach (Frame frame in tag.GetFrames(FrameType.USLT))
            {
                UnsynchronisedLyricsFrame uslt =
                    frame as UnsynchronisedLyricsFrame;

                if (uslt == null)
                {
                    continue;
                }

                bool same_name = uslt.Description == description;
                bool same_lang = uslt.Language == language;

                if (same_name && same_lang)
                {
                    return(uslt);
                }

                int value = same_lang ? 2 : same_name ? 1 : 0;

                if (value <= best_value)
                {
                    continue;
                }

                best_value = value;
                best_frame = uslt;
            }

            return(best_frame);
        }
        /// <summary>
        ///    Gets a specified lyrics frame from the specified tag,
        ///    optionally creating it if it does not exist.
        /// </summary>
        /// <param name="tag">
        ///    A <see cref="Tag" /> object to search in.
        /// </param>
        /// <param name="description">
        ///    A <see cref="string" /> specifying the description to
        ///    match.
        /// </param>
        /// <param name="language">
        ///    A <see cref="string" /> specifying the ISO-639-2 language
        ///   code to match.
        /// </param>
        /// <param name="create">
        ///    A <see cref="bool" /> specifying whether or not to create
        ///    and add a new frame to the tag if a match is not found.
        /// </param>
        /// <returns>
        ///    A <see cref="UnsynchronisedLyricsFrame" /> object
        ///    containing the matching frame, or <see langword="null" />
        ///    if a match wasn't found and <paramref name="create" /> is
        ///    <see langword="false" />.
        /// </returns>
        public static UnsynchronisedLyricsFrame Get(Tag tag,
                                                    string description,
                                                    string language,
                                                    bool create)
        {
            UnsynchronisedLyricsFrame uslt;

            foreach (Frame frame in tag.GetFrames(FrameType.USLT))
            {
                uslt = frame as UnsynchronisedLyricsFrame;

                if (uslt == null)
                {
                    continue;
                }

                if (uslt.Description != description)
                {
                    continue;
                }

                if (language != null && language != uslt.Language)
                {
                    continue;
                }

                return(uslt);
            }

            if (!create)
            {
                return(null);
            }

            uslt = new UnsynchronisedLyricsFrame(description,
                                                 language);
            tag.AddFrame(uslt);
            return(uslt);
        }