{$MODE OBJFPC}
{$MODESWITCH AUTODEREF}
uses
  dtypes,
  dlog,
  dlog_stderr,
  dstrnum,
  dvector2i,
  dvector2f,
  dvector4f,
  drect2i,
  dstream,
  dstreamfile,
  dtexture2d,
  dtiff_reader,
  dtiff_maximal;

type
TTiffDumper = object
private
  TiffReader: PTiffReader;
  J: LongInt;
  S: AnsiString;
  R: TTiffRational;
  SR: TTiffSRational;
  StripByteCounts: array of LongInt;
  StripOffsets: array of LongInt;
public
  procedure Init(Reader: PTiffReader);
  procedure Done;
  procedure Dump;
  procedure DumpDataLzw(const Prefix: AnsiString; Offset, Count: LongInt);
end;

procedure TTiffDumper.Init(Reader: PTiffReader);
begin
  TiffReader := Reader;
end;

procedure TTiffDumper.Done;
begin
end;

procedure TTiffDumper.Dump;
var
  I: LongInt;
begin
  while True do
    case TiffReader.NextEntry of
      TIFF_TAG_END: begin
        Log('TiffReader: END');
        break;
      end;
      TIFF_TAG_CORRUPTED: begin
        Log('TiffReader FAILED: CORRUPTED');
        Halt(1);
      end;
      TIFF_TAG_NEW_SUBFILE_TYPE: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_Long) then begin  
          Log('TiffReader FAILED: NewSubfileType has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        I := TiffReader.EntryLong;
        S := '';
        S := S + '  ReducedResolution=' + Ord(I and TIFF_NEW_SUBFILE_TYPE_REDUCED_RESOLUTION_MASK);
        S := S + '  SinglePage=' + Ord(I and TIFF_NEW_SUBFILE_TYPE_SINGLE_PAGE_MASK);
        S := S + '  TransparencyMask=' + Ord(I and TIFF_NEW_SUBFILE_TYPE_TRANSPARENCY_MASK_MASK);
        Log('TiffReader: NewSubfileType=' + S);
      end;
      TIFF_TAG_SUBFILE_TYPE: begin
        Log('TiffReader: SubfileType');
      end;
      TIFF_TAG_PHOTOMETRIC_INTERPRETATION: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_SHORT) then begin  
          Log('TiffReader FAILED: PhotometricInterpretation has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        I := TiffReader.EntryShort;
        case I of
          TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO: begin
            Log('TiffReader: PlanarConfiguration=WhiteIsZero');
          end;
          TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO: begin
            Log('TiffReader: PhotometricInterpretation=BlackIsZero');
          end;
          TIFF_PHOTOMETRIC_INTERPRETATION_RGB: begin
            Log('TiffReader: PhotometricInterpretation=RGB');
          end;
          TIFF_PHOTOMETRIC_INTERPRETATION_RGB_PALETTE: begin
            Log('TiffReader: PhotometricInterpretation=RGBPalette');
          end;
          TIFF_PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK: begin
            Log('TiffReader: PhotometricInterpretation=TransprarencyMask');
          end;
          TIFF_PHOTOMETRIC_INTERPRETATION_CMYK: begin
            Log('TiffReader: PhotometricInterpretation=CMYK');
          end;
          TIFF_PHOTOMETRIC_INTERPRETATION_YCbCr: begin
            Log('TiffReader: PhotometricInterpretation=YCbCr');
          end;
          TIFF_PHOTOMETRIC_INTERPRETATION_CIELab: begin
            Log('TiffReader: PhotometricInterpretation=CIELab');
          end;
          else
            Log('TiffReader FAILED: PhotometricInterpretation has unexpected value: ' + IntToStr(I));
            Halt(1);
        end;
      end;
      TIFF_TAG_COMPRESSION: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_SHORT) then begin  
          Log('TiffReader FAILED: Compression has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        I := TiffReader.EntryShort;
        case I of
          TIFF_COMPRESSION_UNCOMPRESSED: begin
            Log('TiffReader: Compression=Uncompressed');
          end;
          TIFF_COMPRESSION_CCITT_1D: begin
            Log('TiffReader: Compression=CCITT1D');
          end;
          TIFF_COMPRESSION_GROUP_3_FAX: begin
            Log('TiffReader: Compression=Group3FAX');
          end;
          TIFF_COMPRESSION_GROUP_4_FAX: begin
            Log('TiffReader: Compression=Group4FAX');
          end;
          TIFF_COMPRESSION_LZW: begin
            Log('TiffReader: Compression=LZW');
          end;
          TIFF_COMPRESSION_JPEG: begin
            Log('TiffReader: Compression=JPEG');
          end;
          TIFF_COMPRESSION_PACK_BITS: begin
            Log('TiffReader: Compression=PackBits');
          end;
          else
            Log('TiffReader FAILED: Compression has unexpected value: ' + IntToStr(I));
            Halt(1);
        end;
      end;
      TIFF_TAG_IMAGE_LENGTH: begin
        if TiffReader.Entry._Type = TIFF_TYPE_SHORT then begin
          Log('TiffReader: ImageLength=' + IntToStr(TiffReader.EntryShort));
        end else if TiffReader.Entry._Type = TIFF_TYPE_LONG then begin
          Log('TiffReader: ImageLength=' + IntToStr(TiffReader.EntryLong));
        end else begin
          Log('TiffReader FAILED: ImageLength has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
      end;
      TIFF_TAG_IMAGE_WIDTH: begin
        if TiffReader.Entry._Type = TIFF_TYPE_SHORT then begin
          Log('TiffReader: ImageWidth=' + IntToStr(TiffReader.EntryShort));
        end else if TiffReader.Entry._Type = TIFF_TYPE_LONG then begin
          Log('TiffReader: ImageWidth=' + IntToStr(TiffReader.EntryLong));
        end else begin
          Log('TiffReader FAILED: ImageWidth has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
      end;
      TIFF_TAG_RESOLUTION_UNIT: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_SHORT) then begin  
          Log('TiffReader FAILED: ResolutionUnit has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        I := TiffReader.EntryShort;
        case I of
          TIFF_RESOLUTION_UNIT_NO:
            Log('TiffReader: ResolutionUnit=No');
          TIFF_RESOLUTION_UNIT_INCH:
            Log('TiffReader: ResolutionUnit=Inch');
          TIFF_RESOLUTION_UNIT_CENTIMETER:
            Log('TiffReader: ResolutionUnit=Centimeter');
          else
            Log('TiffReader FAILED: ResolutionUnit has unexpected value: ' + IntToStr(I));
            Halt(1);
          end;
      end;
      TIFF_TAG_XRESOLUTION: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_RATIONAL) then begin  
          Log('TiffReader FAILED: XResolution has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        R := TiffReader.EntryRational;
        Log('TiffReader: XResolution=' + IntToStr(R.Numerator) + '/' + IntToStr(R.Denumerator));
      end;
      TIFF_TAG_YRESOLUTION: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_RATIONAL) then begin  
          Log('TiffReader FAILED: YResolution has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        R := TiffReader.EntryRational;
        Log('TiffReader: YResolution=' + IntToStr(R.Numerator) + '/' + IntToStr(R.Denumerator));
      end;
      TIFF_TAG_ROWS_PER_STRIP: begin
        if TiffReader.Entry._Type = TIFF_TYPE_SHORT then begin
          Log('TiffReader: RowsPerStrip=' + IntToStr(TiffReader.EntryShort));
        end else if TiffReader.Entry._Type = TIFF_TYPE_LONG then begin
          Log('TiffReader: RowsPerStrip=' + IntToStr(TiffReader.EntryLong));
        end else begin
          Log('TiffReader FAILED: RowsPerStrip has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
      end;
      TIFF_TAG_STRIP_OFFSETS: begin
        if TiffReader.Entry._Type = TIFF_TYPE_SHORT then begin
            S := '';
            for I := 0 to TiffReader.Entry.Count - 1 do begin
              J := TiffReader.EntryShort(I);
              SetLength(StripOffsets, Length(StripOffsets) + 1);
              StripOffsets[I] := J;
              if Length(StripOffsets) <= Length(StripByteCounts) then
                DumpDataLzw('Strip: ', StripOffsets[I], StripByteCounts[I]);
              S := S + ' ' + IntToStr(J);
            end;
            Log('TiffReader: StripOffsets[' + IntToStr(TiffReader.Entry.Count) + '] =' + S);
        end else if TiffReader.Entry._Type = TIFF_TYPE_LONG then begin
            S := '';
            for I := 0 to TiffReader.Entry.Count - 1 do begin
              J := TiffReader.EntryLong(I);
              SetLength(StripOffsets, Length(StripOffsets) + 1);
              StripOffsets[I] := J;
              if Length(StripOffsets) <= Length(StripByteCounts) then
                DumpDataLzw('Strip: ', StripOffsets[I], StripByteCounts[I]);
              S := S + ' ' + IntToStr(J);
            end;
            Log('TiffReader: StripOffsets[' + IntToStr(TiffReader.Entry.Count) + '] =' + S);
        end else begin
          Log('TiffReader FAILED: StripOffsets has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
      end;
      TIFF_TAG_STRIP_BYTE_COUNTS: begin
        if TiffReader.Entry._Type = TIFF_TYPE_SHORT then begin
            S := '';
            for I := 0 to TiffReader.Entry.Count - 1 do begin
              J := TiffReader.EntryShort(I);
              SetLength(StripByteCounts, Length(StripByteCounts) + 1);
              StripByteCounts[I] := J;
              if Length(StripOffsets) >= Length(StripByteCounts) then
                DumpDataLzw('Strip: ', StripOffsets[I], StripByteCounts[I]);
              S := S + ' ' + IntToStr(J);
            end;
            Log('TiffReader: StripByteCounts[' + IntToStr(TiffReader.Entry.Count) + '] =' + S);
        end else if TiffReader.Entry._Type = TIFF_TYPE_LONG then begin
            S := '';
            for I := 0 to TiffReader.Entry.Count - 1 do begin
              J := TiffReader.EntryLong(I);
              SetLength(StripByteCounts, Length(StripByteCounts) + 1);
              StripByteCounts[I] := J;
              if Length(StripOffsets) >= Length(StripByteCounts) then
                DumpDataLzw('Strip: ', StripOffsets[I], StripByteCounts[I]);
              S := S + ' ' + IntToStr(J);
            end;
            Log('TiffReader: StripByteCounts[' + IntToStr(TiffReader.Entry.Count) + '] =' + S);
        end else begin
          Log('TiffReader FAILED: StripByteCounts has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
      end;
      TIFF_TAG_BITS_PER_SAMPLE: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_SHORT) then begin  
          Log('TiffReader FAILED: BitsPerSample has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        S := '';
        for I := 0 to TiffReader.Entry.Count - 1 do
          S := S + ' ' + IntToStr(TiffReader.EntryShort(I));
        Log('TiffReader: BitsPerSample[' + IntToStr(TiffReader.Entry.Count) + '] =' + S);
      end;
      TIFF_TAG_COLOR_MAP: begin
        Log('TiffReader: ColorMap');
      end;
      TIFF_TAG_SAMPLES_PER_PIXEL: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_SHORT) then begin  
          Log('TiffReader FAILED: SamplesPerPixel has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        Log('TiffReader: SamplesPerPixel=' + IntToStr(TiffReader.EntryShort));
      end;
      TIFF_TAG_THRESHHOLDING: begin
        Log('TiffReader: Threshholding');
      end;
      TIFF_TAG_CELL_WIDTH: begin
        Log('TiffReader: CellWidth');
      end;
      TIFF_TAG_CELL_LENGTH: begin
        Log('TiffReader: CellLength');
      end;
      TIFF_TAG_FILL_ORDER: begin
        Log('TiffReader: FillOrder');
      end;
      TIFF_TAG_DOCUMENT_NAME: begin
        Log('TiffReader: DocumentName');
      end;
      TIFF_TAG_IMAGE_DESCRIPTION: begin
        Log('TiffReader: Description');
      end;
      TIFF_TAG_MAKE: begin
        Log('TiffReader: Make');
      end;
      TIFF_TAG_MODEL: begin
        Log('TiffReader: Model');
      end;
      TIFF_TAG_ORIENTATION: begin
        Log('TiffReader: Orientation');
      end;
      TIFF_TAG_MIN_SAMPLE_VALUE: begin
        Log('TiffReader: MinSampleValue');
      end;
      TIFF_TAG_MAX_SAMPLE_VALUE: begin
        Log('TiffReader: MaxSampleValue');
      end;
      TIFF_TAG_PLANAR_CONFIGURATION: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_SHORT) then begin  
          Log('TiffReader FAILED: PlanarConfiguration has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        I := TiffReader.EntryShort;
        case I of
          TIFF_PLANAR_CONFIGURATION_CHUNKY: begin
            Log('TiffReader: PlanarConfiguration=Chunky');
          end;
          TIFF_PLANAR_CONFIGURATION_PLANAR: begin
            Log('TiffReader: PlanarConfiguration=Planar');
          end;
          else
            Log('TiffReader FAILED: PlanarConfiguration has unexpected value: ' + IntToStr(I));
            Halt(1);
        end;
      end;
      TIFF_TAG_PAGE_NAME: begin
        Log('TiffReader: PageName');
      end;
      TIFF_TAG_X_POSITION: begin
        Log('TiffReader: XPosition');
      end;
      TIFF_TAG_Y_POSITION: begin
        Log('TiffReader: YPosition');
      end;
      TIFF_TAG_FREE_OFFSETS: begin
        Log('TiffReader: FreeOffsets');
      end;
      TIFF_TAG_FREE_BYTE_COUNTS: begin
        Log('TiffReader: FreeByteCounts');
      end;
      TIFF_TAG_GRAY_RESPONSE_UNIT: begin
        Log('TiffReader: GrayResponseUnit');
      end;
      TIFF_TAG_GRAY_RESPONSE_CURVE: begin
        Log('TiffReader: GrayResponseCurve');
      end;
      TIFF_TAG_T4_OPTIONS: begin
        Log('TiffReader: T4Options');
      end;
      TIFF_TAG_T6_OPTIONS: begin
        Log('TiffReader: T6Options');
      end;
      TIFF_TAG_PAGE_NUMBER: begin
        Log('TiffReader: PageNumber');
      end;
      TIFF_TAG_SOFTWARE: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_ASCII) then begin  
          Log('TiffReader FAILED: Software has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        Log('TiffReader: Software="' + TiffReader.EntryASCII + '"');
      end;
      TIFF_TAG_DATE_TIME: begin
        Log('TiffReader: DateTime');
      end;
      TIFF_TAG_ARTIST: begin
        Log('TiffReader: Artist');
      end;
      TIFF_TAG_HOST_COMPUTER: begin
        Log('TiffReader: HostComputer');
      end;
      TIFF_TAG_PREDICTOR: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_SHORT) then begin  
          Log('TiffReader FAILED: Predictor has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        I := TiffReader.EntryShort;
        case I of
          TIFF_PREDICTOR_NO:
            Log('TiffReader: Predictor=No');
          TIFF_PREDICTOR_HORIZONTAL_DIFFERENCING:
            Log('TiffReader: Predictor=HorizontalDifferencing');
          else
            Log('TiffReader FAILED: Predictor has unexpected value: ' + IntToStr(I));
            Halt(1);
        end;
      end;
      TIFF_TAG_WHITE_POINT: begin
        Log('TiffReader: WhitePoint');
      end;
      TIFF_TAG_PRIMARY_CHROMATICITIES: begin
        Log('TiffReader: PrimaryChromaticities');
      end;
      TIFF_TAG_HALFTONE_HINTS: begin
        Log('TiffReader: HalftoneHints');
      end;
      TIFF_TAG_TILE_WIDTH: begin
        Log('TiffReader: TileWidth');
      end;
      TIFF_TAG_TILE_LENGTH: begin
        Log('TiffReader: TileLength');
      end;
      TIFF_TAG_TILE_OFFSETS: begin
        Log('TiffReader: TileOffsets');
      end;
      TIFF_TAG_TILE_BYTE_COUNTS: begin
        Log('TiffReader: TileByteCounts');
      end;
      TIFF_TAG_INK_SET: begin
        Log('TiffReader: InkSet');
      end;
      TIFF_TAG_INK_NAMES: begin
        Log('TiffReader: InkNames');
      end;
      TIFF_TAG_NUMBER_OF_INKS: begin
        Log('TiffReader: NumberOfInks');
      end;
      TIFF_TAG_DOT_RANGE: begin
        Log('TiffReader: DotRange');
      end;
      TIFF_TAG_TARGET_PRINTER: begin
        Log('TiffReader: TargetPrinter');
      end;
      TIFF_TAG_EXTRA_SAMPLES: begin
        if not TiffReader.CheckEntryType(TIFF_TYPE_SHORT) then begin  
          Log('TiffReader FAILED: ExtraSamples has wrong type: ' + IntToStr(TiffReader.Entry._Type));
          Halt(1);
        end;
        S := '';
        for I := 0 to TiffReader.Entry.Count - 1 do begin
          J := TiffReader.EntryShort(I);
          case J of
            TIFF_EXTRA_SAMPLES_UNSPECIFIED:
              S := S + ' UnspecifiedData';
            TIFF_EXTRA_SAMPLES_ASSOCIATED_ALPHA:
              S := S + ' AssociatedAlpha';
            TIFF_EXTRA_SAMPLES_UNASSOCIATED_ALPHA:
              S := S + ' UnassociatedAlpha';
            else
              Log('TiffReader FAILED: ExtraSamples has unexpected value: ' + IntToStr(J));
          end;
        end;
        Log('TiffReader: ExtraSamples[' + IntToStr(TiffReader.Entry.Count) + '] =' + S);
      end;
      TIFF_TAG_SAMPLE_FORMAT: begin
        Log('TiffReader: SampleFormat');
      end;
      TIFF_TAG_S_MIN_SAMPLE_VALUE: begin
        Log('TiffReader: SMinSampleValue');
      end;
      TIFF_TAG_S_MAX_SAMPLE_VALUE: begin
        Log('TiffReader: SMaxSampleValue');
      end;
      TIFF_TAG_TRANSFER_RANGE: begin
        Log('TiffReader: TransferRange');
      end;
      TIFF_TAG_JPEG_PROC: begin
        Log('TiffReader: JpegProc');
      end;
      TIFF_TAG_JPEG_INTERCHANGE_FORMAT: begin
        Log('TiffReader: JpegInterchangeFormat');
      end;
      TIFF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH: begin
        Log('TiffReader: JpegInterchangeFormatLength');
      end;
      TIFF_TAG_JPEG_RESTART_INTERVAL: begin
        Log('TiffReader: JpegRestartInterval');
      end;
      TIFF_TAG_JPEG_LOSSLESS_PREDICTORS: begin
        Log('TiffReader: JpegLosslessPredictors');
      end;
      TIFF_TAG_JPEG_POINT_TRANSFORMS: begin
        Log('TiffReader: JpegPointTransforms');
      end;
      TIFF_TAG_JPEG_Q_TABLES: begin
        Log('TiffReader: JpegQTables');
      end;
      TIFF_TAG_JPEG_DC_TABLES: begin
        Log('TiffReader: JpegDCTables');
      end;
      TIFF_TAG_JPEG_AC_TABLES: begin
        Log('TiffReader: JpegACTables');
      end;
      TIFF_TAG_YCbCrCoefficients: begin
        Log('TiffReader: YCbCrCoefficients');
      end;
      TIFF_TAG_YCbCrSubSampling: begin
        Log('TiffReader: YCbCrSubSampling');
      end;
      TIFF_TAG_YCbCrPositioning: begin
        Log('TiffReader: YCbCrPositioning');
      end;
      TIFF_TAG_REFERENCE_BLACK_WHITE: begin
        Log('TiffReader: ReferenceBlackWhite');
      end;
      TIFF_TAG_COPYRIGHT: begin
        Log('TiffReader: Copyright');
      end;
      else
        Log('TiffReader: unknown Tag=' + IntToStr(TiffReader.Entry.Tag) + ', skipped');
    end;
end;

procedure TTiffDumper.DumpDataLzw(const Prefix: AnsiString; Offset, Count: LongInt);
var
  P: Pointer;
  L: LongInt;
  B: PByte;
  A: PArrayOfByte;
  I: LongInt;
begin
  if not TiffReader.ReadLzwData(Offset, Count, P, L) then begin
    Log('TiffReader FAILED: ' + Prefix + 'cannot decode LZW');
    Halt(1);
  end;
  B := P;
  A := P;
  for I := L - 1 downto 4 do begin
  // TODO: only horizontal difference
  //for I := 4 to L - 1 do begin
    //A^[I] := A^[I] + A^[I - 4];
  end;
  for I := 0 to L - 1 do begin
    S := S + ' ' + IntToStr(A^[I]);
  end;
  {while L > 0 do begin
    S := S + ' ' + IntToStr(B^);
    Dec(L);
    Inc(B);
  end;}
  Log('TiffReader: ' + Prefix + S);
  FreeMem(P);
end;

var
  TiffReader: TTiffReader;
  TiffDumper: TTiffDumper;

begin
  //Eng.InitDisplayMode(800, 600, 32, DENG_GL);
  //Eng.InitTitle('DEng: Filled rects test');
  //Eng.InitRenderFunc(Render);
  //Eng.InitUpdateFunc(Update, 30);
  //Eng.Run;
  TiffReader.Init('small.tif');
  if not TiffReader.IsHeaderCorrect then begin
    Log('TiffReader FAILED: Bad tiff header');
    TiffReader.Done;
    Halt(1);
  end;
  Log('TiffReader little endian: ' + IntToStr(Ord(TiffReader.IsLittleEndian)));
  Log('TiffReader 1st IFD entries number: ' + IntToStr(Ord(TiffReader.NumberOfEntries)));
  TiffDumper.Init(@TiffReader);
  TiffDumper.Dump;
  TiffDumper.Done;
  TiffReader.Done;
end.
