void ID3v2_Tag::setTextFrame(const wxString &id, const wxString &value)
{
	if(value.IsEmpty()) {
		removeFrames(id);
		return;
	}

	//if(!d->frameListMap[id].isEmpty())
	//  d->frameListMap[id].front()->setText(value);
	ID3v2_FrameList* it = frameList(id);
	if( it )
	{
		ID3v2_FrameList::Node* l = it->GetFirst();
		if( l )
		{
			l->GetData()->setText(value);
		}
	}
	else
	{
		const SjStringType encoding = m_factory->defaultTextEncoding();

		SjByteVector idBv;
		idBv.appendString(id, SJ_LATIN1);
		ID3v2_TextIdentificationFrame *f = new ID3v2_TextIdentificationFrame(idBv, encoding);

		addFrame(f);
		f->setText(value);
	}
}
ID3v2_CommentsFrame* ID3v2_Tag::findCommentFrame() const
{
	ID3v2_FrameList*        list = frameList(wxT("COMM"));
	ID3v2_CommentsFrame*    frame;
	if( list )
	{
		// prefer comments without description, start searching with the last (mostly the newest)
		ID3v2_FrameList::Node* node = list->GetLast();
		while( node )
		{
			frame = (ID3v2_CommentsFrame*)node->GetData();
			if(  frame->description().IsEmpty()
			        && !frame->text().IsEmpty() )
			{
				return frame;
			}
			node = node->GetPrevious();
		}

		// try to return empty frames without description
		list->GetLast();
		while( node )
		{
			frame = (ID3v2_CommentsFrame*)node->GetData();
			if(  frame->description().IsEmpty() )
			{
				return frame;
			}
			node = node->GetPrevious();
		}

		/*
		if( !forWrite ) // do not overwrite eg. MusicMatch comments
		{
		    // okay, no comment so far, check all comment frames and return the first non-empty comment
		    node = list->GetFirst();
		    while( node )
		    {
		        frame = (ID3v2_CommentsFrame*)node->GetData();
		        if( !frame->text().IsEmpty() )
		        {
		            return frame;
		        }
		        node = node->GetNext();
		    }

		    // just use the first frame
		    node = list->GetFirst();
		    if( node )
		    {
		        return (ID3v2_CommentsFrame*)node->GetData();
		    }
		}*/
	}
	return NULL;
}
wxString ID3v2_Tag::simpleFrame(const wxString& frameId) const
{
	ID3v2_FrameList* list = frameList(frameId);
	if( list )
	{
		ID3v2_FrameList::Node* node = list->GetFirst();
		if( node )
		{
			return node->GetData()->toString();
		}
	}
	return wxEmptyString;
}
void ID3v2_Tag::removeFrames(const wxString &id)
{
	/*FrameList l = d->frameListMap[id];
	for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
	  removeFrame(*it, true);*/
	ID3v2_FrameList* l = frameList(id);
	if( l )
	{
		ID3v2_FrameList::Node* first;
		while( (first=l->GetFirst()) != NULL )
		{
			removeFrame(first->GetData(), true);
		}
	}
}
ID3v2_Tag::~ID3v2_Tag()
{
	if( m_extendedHeader) delete m_extendedHeader;
	if( m_footer ) delete m_footer;

	// delete all frame list contents, the frame list itself is destroyed automatically
	ID3v2_FrameList::Node* node = m_frameList.GetFirst();
	for( ; node; node = node->GetNext() )
	{
		delete node->GetData();
	}

	// delete all frame hash values, the hash itself is destroyed automatically
	SjHashIterator iterator;
	ID3v2_FrameList* frameList;
	wxString frameId;
	while( (frameList=(ID3v2_FrameList*)m_frameListMap.Iterate(iterator, frameId)) )
	{
		delete frameList;
	}
}
SjByteVector ID3v2_Tag::render()
{
	// We need to render the "tag data" first so that we have to correct size to
	// render in the tag's header.  The "tag data" -- everything that is included
	// in ID3v2::Header::tagSize() -- includes the extended header, frames and
	// padding, but does not include the tag's header or footer.

	SjByteVector tagData;

	// TODO: Render the extended header.

	// Loop through the frames rendering them and adding them to the tagData.

	//for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++)
	for ( ID3v2_FrameList::Node *node = m_frameList.GetFirst(); node; node = node->GetNext() )
	{
		ID3v2_Frame* it = node->GetData();
		if(!it->header()->tagAlterPreservation())
			tagData.append(it->render());
	}

	// Compute the amount of padding, and append that to tagData.

	SjUint paddingSize = 0;
	SjUint originalSize = m_header.tagSize();

	if(tagData.size() < originalSize)
		paddingSize = originalSize - tagData.size();
	else
		paddingSize = 1024;

	tagData.append(SjByteVector(paddingSize, char(0)));

	// Set the tag size.
	m_header.setTagSize(tagData.size());

	// TODO: This should eventually include d->footer->render().
	return m_header.render() + tagData;
}
ID3v2_UserTextIdentificationFrame *find(ID3v2_Tag *tag, const wxString &description) // static
{
	const ID3v2_FrameList* l = tag->frameList(wxT("TXXX"));

	/*for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) {
	  UserTextIdentificationFrame *f = dynamic_cast<UserTextIdentificationFrame *>(*it);
	  if(f && f->description() == description)
	    return f;
	}
	*/
	if( l )
	{
		for ( ID3v2_FrameList::Node *node = l->GetFirst(); node; node = node->GetNext() )
		{
			ID3v2_UserTextIdentificationFrame *f = (ID3v2_UserTextIdentificationFrame*)node->GetData();
			if( f && f->description() == description )
				return f;
		}
	}

	return 0;
}