// $Id$

// id3lib: a C++ library for creating and manipulating id3v1/v2 tags
// Copyright 1999, 2000  Scott Thomas Haug
// Copyright 2002 Thijmen Klok (thijmen@id3lib.org)

/* This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Library General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* The id3lib authors encourage improvements and optimisations to be sent to
 * the id3lib coordinator.  Please see the README file for details on where to
 * send such submissions.  See the AUTHORS file for a list of people who have
 * contributed to id3lib.  See the ChangeLog file for a list of changes to
 * id3lib.  These files are distributed with id3lib at
 * http://download.sourceforge.net/id3lib/
 */

#if defined HAVE_CONFIG_H
#	include <config.h>
#endif

#include "frame_impl.h"

#include <id3/readers.h>

/** \class ID3_Frame frame.h id3/frame.h
 ** \brief The representative class of an id3v2 frame.
 **
 ** id3lib defines frames in a funny way.  Using some nice c++ conventions,
 ** ID3_FrameImpl class objects appear to be quite polymorphic; that is, they
 ** can take on many forms.  The same ID3_FrameImpl class provides the
 ** facilities for the implementation of a complex APIC frame and for a simple
 ** text frame.
 **
 ** @author Dirk Mahoney
 ** @version $Id$
 ** @see ID3_Tag
 ** @see ID3_Field
 ** @see ID3_Err
 **/

/** Default constructor; accepts as a default parameter the type of frame
 ** to create.
 **
 ** The parameter which will internally set the frame's structure.  See
 ** SetID() for more details.
 **
 ** @param id The type of frame to create
 ** @see ID3_FrameID
 ** @see SetID
 **/
ID3_Frame::ID3_Frame(ID3_FrameID id) : _impl(new ID3_FrameImpl(id))
{
}

ID3_Frame::ID3_Frame(const ID3_Frame &frame) : _impl(new ID3_FrameImpl(frame))
{
}

ID3_Frame::~ID3_Frame()
{
	delete _impl;
}

/** Clears the frame of all data and resets the frame such that it can take
 ** on the form of any id3v2 frame that id3lib supports.
 **
 ** @see ID3_Tag::Clear
 **/
void ID3_Frame::Clear()
{
	_impl->Clear();
}

/** Returns the type of frame that the object represents.
 **
 ** Useful in conjunction with ID3_Tag::Find() method
 **
 ** @returns The type, or id, of the frame
 ** @see ID3_Tag::Find
 **/
ID3_FrameID ID3_Frame::GetID() const
{
	return _impl->GetID();
}

/** Establishes the internal structure of an ID3_FrameImpl object so
 ** that it represents the id3v2 frame indicated by the parameter
 **
 ** Given an ID3_FrameID (a list of which is found in &lt;id3/field.h&gt;),
 ** SetID() will structure the object according to the
 ** frame you wish to implement.
 **
 ** Either using this call or via the constructor, this must be the first
 ** command performed on an ID3_FrameImpl object.
 **
 ** \code
 **   myFrame.SetID(ID3FID_TITLE);
 ** \endcode
 **
 ** @param id The type of frame this frame should be set to
 ** @see ID3_FrameID
 **/
bool ID3_Frame::SetID(ID3_FrameID id)
{
	return _impl->SetID(id);
}

bool ID3_Frame::SetSpec(ID3_V2Spec spec)
{
	return _impl->SetSpec(spec);
}

ID3_V2Spec ID3_Frame::GetSpec() const
{
	return _impl->GetSpec();
}

ID3_V2Spec ID3_Frame::MinSpec() const
{
	return _impl->MinSpec();
}

/** Returns a pointer to the frame's internal field indicated by the
 ** parameter.
 **
 ** \code
 **   ID3_TextEnc enc;
 **   enc = (ID3_TextEnc) myFrame.GetField(ID3FN_TEXTENC)->Get();
 ** \endcode
 **
 ** @param name The name of the field to be retrieved
 ** @returns A reference to the desired field
 **/
