/***************************************************************************
 * Copyright (C) 2010
 * by thakis
 *
 * Modification and adjustment for the Wii by Dimok
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you
 * must not claim that you wrote the original software. If you use
 * this software in a product, an acknowledgment in the product
 * documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and
 * must not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 * distribution.
 *
 * gcvid.h
 ***************************************************************************/

#ifndef THAKIS_GCVID_H
#define THAKIS_GCVID_H THAKIS_GCVID_H

#include <stdio.h> //FILE*
#include <string>
#include <vector>
using std::string;
using std::vector;

#include <gccore.h>

#pragma pack(push, 1)

/////////////////////////////////////////////////////////////////////
//THP

struct ThpHeader
{
  char tag[4]; //'THP\0'

  //from monk's thp player:
  u32 version; //0x00011000 = 1.1, 0x00010000 = 1.0
  u32 maxBufferSize;
  u32 maxAudioSamples; //!= 0 if sound is stored in file


  float fps; //usually 29.something (=0x41efc28f) for ntsc
  u32 numFrames;
  u32 firstFrameSize; //size of first frame

  u32 dataSize; //size of file - ThpHeader.offset

  //from monk's thp player:
  u32 componentDataOffset; //ThpComponents stored here
  u32 offsetsDataOffset; //?? if != 0, offset to table with offsets of all frames?

  u32 firstFrameOffset;
  u32 lastFrameOffset;
};

//monk:

struct ThpComponents
{
  u32 numComponents; //usually 1 or 2 (video or video + audio)

  //component type 0 is video, type 1 is audio,
  //type 0xff is "no component" (numComponent many entries
  //are != 0xff)
  u8 componentTypes[16];
};

struct ThpVideoInfo
{
  u32 width;
  u32 height;
  u32 unknown; //only for version 1.1 thp files
};

struct ThpAudioInfo
{
  u32 numChannels;
  u32 frequency;
  u32 numSamples;
  u32 numData; //only for version 1.1 - that many
               //audio blocks are after each video block
               //(for surround sound?)
};

//endmonk


//a frame image is basically a normal jpeg image (without
//the jfif application marker), the only important difference
//is that after the image start marker (0xff 0xda) values
//of 0xff are simply written as 0xff whereas the jpeg
//standard requires them to be written as 0xff 0x00 because
//0xff is the start of a 2-byte control code in jpeg

//frame (offsets relative to frame start):
//u32 total (image, sound, etc) size of NEXT frame
//u32 size1 at 0x04 (total size of PREV frame according to monk)
//u32 image data size at 0x08
//size of one audio block ONLY IF THE FILE HAS SOUND. ThpAudioInfo.numData
//many audio blocks after jpeg data
//jpeg data
//audio block(s)

struct ThpAudioBlockHeader
{
  //size 80 byte
  u32 channelSize; //size of one channel in bytes
  u32 numSamples; //number of samples/channel
  s16 table1[16]; //table for first channel
  s16 table2[16]; //table for second channel
  s16 channel1Prev1;
  s16 channel1Prev2;
  s16 channel2Prev1;
  s16 channel2Prev2;
};

//audio block:
//u32 size of this audioblock
//
//u32 numBytes/channel of audioblock - that many bytes per channel after adpcm table)
//
//u32 number of samples per channel
//
//2*16 shorts adpcm table (one per channel - always stored both,
//even for mono files), 5.11 fixed point values
//
//4 s16: 2 shorts prev1 and prev2 for each channel (even for mono files)
//
//sound data

//sound data:
//8 byte are 14 samples:
//the first byte stores index (upper nibble) and shift (lower nibble),
//the following 7 bytes contain 14o samples a 4 bit each


/////////////////////////////////////////////////////////////////////
//MTH ("mute thp"?)

//similar to a thp file, but without sound

struct MthHeader
{
  //one of the unknown has to be fps in some form

  char tag[4]; //'MTHP'
  u32 unknown;
  u32 unknown2;
  u32 maxFrameSize;

  u32 width;
  u32 height;
  u32 fps;
  u32 numFrames;

  u32 offset;
  u32 unknown5;
  u32 firstFrameSize;

  //5 padding u32's follow
};

//frame:
//u32 size of NEXT frame
//jpeg data

