/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM is free software; you can redistribute it and/or modify it    
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; only version 2 of the License.      
 * 
 * NMM 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 General Public 
 * License for more details.  
 * 
 * You should have received a copy of the GNU General Public License 
 * along with NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */


#ifndef NMM_MP3READNODE_HPP
#define NMM_MP3READNODE_HPP

#include "nmm/nmm_prefix.hpp"

#include <map>
#include <string>
#include <fstream>

#include "nmm/multimedia/GenericSourceNode.hpp"

#include "nmm/plugins/interfaces/IBufferSizeImpl.hpp"
#include "nmm/plugins/interfaces/IProgressImpl.hpp"

#include "nmm/plugins/file/interfaces/ISourceFileHandlerImpl.hpp"
#include "nmm/plugins/file/interfaces/ISeekableImpl.hpp"
#include "nmm/plugins/file/interfaces/IMetaDataImpl.hpp"

#include "visibility.hpp"

namespace NMM {

  class MP3ReadNode :
    public GenericSourceNode,
    public ISourceFileHandlerImpl,
    public ISeekableImpl,
    public IProgressImpl,
    public IBufferSizeImpl,
    public IMetaDataImpl
  {
  private:
    enum States {
      DATA,
      SET_TITLE,
      TAGINFO,
      START,
      START_FLUSH,
      NEXT_TRACK,
      SET_FORMAT,
      TRACK_ENDED,
      FINISHED,
      TRACK_DURATION
    };                /** possible node states */
    
    int state;        /** current node state */
      string filename;  /** currently opened file */
    
    bool send_format;   /** TRUE if FormatChanged event must be sent */
    bool send_progress; /** TRUE if progress event must be sent */
    unsigned int buffers_before_progress; /** buffers to receive before sending progress event */
    unsigned int buffers_progress;        /** buffers still received */

    unsigned int buffer_size; /** currently used buffer size */

    // seeking & file position
      u_int64_t         filesize;         /** size of current file */
      u_int64_t         filepos;          /** bytes position in file */
      int64_t           seekto;           /** bytes to seek */
      int64_t           seekto_frame;     /** frame to seek */

      Interval          track_duration;   /** duration of current file */

    fstream in; /* file object */

    /* curent mp3 frame info */
      char header[4];          /** header string */
      char fmpeg;              /** mpeg format */
      char flayer;             /** mpeg layer */
      char fprotbit;           /** protection bit */
      int  fbitrate;           /** bitrate */
      int  fsample_rate;       /** samplerate */
      char fpadding;           /** padding bit */
      int fnumchannels;        /** number of channels */

      bool found_tag;          /** True if tags were found */
      char title[31];          /** title */
      char interpret[31];      /** interpret */
      char album[31];          /** album */
      char year[5];            /** year */
      char comment[31];        /** comment */
      int genre;               /** genre */

      int frames_total;         /** total number of frames  */
      int64_t frames_pos;       /** read position in frames */
      bool have_mp3_frame_map;  /** True if mp3 frame map is initialized */
      map<int64_t, int64_t> mp3_header; /** mp3 frame header map */

      u_int64_t mp3_data_start;  /** byte position of mp3 data start */
      u_int64_t mp3_data_end;    /** byte position of mp3 data end */

      Timestamp timestamp;       /** currently used timestamp */

      const Format* old_format;  /** last files format */
      Format old_connection_format; /** last connection format */
      Format new_simple_format;  /** new connection format */  

    /* private functions */

    /**
     * method tests if string contains valid header and parses it
     * 
     * \param header string to be tested for a valid header
     *
     * \returns true if valid header is found else false
     */
     bool parseHeader(char* header);

    /**
     * method tries to read complete frames from file
     * 
     * \param read_buffer pointer to buffer to write read frames to
     * \param max_bytes maximum of bytes that can be written to read_buffer 
     *
     * \returns number of frames written to read_buffer
     */
     int getNextFrames(char* read_buffer, size_t max_bytes);

    /**
     * method returns number of samples per frame 
     * 
     * \param mpeg mpeg format
     * \param layer mpeg layer
     *
     * \returns number of samples per frame
     */
      int getSamplesPerFrame(int mpeg, int layer);
    
     /**
     * closes old file, opens new and sets new format
     * 
     * \returns SUCCESS if successful else FAILURE
     */
      Result openFile();

     /**
     * initializes framemap for current file 
     * 
     * \returns number of found frames
     */
      int initializeMP3FrameMap();

     /**
     * find first valid frame 
     * 
     * \returns byte position of first valid frame
     */
     int analyseFirstValidMP3Frame();
    
    /**
    * check file with fstream in, if it has a RIFF header
    * \returns 0, if no RIFF header was found
    * \returns position of data section (without data chunk)
    */
    int isWav();
    /**
    * check file with fstream in, if it has a ID3v2 header
    * \returns 0, if no ID3v2 header was found
    * \returns position of the end of ID3v2 header
    */
    int isID3v2();

    /**
    * check file with fstream in, if it has a ID3vq header
    * \returns 0, if no ID3v1 header was found
    * \returns position of the start of ID3v1 header  
    */
    int isID3v1();



  protected:
    
    virtual Result doInit();
    virtual Result doInitOutput();
    virtual Result doDeinitOutput();
    virtual Result doActivate();

    virtual Message* processBuffer(Buffer *);
    
    
    // ISourceFileHandler interface
    virtual void setFilename(const string& name);
    virtual nmm_uint64 getFilesize();

    // ISeekable interface
    virtual nmm_bool hasPercentSeek() const;
    virtual void seekPercent(const Rational& percent);
    virtual void seekPercentTo(const Rational& percent);
    virtual nmm_bool hasBytesSeek() const;
    virtual void seekBytes(const nmm_int64& offset);
    virtual void seekBytesTo(const nmm_uint64& bytes);
    virtual nmm_bool hasTimeSeek() const;
    virtual void seekTime(const Interval& interval);
    virtual void seekTimeTo(const Time& time);

    //IMetaData interface
    virtual void getMetaData(string&,string&,string&,string&,string&);

    // IBufferSize interface
    virtual void setBufferSize(const nmm_int32& size);
    virtual nmm_int32 getBufferSize();
    
    // IProgress interface
    virtual void sendProgressInformation(const nmm_bool&);
    virtual void setProgressInterval(const nmm_int32& buffers);

  public:

    MP3ReadNode(const char *name = "MP3ReadNode",  
		const StreamQueue::Mode queue_mode = StreamQueue::DEFAULT_MODE,
		const int size = StreamQueue::DEFAULT_SIZE);

    ~MP3ReadNode();
  };

}

#include "nmm/nmm_suffix.hpp"

#endif
