classdef Tiff
  properties (Constant = true)
    TagID = struct(
        "SubFileType", 254,
        "ImageWidth", 256,
        "ImageLength", 257,
        "BitsPerSample", 258,
        "Compression", 259,
        "Photometric", 262,
        "Thresholding", 263,
        "FillOrder", 266,
        "DocumentName", 269,
        "ImageDescription", 270,
        "Make", 271,
        "Model", 272,
        "StripOffsets", 273,
        "Orientation", 274,
        "SamplesPerPixel", 277,
        "RowsPerStrip", 278,
        "StripByteCounts", 279,
        "MinSampleValue", 280,
        "MaxSampleValue", 281,
        "XResolution", 282,
        "YResolution", 283,
        "PlanarConfiguration", 284,
        "PageName", 285,
        "XPosition", 286,
        "YPosition", 287,
        "GrayResponseUnit", 290,
        'GrayResponseCurve', 291,
        "Group3Options", 292,
        "Group4Options", 293,
        "ResolutionUnit", 296,
        "PageNumber", 297,
        "TransferFunction", 301,
        "Software", 305,
        "DateTime", 306,
        "Artist", 315,
        "HostComputer", 316,
        "WhitePoint", 318,
        "PrimaryChromaticities", 319,
        "ColorMap", 320,
        "HalfToneHints", 321,
        "TileWidth", 322,
        "TileLength", 323,
        "TileOffsets", 324,
        "TileByteCounts", 325,
        "SubIFD", 330,
        "InkSet", 332,
        "InkNames", 333,
        "NumberOfInks", 334,
        "DotRange", 336,
        "TargetPrinter", 337,
        "ExtraSamples", 338,
        "SampleFormat", 339,
        "SMinSampleValue", 340,
        "SMaxSampleValue", 341,
        "YCbCrCoefficients", 529,
        "YCbCrSubSampling", 530,
        "YCbCrPositioning", 531,
        "ReferenceBlackWhite", 532,
        "XMP", 700,
        "ImageDepth", 32997,
        "Copyright", 33432,
        "RichTIFFIPTC", 33723,
        "Photoshop", 34377,
        "ICCProfile", 34675,
        "SToNits", 37439,
        "JPEGQuality", 65537,
        "JPEGColorMode", 65538,
        "ZipQuality", 65557,
        "SGILogDataFmt", 65560
    )
  endproperties

  properties (Access = private)
    tiff_handle
  endproperties

  methods
    function t = Tiff (filename, mode="r")
      if (nargin == 0 || nargin > 2)
        % print_usage();
        error("Usage: Tiff(filename[, mode])");
      endif

      t.tiff_handle = __open_tiff__ (filename, mode);
    endfunction

    function close (t)
      __close_tiff__ (t.tiff_handle);
    endfunction

    function tag = getTag (t, tag_name)
      tag = __tiff_get_tag__ (t.tiff_handle, tag_name);
    endfunction

    function setTag (t, tag_name, tag_value)
      __tiff_set_tag__ (t.tiff_handle, tag_name, tag_value);
    endfunction

    function argout = read (t)
      argout = __tiff_read__ (t.tiff_handle);
    endfunction

    function writeEncodedStrip (t, stripNumber, imageData)
      __tiff_write_encoded_strip__ (t.tiff_handle, stripNumber, imageData);
    endfunction

    % TODO(maged): add documentation and make print_usage work
  endmethods
endclassdef

%!function set_configs (img, height, width, channels=-1, bit_depth=-1, rows_per_strip=-1)
%!  setTag (img, "ImageLength", height);
%!  setTag (img, "ImageWidth", width);
%!  if channels != -1
%!    setTag (img, "SamplesPerPixel", channels);
%!  endif
%!  if bit_depth != -1
%!    setTag (img, "BitsPerSample", bit_depth);
%!  endif
%!  if rows_per_strip != -1
%!    setTag(img, "RowsPerStrip", rows_per_strip);
%!  endif
%!endfunction