ID3_Field *ID3_Frame::GetField(ID3_FieldID fieldName) const
{
	return _impl->GetField(fieldName);
}

size_t ID3_Frame::NumFields() const
{
	return _impl->NumFields();
}

size_t ID3_Frame::Size()
{
	return _impl->Size();
}

bool ID3_Frame::HasChanged() const
{
	return _impl->HasChanged();
}

ID3_Frame &ID3_Frame::operator=(const ID3_Frame &rFrame)
{
	if (this != &rFrame)
	{
		*_impl = rFrame;
	}

	return *this;
}

const char *ID3_Frame::GetDescription(ID3_FrameID id)
{
	return ID3_FrameImpl::GetDescription(id);
}

const char *ID3_Frame::GetDescription() const
{
	return _impl->GetDescription();
}

const char *ID3_Frame::GetTextID() const
{
	return _impl->GetTextID();
}

bool ID3_Frame::Parse(ID3_Reader &reader)
{
	return _impl->Parse(reader);
}

ID3_Err ID3_Frame::Render(ID3_Writer &writer) const
{
	return _impl->Render(writer);
}

bool ID3_Frame::Contains(ID3_FieldID id) const
{
	return _impl->Contains(id);
}

/** Sets the compression flag within the frame.  When the compression flag is
 ** is set, compression will be attempted.  However, the frame might not
 ** actually be compressed after it is rendered if the "compressed" data is
 ** no smaller than the "uncompressed" data.
 **/
bool ID3_Frame::SetCompression(bool b)
{
	return _impl->SetCompression(b);
}

/** Returns whether or not the compression flag is set.  After parsing a tag,
 ** this will indicate whether or not the frame was compressed.  After
 ** rendering a tag, however, it does not actually indicate if the frame is
 ** compressed rendering.  It only indicates whether or not compression was
 ** attempted.  A frame will not be compressed, even whent the compression
 ** flag is set, if the "compressed" data is no smaller than the
 ** "uncompressed" data.
 **/
bool ID3_Frame::GetCompression() const
{
	return _impl->GetCompression();
}

size_t ID3_Frame::GetDataSize() const
{
	return _impl->GetDataSize();
}

bool ID3_Frame::SetEncryptionID(uchar id)
{
	return _impl->SetEncryptionID(id);
}

uchar ID3_Frame::GetEncryptionID() const
{
	return _impl->GetEncryptionID();
}

bool ID3_Frame::SetGroupingID(uchar id)
{
	return _impl->SetGroupingID(id);
}

uchar ID3_Frame::GetGroupingID() const
{
	return _impl->GetGroupingID();
}

namespace
{
	class IteratorImpl : public ID3_Frame::Iterator
	{
		ID3_FrameImpl::iterator	 _cur;
		ID3_FrameImpl::iterator	 _end;

		public:
					 IteratorImpl(ID3_FrameImpl &frame)
			 :	_cur(frame.begin()),
				_end(frame.end())
			{
			}

			ID3_Field	*GetNext()
			{
				ID3_Field	*next = NULL;

				while (next == NULL && _cur != _end)
				{
					next = *_cur;
					++_cur;
				}

				return next;
			}
	};

	class ConstIteratorImpl : public ID3_Frame::ConstIterator
	{
		ID3_FrameImpl::const_iterator	 _cur;
		ID3_FrameImpl::const_iterator	 _end;

		public:
					 ConstIteratorImpl(ID3_FrameImpl& frame)
			 :	 _cur(frame.begin()), _end(frame.end())
			{
			}

			const ID3_Field	*GetNext()
			{
				ID3_Field	*next = NULL;

				while (next == NULL && _cur != _end)
				{
					next = *_cur;
					++_cur;
				}

				return next;
			}
	};
}

ID3_Frame::Iterator *ID3_Frame::CreateIterator()
{
	return new IteratorImpl(*_impl);
}

ID3_Frame::ConstIterator *ID3_Frame::CreateIterator() const
{
	return new ConstIteratorImpl(*_impl);
}
