/*-------------------------------------------------------------
tiio_jpg_exif.h
Based on source code of a public domain software "Exif Jpeg header manipulation
tool (jhead)" by Matthias Wandel.
For now it is used only for obtaining resolution values.
http://www.sentex.net/~mwandel/jhead/
-------------------------------------------------------------*/
#pragma once
#ifndef TIIO_JPG_EXIF_H
#define TIIO_JPG_EXIF_H

#include <stdlib.h>

// undefining ReadAllTags will load only resolution information
#define ReadAllTags
#undef ReadAllTags

#define MAX_COMMENT_SIZE 16000
#define MAX_DATE_COPIES 10

#ifdef _WIN32
#define PATH_MAX _MAX_PATH
#define SLASH '\\'
#else
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#define SLASH '/'
#endif

class JpgExifReader {
  enum FORMATS {
    FMT_BYTE = 1,
    FMT_STRING,
    FMT_USHORT,
    FMT_ULONG,
    FMT_URATIONAL,
    FMT_SBYTE,
    FMT_UNDEFINED,
    FMT_SSHORT,
    FMT_SLONG,
    FMT_SRATIONAL,
    FMT_SINGLE,
    FMT_DOUBLE
  };

  int NUM_FORMATS = FMT_DOUBLE;

  struct ExifImageInfo {
    float xResolution = 0.0;
    float yResolution = 0.0;
    int ResolutionUnit;
#ifdef ReadAllTags
    char FileName[PATH_MAX + 1];
    time_t FileDateTime;

    unsigned FileSize;
    char CameraMake[32];
    char CameraModel[40];
    char DateTime[20];
    unsigned Height, Width;
    int Orientation;
    int IsColor;
    int Process;
    int FlashUsed;
    float FocalLength;
    float ExposureTime;
    float ApertureFNumber;
    float Distance;
    float CCDWidth;
    float ExposureBias;
    float DigitalZoomRatio;
    int FocalLength35mmEquiv;  // Exif 2.2 tag - usually not present.
    int Whitebalance;
    int MeteringMode;
    int ExposureProgram;
    int ExposureMode;
    int ISOequivalent;
    int LightSource;
    int DistanceRange;

    char Comments[MAX_COMMENT_SIZE];
    int CommentWidthchars;  // If nonzero, widechar comment, indicates number of
                            // chars.

    unsigned ThumbnailOffset;    // Exif offset to thumbnail
    unsigned ThumbnailSize;      // Size of thumbnail.
    unsigned LargestExifOffset;  // Last exif data referenced (to check if
                                 // thumbnail is at end)

    char ThumbnailAtEnd;  // Exif header ends with the thumbnail
    // (we can only modify the thumbnail if its at the end)
    int ThumbnailSizeOffset;

    int DateTimeOffsets[MAX_DATE_COPIES];
    int numDateTimeTags = 0;

    int GpsInfoPresent;
    char GpsLat[31];
    char GpsLong[31];
    char GpsAlt[20];

    int QualityGuess;
#endif
  } ImageInfo;

#ifdef ReadAllTags
  unsigned char* DirWithThumbnailPtrs;
  double FocalplaneXRes;
  double FocalplaneUnits;
  int ExifImageWidth;

  // for fixing the rotation.
  void* OrientationPtr[2];
  int OrientationNumFormat[2];
  int NumOrientations = 0;
#endif

  int MotorolaOrder = 0;

  int Get16u(void* Short);
  unsigned Get32u(void* Long);
  int Get32s(void* Long);
  void PrintFormatNumber(void* ValuePtr, int Format, int ByteCount);
  double ConvertAnyFormat(void* ValuePtr, int Format);

public:
  void process_EXIF(unsigned char* ExifSection, unsigned int length);
  void ProcessExifDir(unsigned char* DirStart, unsigned char* OffsetBase,
                      unsigned ExifLength, int NestingLevel);

  // obtaining resolution info
  bool containsResolution() { return ImageInfo.xResolution != 0.0; }
  float getXResolution() { return ImageInfo.xResolution; }
  float getYResolution() { return ImageInfo.yResolution; }
  int getResolutionUnit() { return ImageInfo.ResolutionUnit; }
};

#endif