// $Id: frame.cpp,v 1.2 2008/10/07 17:20:33 stulleamgym Exp $

// 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.h"
#include "readers.h"
#include "frame_impl.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: frame.cpp,v 1.2 2008/10/07 17:20:33 stulleamgym Exp $
 ** @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();
}

/** 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::Field(ID3_FieldID fieldName) const
{
  return *this->GetField(fieldName);
}

ID3_Field* ID3_Frame::GetField(ID3_FieldID fieldName) const
{
  return _impl->GetField(fieldName);
}

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

/*
ID3_Field* ID3_Frame::GetFieldNum(size_t index) const
{
  return _impl->GetFieldNum(index);
}
*/

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) 
{
  try // Klenotic: Catch errors and return false instead of crashing.
  { 
    return _impl->Parse(reader);
  }
  catch(...)
  {
    ID3D_WARNING( "ID3_Frame::Parse: call to _impl->Parse() failed");
    return false;
  }
}

void ID3_Frame::Render(ID3_Writer& writer) const
{
  _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);
}

