//
//  Lynkeos
//  $Id:$
//
//  Created by Jean-Etienne LAMIAUD on Mon Sep 11 2023.
//  Copyright (c) 2023. Jean-Etienne LAMIAUD
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//

//
//  Created by Jean-Etienne LAMIAUD on 11/09/2023.
//
#include <libraw/libraw.h>

#import <XCTest/XCTest.h>

#include "LibRaw_Reader.h"
#include "MyPluginsController.h"
#include "BayerImageBuffer.h"

extern BOOL pluginsInitialized;

// Stub libraw
#define XMARGIN 12
#define YMARGIN 10
#define IMAGE_WIDTH 96
#define SENSOR_WIDTH (XMARGIN+IMAGE_WIDTH)
#define IMAGE_HEIGHT 64
#define SENSOR_HEIGHT (YMARGIN+IMAGE_HEIGHT)
#define IMAGE_ROW_LENGTH 112

static libraw_data_t libraw_data;
static ushort image_data[(XMARGIN+IMAGE_WIDTH)*(YMARGIN+IMAGE_HEIGHT)];
static const int bayerMatrix [] = {0, 1, 3, 2};

libraw_data_t *libraw_init(unsigned int flags) {return &libraw_data;}
int libraw_open_file(libraw_data_t *data, const char *path)
{
   memset(&libraw_data, 0, sizeof(libraw_data));
   data->sizes.left_margin = XMARGIN;
   data->sizes.top_margin = YMARGIN;
   data->sizes.iwidth = SENSOR_WIDTH;
   data->sizes.iheight = SENSOR_HEIGHT;
   data->sizes.raw_pitch = IMAGE_ROW_LENGTH*sizeof(ushort);
   data->color.maximum = 16384;
   data->rawdata.raw_image = image_data;

   for (int y = 0; y < SENSOR_HEIGHT; y++)
   {
      for (int x = 0; x < SENSOR_WIDTH; x++)
      {
         if (x < XMARGIN || y < YMARGIN)
            image_data[x+IMAGE_ROW_LENGTH*y] = 16383;
         else
            image_data[x+IMAGE_ROW_LENGTH*y] = (x - XMARGIN)*64 + (y - YMARGIN);
      }
   }

   return 0;
}
int libraw_unpack(libraw_data_t *data) {return 0;}
void libraw_close(libraw_data_t *data) {}
int libraw_COLOR(libraw_data_t *data, int row, int col) {return bayerMatrix[(row%2)*2 + (col%2)];}

@interface LibRaw_ReaderTest : XCTestCase

@end

@implementation LibRaw_ReaderTest

+ (void) initialize
{
   // Create the plugins controller singleton, and initialize it
   // Only if not already done by another test class
   if ( !pluginsInitialized )
   {
      [[[MyPluginsController alloc] init] awakeFromNib];
      pluginsInitialized = YES;
   }
}

- (void)setUp
{
   self.executionTimeAllowance = 10.0;
}

- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
}

- (void)testExtractNoTransform
{
   NSPoint off[] = {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}};
   LibRaw_Reader *reader = [[[LibRaw_Reader alloc] initWithURL:[NSURL URLWithString:@"file:///toto.CR2"]] autorelease];
   BayerImageBuffer *buf = (BayerImageBuffer*)[reader getCustomImageSampleAtX:40 Y:24 W:16 H:16
                                                                withTransform:[[NSAffineTransform transform] transformStruct]
                                                                  withOffsets:off];

   XCTAssertTrue([buf isMemberOfClass:[BayerImageBuffer class]], @"Custom RAW buffer is not in bayer format");
   XCTAssertEqual(buf->_w, 16);
   XCTAssertEqual(buf->_h, 16);
   XCTAssertEqual(buf->_nPlanes, 3);
   for (u_short y = 0; y < 16; y++)
   {
      for (u_short x = 0; x < 16; x++)
      {
         for (u_short c = 0; c < 3; c++)
         {
            u_short bc = libraw_COLOR(NULL, y+24+YMARGIN, x+40+XMARGIN);
            if ( (bc == 3 ? 1 : bc) == c)
            {
               XCTAssertEqualWithAccuracy(stdColorValue(buf, x, y, c), (REAL)((x + 40)*64 + (y + 24))/64.0, 1.0e-5,
                                          @"Wrong value at (%d, %d) color %d", x, y, c);
               XCTAssertEqual(stdColorValue(buf->_weight, x, y, c), (REAL)1.0,
                              @"Null bayer weight matrix at (%d, %d) color %d", x, y, c);
            }
            else
            {
               XCTAssertEqual(stdColorValue(buf, x, y, c), (REAL)0.0,
                              @"Non null value outside of bayer matrix at (%d, %d) color %d", x, y, c);
               XCTAssertEqual(stdColorValue(buf->_weight, x, y, c), (REAL)0.0,
                              @"Non null bayer weight matrix at (%d, %d) color %d", x, y, c);
            }
         }
      }
   }
}

#if 0 // Test is not in working order
- (void)testExtractRotationTranslation
{
   NSPoint off[] = {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}};
   NSAffineTransform *tr = [NSAffineTransform transform];
   [tr rotateByDegrees:45.0];
   [tr translateXBy:48.0*0.293 + 32.0*0.707 yBy:-48.0*0.707 + 32.0*0.293];
   LibRaw_Reader *reader = [[[LibRaw_Reader alloc] initWithURL:[NSURL URLWithString:@"file:///toto.CR2"]] autorelease];
   BayerImageBuffer *buf = (BayerImageBuffer*)[reader getCustomImageSampleAtX:40 Y:24 W:16 H:16
                                                                withTransform:[tr transformStruct]
                                                                  withOffsets:off];
   
   XCTAssertTrue([buf isMemberOfClass:[BayerImageBuffer class]], @"Custom RAW buffer is not in bayer format");
   XCTAssertEqual(buf->_w, 16);
   XCTAssertEqual(buf->_h, 16);
   XCTAssertEqual(buf->_nPlanes, 3);
   [tr invert];
   for (u_short y = 0; y < 16; y++)
   {
      for (u_short x = 0; x < 16; x++)
      {
         for (u_short c = 0; c < 3; c++)
         {
            CGPoint src = [tr transformPoint:NSMakePoint(x+24, y+40)];

            u_short bc = libraw_COLOR(NULL, round(src.y+YMARGIN), round(src.x+XMARGIN));
            if ( (bc == 3 ? 1 : bc) == c)
            {
               XCTAssertNotEqual(stdColorValue(buf->_weight, x, y, c), 0.0, @"Null weight at (%d, %d) color %d", x, y, c);
               XCTAssertEqualWithAccuracy(stdColorValue(buf, x, y, c)/stdColorValue(buf->_weight, x, y, c),
                                          (REAL)((src.x + 40)*64 + (src.y + 24))/64.0, 1.0e-2,
                                          @"Wrong value at (%d, %d) color %d", x, y, c);
            }
         }
      }
   }
}
#endif

//- (void)testPerformanceExample {
//    // This is an example of a performance test case.
//    [self measureBlock:^{
//        // Put the code you want to measure the time of here.
//    }];
//}

@end
