// Simple 1920*1080*3*2 SGI -> YUV converter
// (c) Sony Europe, 2013.

// Compile with
//    g++ -o convert_sgi_to_hevcyuv convert_sgi_to_hevcyuv.cpp -pedantic -Wall

#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#define FILENAME_BUFFER_SIZE 4096

#define IMG_WIDTH 1920
#define IMG_HEIGHT 1080
#define BYTES_PER_SAMPLE 2
#define NUMBER_OF_CHANNELS 3
#define SGI_HEADER_SIZE 512
#define IMAGE_SIZE_EXCL_HDR IMG_WIDTH*IMG_HEIGHT*BYTES_PER_SAMPLE*NUMBER_OF_CHANNELS
#define IMAGE_SIZE_INCL_HDR IMAGE_SIZE_EXCL_HDR+SGI_HEADER_SIZE


int main(int argc, char *argv[])
{
  if (argc<=4 || std::string(argv[0])=="--help" || std::string(argv[1])=="-h" )
  {
    std::cout << "Syntax: <input sgi filename> <starting file number> <number of files> <output yuv filename>\n"
              << " where <input sgi filename> should include C-style formatted string/number controls\n"
              << "  eg " << argv[0] << " %05d.yuv 7111 500 CrowdRun_RGB_16bit.yuv\n";
    return 0;
  }

  std::string sgiFilenamePattern=argv[1];
  std::string opYuvFilename=argv[4];

  if (sgiFilenamePattern.size()+128 > FILENAME_BUFFER_SIZE) // Note that the buffer could still be overflowed if the user used, for example "%129d".
  {
    std::cout << "Filename is too big. Please recompile with larger FILENAME_BUFFER_SIZE value.\n";
    return 1;
  }

  char *tmp;
  unsigned long startFileNumber=strtoul(argv[2], &tmp, 10);
  if (*tmp !=0 )
  {
    std::cout << "Bad <starting file number>: '" << argv[2] << "'\n";
    return 1;
  }

  unsigned long numberOfFiles=strtoul(argv[3], &tmp, 10);
  if (*tmp !=0 )
  {
    std::cout << "Bad <number of files>: '" << argv[2] << "'\n";
    return 1;
  }

  // check if output file already exists...
  {
    std::ifstream ifile(opYuvFilename.c_str());
    if (ifile.good() && ifile.is_open())
    {
      std::cout << "File '" << opYuvFilename << "' already exists. Not overwriting.\n";
      return 1;
    }
  }
  
  // output yuv file for writing
  std::ofstream ofile(opYuvFilename.c_str(), std::ios::binary | std::ios::out);
  if (!ofile.good() || !ofile.is_open())
  {
    std::cout << "Unable to open file '" << opYuvFilename << "' for writing.\n";
    return 1;
  }

  std::vector<char> srcFileBuffer(IMAGE_SIZE_EXCL_HDR);
  std::vector<char> opFileBuffer(IMAGE_SIZE_EXCL_HDR);

  for(unsigned long fileNumDelta=0; fileNumDelta < numberOfFiles; fileNumDelta++)
  {
    unsigned long fileNum=fileNumDelta+startFileNumber;
    char bufferName[FILENAME_BUFFER_SIZE];
    sprintf(bufferName, sgiFilenamePattern.c_str(), fileNum);
    std::string sSGIFilename(bufferName);

    // open file and calculate length of it...
    std::ifstream ifile(sSGIFilename.c_str(), std::ios::binary | std::ios::in);
    if (!ifile.good() || !ifile.is_open())
    {
      std::cout << "Could not open file '" << sSGIFilename << "'\n";
      // YUV file will be valid for the files it has processed though.
      return 1;
    }
    ifile.seekg(0, std::ios::end);
    std::size_t filelength=ifile.tellg();
    if (filelength != IMAGE_SIZE_INCL_HDR)
    {
      std::cout << "Could not process file '" << sSGIFilename << "'\n";
      return 1;
    }
    ifile.seekg(SGI_HEADER_SIZE, std::ios::beg);
    ifile.read(&(srcFileBuffer[0]), IMAGE_SIZE_EXCL_HDR);
    ifile.close();
    
    // the image is stored up-side down.

    for(int channel=0; channel<NUMBER_OF_CHANNELS; channel++)
    {
      for(int y=0; y<IMG_HEIGHT; y++)
      {
        char *opLine=(&(opFileBuffer[0])) + (((channel*IMG_HEIGHT)+y)*IMG_WIDTH*BYTES_PER_SAMPLE);
        const char *ipLine=(&(srcFileBuffer[0])) + (((channel*IMG_HEIGHT)+(IMG_HEIGHT-1-y))*IMG_WIDTH*BYTES_PER_SAMPLE);
        if (BYTES_PER_SAMPLE==2)
        {
          for(int x=0; x<IMG_WIDTH; x++)
          {
            opLine[x*2]  =ipLine[x*2+1];
            opLine[x*2+1]=ipLine[x*2];
          }
        }
        else
        {
          for(int x=0; x<IMG_WIDTH; x++)
          {
            opLine[x]  =ipLine[x];
          }
        }
      }
    }
    
    ofile.write(&(opFileBuffer[0]), IMAGE_SIZE_EXCL_HDR);
  }

  return 0;
}