## test one-pixel grayscale image
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 1, 1, 1, 8);
%!    data = uint8 (randi (intmax ("uint8"), 1, 1));
%!    writeEncodedStrip (a, 1, data);
%!    a.close ();
%!    a = Tiff (filename, "r");
%!    data2 = a.read ();
%!    assert (size (data2), [1, 1]);
%!    assert (data2, data);
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test failure to write to image without dimensions
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    data = uint8 (randi (intmax ("uint8"), 1, 1));
%!    fail ("writeEncodedStrip (a, 1, data)", "Failed to read image width");
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test one row grayscale image
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 1, 10, 1, 8);
%!    data = uint8 (randi (intmax ("uint8"), 1, 10));
%!    writeEncodedStrip (a, 1, data);
%!    a.close ();
%!    a = Tiff (filename, "r");
%!    data2 = a.read ();
%!    assert (size (data2), [1, 10]);
%!    assert (data2, data);
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test failure of wrong size of row
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 1, 10, 1, 8);
%!    data = uint8 (randi (intmax ("uint8"), 1, 9));
%!    fail ("writeEncodedStrip (a, 1, data)", "Size of strip data is different from the expected size of the strip");
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test one strip grayscale image
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 10, 10, 1, 8);
%!    data = uint8 (randi (intmax ("uint8"), 10, 10));
%!    writeEncodedStrip (a, 1, data);
%!    a.close ();
%!    a = Tiff (filename, "r");
%!    data2 = a.read ();
%!    assert (size (data2), [10, 10]);
%!    assert (data2, data);
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test failure wrong height of strip
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 10, 10, 1, 8);
%!    data = uint8 (randi (intmax ("uint8"), 11, 10));
%!    fail ("writeEncodedStrip (a, 1, data)", "Size of strip data is different from the expected size of the strip");
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test one strip RGB image chunky planes (RGBRGBRGB)
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 10, 10, 3, 8);
%!    setTag(a, "PlanarConfiguration", 1);
%!    setTag(a, "PhotometricInterpretation", 2);
%!    data = uint8 (randi (intmax ("uint8"), 10, 10, 3));
%!    writeEncodedStrip (a, 1, data);
%!    a.close ();
%!    a = Tiff (filename, "r");
%!    data2 = a.read ();
%!    assert (size (data2), [10, 10, 3]);
%!    assert (data2, data);
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test failure wrong number of channels
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 10, 10, 3, 8);
%!    setTag(a, "PlanarConfiguration", 1);
%!    setTag(a, "PhotometricInterpretation", 2);
%!    data = uint8 (randi (intmax ("uint8"), 10, 10, 4));
%!    fail ("writeEncodedStrip (a, 1, data)", "Size of strip data is different from the expected size of the strip");
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test one strip RGB image separate planes (RRRGGGBBB)
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 10, 10, 3, 8);
%!    setTag(a, "PlanarConfiguration", 2);
%!    setTag(a, "PhotometricInterpretation", 2);
%!    data = uint8 (randi (intmax ("uint8"), 10, 10, 3));
%!    for i = 1:3
%!      writeEncodedStrip (a, i, data(:,:,i));
%!    endfor
%!    a.close ();
%!    a = Tiff (filename, "r");
%!    data2 = a.read ();
%!    assert (size (data2), [10, 10, 3]);
%!    assert (data2, data);
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test failure of strip size in separate planes image
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 10, 10, 3, 8);
%!    setTag(a, "PlanarConfiguration", 2);
%!    setTag(a, "PhotometricInterpretation", 2);
%!    data = uint8 (randi (intmax ("uint8"), 10, 10, 3));
%!    fail ("writeEncodedStrip (a, 1, data)", "Size of strip data is different from the expected size of the strip");
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test 16-bit grayscale image
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 20, 20, 1, 16);
%!    data = uint16 (randi (intmax ("uint16"), 20, 20));
%!    writeEncodedStrip (a, 1, data);
%!    a.close ();
%!    a = Tiff (filename, "r");
%!    data2 = a.read ();
%!    assert (size (data2), [20, 20]);
%!    assert (data2, data);
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test 32-bit grayscale image
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 20, 20, 1, 32);
%!    data = uint32 (randi (intmax ("uint32"), 20, 20));
%!    writeEncodedStrip (a, 1, data);
%!    a.close ();
%!    a = Tiff (filename, "r");
%!    data2 = a.read ();
%!    assert (size (data2), [20, 20]);
%!    assert (data2, data);
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test 16-bit RGB image
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 20, 20, 3, 16);
%!    setTag(a, "PlanarConfiguration", 1);
%!    setTag(a, "PhotometricInterpretation", 2);
%!    data = uint16 (randi (intmax ("uint16"), 20, 20, 3));
%!    writeEncodedStrip (a, 1, data);
%!    a.close ();
%!    a = Tiff (filename, "r");
%!    data2 = a.read ();
%!    assert (size (data2), [20, 20, 3]);
%!    assert (data2, data);
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test 32-bit RGB image
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 20, 20, 3, 32);
%!    setTag(a, "PlanarConfiguration", 1);
%!    setTag(a, "PhotometricInterpretation", 2);
%!    data = uint32 (randi (intmax ("uint32"), 20, 20, 3));
%!    writeEncodedStrip (a, 1, data);
%!    a.close ();
%!    a = Tiff (filename, "r");
%!    data2 = a.read ();
%!    assert (size (data2), [20, 20, 3]);
%!    assert (data2, data);
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect

## test failure data-type and bit-depth mismatch
%!testif HAVE_TIFF
%!  filename = [tempname() ".tif"];
%!  unwind_protect
%!    a = Tiff (filename, "w");
%!    set_configs (a, 20, 20, 3, 16);
%!    setTag(a, "PlanarConfiguration", 1);
%!    setTag(a, "PhotometricInterpretation", 2);
%!    data = uint8 (randi (intmax ("uint8"), 20, 20, 3));
%!    fail ("writeEncodedStrip (a, 1, data)", "Only uint16 data is allowed for uint images with bit depth of 16");
%!    a.close ();
%!  unwind_protect_cleanup
%!    unlink (filename);
%!  end_unwind_protect
