/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// Class for reading (x)or writing to an AVI file.
// Note: the class cannot be used for reading and writing at the same time.
#ifndef WEBRTC_MODULES_MEDIA_FILE_SOURCE_AVI_FILE_H_
#define WEBRTC_MODULES_MEDIA_FILE_SOURCE_AVI_FILE_H_

#include <stdio.h>

#include "typedefs.h"

namespace webrtc {
class CriticalSectionWrapper;
class ListWrapper;

struct AVISTREAMHEADER
{
    AVISTREAMHEADER();
    WebRtc_UWord32 fcc;
    WebRtc_UWord32 cb;
    WebRtc_UWord32 fccType;
    WebRtc_UWord32 fccHandler;
    WebRtc_UWord32 dwFlags;
    WebRtc_UWord16 wPriority;
    WebRtc_UWord16 wLanguage;
    WebRtc_UWord32 dwInitialFrames;
    WebRtc_UWord32 dwScale;
    WebRtc_UWord32 dwRate;
    WebRtc_UWord32 dwStart;
    WebRtc_UWord32 dwLength;
    WebRtc_UWord32 dwSuggestedBufferSize;
    WebRtc_UWord32 dwQuality;
    WebRtc_UWord32 dwSampleSize;
    struct
    {
        WebRtc_Word16 left;
        WebRtc_Word16 top;
        WebRtc_Word16 right;
        WebRtc_Word16 bottom;
    } rcFrame;
};

struct BITMAPINFOHEADER
{
    BITMAPINFOHEADER();
    WebRtc_UWord32 biSize;
    WebRtc_UWord32 biWidth;
    WebRtc_UWord32 biHeight;
    WebRtc_UWord16 biPlanes;
    WebRtc_UWord16 biBitCount;
    WebRtc_UWord32 biCompression;
    WebRtc_UWord32 biSizeImage;
    WebRtc_UWord32 biXPelsPerMeter;
    WebRtc_UWord32 biYPelsPerMeter;
    WebRtc_UWord32 biClrUsed;
    WebRtc_UWord32 biClrImportant;
};

struct WAVEFORMATEX
{
    WAVEFORMATEX();
    WebRtc_UWord16 wFormatTag;
    WebRtc_UWord16 nChannels;
    WebRtc_UWord32 nSamplesPerSec;
    WebRtc_UWord32 nAvgBytesPerSec;
    WebRtc_UWord16 nBlockAlign;
    WebRtc_UWord16 wBitsPerSample;
    WebRtc_UWord16 cbSize;
};

class AviFile
{
public:
    enum AVIStreamType
    {
        AVI_AUDIO = 0,
        AVI_VIDEO = 1
    };

    // Unsigned, for comparison with must-be-unsigned types.
    static const unsigned int CODEC_CONFIG_LENGTH = 64;
    static const unsigned int STREAM_NAME_LENGTH  = 32;

    AviFile();
    ~AviFile();

    WebRtc_Word32 Open(AVIStreamType streamType, const char* fileName,
                       bool loop = false);

    WebRtc_Word32 CreateVideoStream(const AVISTREAMHEADER& videoStreamHeader,
                                    const BITMAPINFOHEADER& bitMapInfoHeader,
                                    const WebRtc_UWord8* codecConfigParams,
                                    WebRtc_Word32 codecConfigParamsLength);

    WebRtc_Word32 CreateAudioStream(const AVISTREAMHEADER& audioStreamHeader,
                                    const WAVEFORMATEX& waveFormatHeader);
    WebRtc_Word32 Create(const char* fileName);

    WebRtc_Word32 WriteAudio(const WebRtc_UWord8* data, WebRtc_Word32 length);
    WebRtc_Word32 WriteVideo(const WebRtc_UWord8* data, WebRtc_Word32 length);

    WebRtc_Word32 GetVideoStreamInfo(AVISTREAMHEADER& videoStreamHeader,
                                     BITMAPINFOHEADER& bitmapInfo,
                                     char* codecConfigParameters,
                                     WebRtc_Word32& configLength);

    WebRtc_Word32 GetDuration(WebRtc_Word32& durationMs);

    WebRtc_Word32 GetAudioStreamInfo(WAVEFORMATEX& waveHeader);

    WebRtc_Word32 ReadAudio(WebRtc_UWord8* data, WebRtc_Word32& length);
    WebRtc_Word32 ReadVideo(WebRtc_UWord8* data, WebRtc_Word32& length);

    WebRtc_Word32 Close();

    static WebRtc_UWord32 MakeFourCc(WebRtc_UWord8 ch0, WebRtc_UWord8 ch1,
                                     WebRtc_UWord8 ch2, WebRtc_UWord8 ch3);

private:
    enum AVIFileMode
    {
        NotSet,
        Read,
        Write
    };

    struct AVIINDEXENTRY
    {
        AVIINDEXENTRY(WebRtc_UWord32 inckid, WebRtc_UWord32 indwFlags,
                      WebRtc_UWord32 indwChunkOffset,
                      WebRtc_UWord32 indwChunkLength);
        WebRtc_UWord32 ckid;
        WebRtc_UWord32 dwFlags;
        WebRtc_UWord32 dwChunkOffset;
        WebRtc_UWord32 dwChunkLength;
    };

    WebRtc_Word32 PrepareDataChunkHeaders();

