#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <stdlib.h>
#include <stdio.h>
#include "registry.h"
#include "sprite.h"
#include "string.h"
#include "heap.h"
#include "error.h"
#include "util.h"

SDL_Window *window;
SDL_Renderer *renderer;

ErrorContext *test_spritesheet_initialize(void)
{
  PREPARE_ERROR(errctx);
  spritesheet *sheet = NULL;
  SDL_Texture *image = NULL;
  string *tmpstr = NULL;
  // Does the image file get loaded?
  // Is the image file loaded correctly? (Surface comparison)
  // Is the spritesheet in the registry?
  ATTEMPT {
      CATCH(errctx, heap_next_spritesheet(&sheet));
      CATCH(errctx, heap_next_string(&tmpstr));
      
      snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
      CATCH(errctx, spritesheet_initialize(sheet, 48, 48, "assets/spritesheet.png"));
      FAIL_ZERO_BREAK(errctx, sheet->texture, ERR_VALUE, "spritesheet_initialize failed to load the sprite texture");
      FAIL_NONZERO_BREAK(
	  errctx,
	  ((sheet->texture->w != 576) || (sheet->texture->h != 384)),
	  ERR_VALUE,
	  "Loaded texture was not the correct size");
      
      snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
      image = IMG_LoadTexture(renderer, (char *)&tmpstr->data);
      FAIL_ZERO_BREAK(errctx, image, ERR_SDL, "Failed to load comparison image");

      CATCH(
	  errctx,
	  render_and_compare(
	      sheet->texture,
	      image,
	      0, 0, 576, 384,
	      "test_spritesheet_loaded_image.png")
	  );

      FAIL_ZERO_BREAK(
	  errctx,
	  SDL_GetPointerProperty(REGISTRY_SPRITESHEET, "assets/spritesheet.png", NULL),
	  ERR_KEY,
	  "Spritesheet was not placed in the registry");
      
  } CLEANUP {
      IGNORE(heap_release_string(tmpstr));
      IGNORE(heap_release_spritesheet(sheet));
      if ( image != NULL )
	  SDL_DestroyTexture(image);
  } PROCESS(errctx) {
  } FINISH(errctx, true);
  SUCCEED_RETURN(errctx);
}

ErrorContext *test_sprite_initialize(void)
{
    PREPARE_ERROR(errctx);
    spritesheet *testsheet = NULL;
    sprite *testsprite = NULL;
    string *tmpstr = NULL;
    
    // Does the sprite get loaded?
    // Do all frames of the sprite get loaded?
    // Are all the frames of the sprite what we expect? (Surface comparison)
    // Is the sprite added to the registry?
    ATTEMPT {
      CATCH(errctx, heap_next_spritesheet(&testsheet));
      CATCH(errctx, heap_next_sprite(&testsprite));
      CATCH(errctx, heap_next_string(&tmpstr));
      
      snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
      CATCH(errctx, spritesheet_initialize(testsheet, 48, 48, "assets/spritesheet.png"));
      FAIL_ZERO_BREAK(errctx, testsheet, ERR_VALUE, "spritesheet_initialize failed");

      CATCH(errctx, sprite_initialize(testsprite, "test", testsheet));
      FAIL_NONZERO_BREAK(errctx, (testsprite->sheet != testsheet), ERR_VALUE, "Initialized sprite uses wrong sheet");
      FAIL_ZERO_BREAK(
	  errctx,
	  SDL_GetPointerProperty(REGISTRY_SPRITE, "test", NULL),
	  ERR_KEY,
	  "Sprite was not placed in the registry");
      
    } CLEANUP {
	IGNORE(heap_release_sprite(testsprite));
	IGNORE(heap_release_string(tmpstr));
    } PROCESS(errctx) {
    } FINISH(errctx, true);
    SUCCEED_RETURN(errctx);
}