//see thp (above) for jpeg format. there's a small difference, though:
//mth jpegs end with 0xff 0xd9 0xff instead of 0xff 0xd9

#pragma pack(pop)

//little helper class that represents one frame of video
//data is 24 bpp, scanlines aligned to 4 byte boundary
class VideoFrame
{
 public:
  VideoFrame() : data(NULL), _w(0), _h(0), _p(0) { };
  ~VideoFrame() { };

  u8 *data; //texdata style
  void resize(int width, int height);

  int getWidth() const;
  int getHeight() const;
  int getPitch() const;

  void dealloc();
  
 private:
  int _w;
  int _h;
  int _p; //pitch in bytes

  //copy constructor and asignment operator are not allowed
  //VideoFrame(const VideoFrame& f);
  VideoFrame& operator=(const VideoFrame& f);
};


class VideoFile
{
 public:
  VideoFile(FILE* f);
  virtual ~VideoFile();


  virtual int getWidth() const;
  virtual int getHeight() const;
  virtual float getFps() const;
  virtual int getFrameCount() const;
  virtual int getCurrentFrameNr() const;

  virtual bool loadNextFrame(bool skip = false) = 0;

  virtual void getCurrentFrame(VideoFrame& frame) = 0;

  //sound support:
  virtual bool hasSound() const;
  virtual int getNumChannels() const;
  virtual int getFrequency() const;
  virtual int getMaxAudioSamples() const;
  virtual int getCurrentBuffer(s16* data) const;

  bool loop;
 protected:

  FILE* _f;

  //void loadFrame(long offset, int size);
  virtual void loadFrame(VideoFrame& frame, const u8* src, int src_size);

  int countRequiredSize(const u8* src, int src_size, int& start, int& end);
  void convertToRealJpeg(u8* dest, const u8* src, int srcSize, int start, int end);
};

VideoFile* openVideo(const std::string& fileName);
void closeVideo(VideoFile*& vf);


class ThpVideoFile : public VideoFile
{
 public:
  ThpVideoFile() : VideoFile(NULL) { };
  ThpVideoFile(FILE* f) : VideoFile(f) { Init(f); };
  bool Init(FILE *f);
  void DeInit();

  virtual int getWidth() const;
  virtual int getHeight() const;
  virtual float getFps() const;
  virtual int getFrameCount() const;

  virtual int getCurrentFrameNr() const;

  virtual bool loadNextFrame(bool skip = false);

  virtual void getCurrentFrame(VideoFrame& frame);

  virtual bool hasSound() const;
  virtual int getNumChannels() const;
  virtual int getFrequency() const;
  virtual int getMaxAudioSamples() const;
  virtual int getCurrentBuffer(s16* data) const;
  virtual void loadFrame(VideoFrame& frame, const u8* src, int src_size);

 protected:
  ThpHeader _head;
  ThpComponents _components;
  ThpVideoInfo _videoInfo;
  ThpAudioInfo _audioInfo;
  int _numInts;

  int _currFrameNr;
  int _nextFrameOffset;
  int _nextFrameSize;
  u8 *_currFrameData;
  u8 *_currFrameRealData;
};

class MthVideoFile : public VideoFile
{
 public:
  MthVideoFile(FILE* f);

  virtual int getWidth() const;
  virtual int getHeight() const;
  virtual float getFps() const;
  virtual int getFrameCount() const;

  virtual int getCurrentFrameNr() const;

  virtual bool loadNextFrame(bool skip = false);

  virtual void getCurrentFrame(VideoFrame& frame);

 protected:
  MthHeader _head;

  int _currFrameNr;
  int _nextFrameOffset;
  int _nextFrameSize;
  int _thisFrameSize;
  vector<u8> _currFrameData;
};

class JpgVideoFile : public VideoFile
{
 public:
  JpgVideoFile(FILE* f);

  virtual int getWidth() const;
  virtual int getHeight() const;
  virtual int getFrameCount() const;

  virtual bool loadNextFrame(bool skip = false) { return skip; }
  virtual void getCurrentFrame(VideoFrame& frame);

 private:
   VideoFrame _currFrame;
};

void decodeRealJpeg(const u8* data, int size, VideoFrame& dest, bool fancy = false);

#endif //THAKIS_GCVID_H