    WebRtc_Word32 ReadMoviSubChunk(WebRtc_UWord8* data, WebRtc_Word32& length,
                                   WebRtc_UWord32 tag1,
                                   WebRtc_UWord32 tag2 = 0);

    WebRtc_Word32 WriteRIFF();
    WebRtc_Word32 WriteHeaders();
    WebRtc_Word32 WriteAVIMainHeader();
    WebRtc_Word32 WriteAVIStreamHeaders();
    WebRtc_Word32 WriteAVIVideoStreamHeaders();
    WebRtc_Word32 WriteAVIVideoStreamHeaderChunks();
    WebRtc_Word32 WriteAVIAudioStreamHeaders();
    WebRtc_Word32 WriteAVIAudioStreamHeaderChunks();

    WebRtc_Word32 WriteMoviStart();

    size_t PutByte(WebRtc_UWord8 byte);
    size_t PutLE16(WebRtc_UWord16 word);
    size_t PutLE32(WebRtc_UWord32 word);
    size_t PutBuffer(const WebRtc_UWord8* str, size_t size);
    size_t PutBufferZ(const char* str);
    long PutLE32LengthFromCurrent(long startPos);
    void PutLE32AtPos(long pos, WebRtc_UWord32 word);

    size_t GetByte(WebRtc_UWord8& word);
    size_t GetLE16(WebRtc_UWord16& word);
    size_t GetLE32(WebRtc_UWord32& word);
    size_t GetBuffer(WebRtc_UWord8* str, size_t size);

    void CloseRead();
    void CloseWrite();

    void ResetMembers();
    void ResetComplexMembers();

    WebRtc_Word32 ReadRIFF();
    WebRtc_Word32 ReadHeaders();
    WebRtc_Word32 ReadAVIMainHeader();
    WebRtc_Word32 ReadAVIVideoStreamHeader(WebRtc_Word32 endpos);
    WebRtc_Word32 ReadAVIAudioStreamHeader(WebRtc_Word32 endpos);

    WebRtc_UWord32 StreamAndTwoCharCodeToTag(WebRtc_Word32 streamNum,
                                             const char* twoCharCode);

    void ClearIndexList();
    void AddChunkToIndexList(WebRtc_UWord32 inChunkId, WebRtc_UWord32 inFlags,
                             WebRtc_UWord32 inOffset,  WebRtc_UWord32 inSize);

    void WriteIndex();

private:
    struct AVIMAINHEADER
    {
        AVIMAINHEADER();
        WebRtc_UWord32 fcc;
        WebRtc_UWord32 cb;
        WebRtc_UWord32 dwMicroSecPerFrame;
        WebRtc_UWord32 dwMaxBytesPerSec;
        WebRtc_UWord32 dwPaddingGranularity;
        WebRtc_UWord32 dwFlags;
        WebRtc_UWord32 dwTotalFrames;
        WebRtc_UWord32 dwInitialFrames;
        WebRtc_UWord32 dwStreams;
        WebRtc_UWord32 dwSuggestedBufferSize;
        WebRtc_UWord32 dwWidth;
        WebRtc_UWord32 dwHeight;
        WebRtc_UWord32 dwReserved[4];
    };

    struct AVIStream
    {
        AVIStreamType streamType;
        int           streamNumber;
    };

    CriticalSectionWrapper* _crit;
    FILE*            _aviFile;
    AVIMAINHEADER    _aviHeader;
    AVISTREAMHEADER  _videoStreamHeader;
    AVISTREAMHEADER  _audioStreamHeader;
    BITMAPINFOHEADER _videoFormatHeader;
    WAVEFORMATEX     _audioFormatHeader;

    WebRtc_Word8 _videoConfigParameters[CODEC_CONFIG_LENGTH];
    WebRtc_Word32 _videoConfigLength;
    WebRtc_Word8 _videoStreamName[STREAM_NAME_LENGTH];
    WebRtc_Word8 _audioConfigParameters[CODEC_CONFIG_LENGTH];
    WebRtc_Word8 _audioStreamName[STREAM_NAME_LENGTH];

    AVIStream _videoStream;
    AVIStream _audioStream;

    WebRtc_Word32 _nrStreams;
    WebRtc_Word32 _aviLength;
    WebRtc_Word32 _dataLength;
    size_t        _bytesRead;
    size_t        _dataStartByte;
    WebRtc_Word32 _framesRead;
    WebRtc_Word32 _videoFrames;
    WebRtc_Word32 _audioFrames;

    bool _reading;
    AVIStreamType _openedAs;
    bool _loop;
    bool _writing;

    size_t _bytesWritten;

    size_t _riffSizeMark;
    size_t _moviSizeMark;
    size_t _totNumFramesMark;
    size_t _videoStreamLengthMark;
    size_t _audioStreamLengthMark;
    WebRtc_Word32 _moviListOffset;

    bool _writeAudioStream;
    bool _writeVideoStream;

    AVIFileMode _aviMode;
    WebRtc_UWord8* _videoCodecConfigParams;
    WebRtc_Word32 _videoCodecConfigParamsLength;

    WebRtc_UWord32 _videoStreamDataChunkPrefix;
    WebRtc_UWord32 _audioStreamDataChunkPrefix;
    bool _created;

    ListWrapper* _indexList; // Elements are of type AVIINDEXENTRY.
};
} // namespace webrtc

#endif // WEBRTC_MODULES_MEDIA_FILE_SOURCE_AVI_FILE_H_