ErrorContext *test_sprite_load_json(void)
{
    PREPARE_ERROR(errctx);
    sprite *testsprite = NULL;
    sprite *testsprite2 = NULL;
    string *tmpstr = NULL;
    SDL_Texture *image = NULL;
    
    // Does the sprite get loaded?
    // Do all frames of the sprite get loaded?
    // Are all the frames of the sprite what we expect? (Surface comparison)
    // Is the sprite added to the registry?
    ATTEMPT {
      CATCH(errctx, heap_next_string(&tmpstr));
      
      CATCH(errctx, sprite_load_json("assets/testsprite.json"));
      testsprite = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite", NULL);
      FAIL_ZERO_BREAK(
	  errctx,
	  testsprite,
	  ERR_KEY,
	  "sprite_load_json succeeds but sprite is not placed in the registry"); 

      FAIL_ZERO_BREAK(errctx, (testsprite->width == 48), ERR_VALUE, "width incorrect (48 : %d)", testsprite->width);
      FAIL_ZERO_BREAK(errctx, (testsprite->height == 48), ERR_VALUE, "height incorrect (48 : %d)", testsprite->height);
      FAIL_ZERO_BREAK(errctx, (testsprite->speed == 100), ERR_VALUE, "speed incorrect (100 : %d)", testsprite->speed);
      FAIL_ZERO_BREAK(errctx, (testsprite->loop == true), ERR_VALUE, "loop incorrect (1 : %d)", testsprite->loop);
      FAIL_ZERO_BREAK(errctx, (testsprite->loopReverse == true), ERR_VALUE, "loopReverse incorrect (1 : %d)", testsprite->loopReverse);
      FAIL_ZERO_BREAK(errctx, (testsprite->frames == 3), ERR_VALUE, "frame count incorrect (3 : %d)", testsprite->frames);
      FAIL_ZERO_BREAK(errctx, (testsprite->frameids[0] == 12), ERR_VALUE, "frameids[0] incorrect (12 : %d)", testsprite->frameids[0]);
      FAIL_ZERO_BREAK(errctx, (testsprite->frameids[1] == 13), ERR_VALUE, "frameids[1] incorrect (13 : %d)", testsprite->frameids[1]);
      FAIL_ZERO_BREAK(errctx, (testsprite->frameids[2] == 14), ERR_VALUE, "frameids[2] incorrect (14 : %d)", testsprite->frameids[2]);
      FAIL_NONZERO_BREAK(errctx, strcmp(testsprite->name, "testsprite"), ERR_VALUE, "name incorrect (testsprite : %s)", (char *)testsprite->name);

      // Is it using the right spritesheet?
      
      snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
      image = IMG_LoadTexture(renderer, (char *)&tmpstr->data);
      FAIL_ZERO_BREAK(errctx, image, ERR_SDL, "Failed to load comparison image");

      CATCH(
	  errctx,
	  render_and_compare(
	      testsprite->sheet->texture,
	      image,
	      0, 0, 576, 384,
	      "test_sprite_loaded_from_json_sheet.png"
	      )
	  );

      // If we load a second sprite using the same sheet name, do they use the same sheet in memory?
      snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testsprite2.json");
      CATCH(errctx, sprite_load_json("assets/testsprite2.json"));
      testsprite2 = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite2", NULL);
      FAIL_ZERO_BREAK(
	  errctx,
	  testsprite,
	  ERR_KEY,
	  "sprite_load_json succeeds but second sprite is not placed in the registry"); 

      FAIL_ZERO_BREAK(
	  errctx,
	  (testsprite->sheet == testsprite2->sheet),
	  ERR_VALUE,
	  "Previously loaded spritesheets are not reused");
      
    } CLEANUP {
	if ( testsprite != NULL ) {
	    IGNORE(heap_release_sprite(testsprite));
	}
	if ( testsprite2 != NULL ) {
	    IGNORE(heap_release_sprite(testsprite2));
	}
	IGNORE(heap_release_string(tmpstr));
	if ( image != NULL )
	    SDL_DestroyTexture(image);
    } PROCESS(errctx) {
    } FINISH(errctx, true);
    SUCCEED_RETURN(errctx);
}

int main(void)
{
    PREPARE_ERROR(errctx);
    
    ATTEMPT {
   
	SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest");
    
	if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO )) {
	    FAIL_BREAK(errctx, ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError());
	}
    
	if (!SDL_CreateWindowAndRenderer("net/aklabs/libsdl3game/test_sprite", 640, 480, 0, &window, &renderer)) {
	    FAIL_BREAK(errctx, ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError());
	}

	CATCH(errctx, heap_init());
	CATCH(errctx, registry_init_sprite());
	CATCH(errctx, registry_init_spritesheet());

	CATCH(errctx, test_spritesheet_initialize());
	CATCH(errctx, test_sprite_initialize());
	CATCH(errctx, test_sprite_load_json());
    } CLEANUP {
    } PROCESS(errctx) {
    } FINISH_NORETURN(errctx);
    return 0;
}
