/*
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "applib/graphics/framebuffer.h"
#include "applib/graphics/graphics.h"
#include "resource/resource.h"
#include "resource/resource_ids.auto.h"
#include "util/graphics.h"
#include "util/size.h"

#include "clar.h"

#include <stdio.h>

// Fakes
/////////////////////

#include "fixtures/load_test_resources.h"

// Stubs
/////////////////////

#include "stubs_analytics.h"
#include "stubs_app_state.h"
#include "stubs_bootbits.h"
#include "stubs_heap.h"
#include "stubs_logging.h"
#include "stubs_memory_layout.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_pebble_tasks.h"
#include "stubs_print.h"
#include "stubs_prompt.h"
#include "stubs_serial.h"
#include "stubs_sleep.h"
#include "stubs_syscalls.h"
#include "stubs_task_watchdog.h"
#include "stubs_ui_window.h"
#include "stubs_unobstructed_area.h"

// Helper Functions
/////////////////////
#include "../graphics/test_graphics.h"
#include "../graphics/util.h"

// Setup and Teardown
////////////////////////////////////

static FrameBuffer *fb;
static GContext s_ctx;
static FontInfo s_font_info;

static GBitmap *s_dest_bitmap;

void test_emoji_fonts__initialize(void) {
  fake_spi_flash_init(0, 0x1000000);
  pfs_init(false);
  pfs_format(true /* write erase headers */);
  load_resource_fixture_in_flash(RESOURCES_FIXTURE_PATH, SYSTEM_RESOURCES_FIXTURE_NAME,
                                 false /* is_next */);

  memset(&s_font_info, 0, sizeof(s_font_info));

  resource_init();

  fb = malloc(sizeof(FrameBuffer));
  framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});

  test_graphics_context_init(&s_ctx, fb);
  framebuffer_clear(fb);
}

void test_emoji_fonts__cleanup(void) {
  free(fb);
  fb = NULL;

  gbitmap_destroy(s_dest_bitmap);
  s_dest_bitmap = NULL;
}

// Helpers
//////////////////////

static char s_emoji_string[] =
  "😄😃😀😊☺😉😍😘😚😗😙😜😝😛😳😁😔😌😒😞😣😢😂😭😥😪😰😅😓😩😫😨😱"
  "😠😡😤😖😆😋😷😎😴😵😲😟😧😈👿😮😬😐😕😯😶😇😏😑😺😸😻😽😼🙀😿😹😾💩"
  "👍👎👌👊✊✌👋✋👐👆👇👉👈🙌🙏☝👏💛💙💜💚❤💔💗💓💕💖💞💘💋🐥🎉💩🍻🍺"
  "💪🔥🐵🙈→►★🎤🎥📷🎵🎁";

static void prv_render_text(GContext *ctx, const char *text, GFont const font, const GRect *box,
                            const GTextOverflowMode overflow_mode, const GTextAlignment alignment,
                            GTextLayoutCacheRef layout, bool render) {
  if (render) {
    graphics_draw_text(ctx, text, font, *box, overflow_mode, alignment, layout);
  } else {
    graphics_text_layout_get_max_used_size(ctx, text, font, *box, overflow_mode, alignment,
                                           layout);
  }
}

static GSize prv_draw_emoji(GContext *ctx, const GRect *bounds, GFont font, bool render) {
  TextLayoutExtended layout = {
    .line_spacing_delta = 2, // Give some space for the larger emojis
  };
  graphics_context_set_text_color(ctx, GColorBlack);
  prv_render_text(ctx, s_emoji_string, font, bounds, GTextOverflowModeWordWrap,
                  GTextAlignmentLeft, (GTextLayoutCacheRef)&layout, true /* render */);
  return layout.max_used_size;
}

void prv_prepare_canvas(GSize bitmap_size, GColor background_color) {
  gbitmap_destroy(s_dest_bitmap);

  s_dest_bitmap = gbitmap_create_blank(bitmap_size, GBitmapFormat8Bit);

  s_ctx.dest_bitmap = *s_dest_bitmap;
  s_ctx.draw_state.clip_box.size = bitmap_size;
  s_ctx.draw_state.drawing_box.size = bitmap_size;

  memset(s_dest_bitmap->addr, background_color.argb,
         s_dest_bitmap->row_size_bytes * s_dest_bitmap->bounds.size.h);
}

void prv_prepare_canvas_and_render_emoji(ResourceId font_handle) {
  // Calculate canvas size necessary
  cl_assert(text_resources_init_font(0, font_handle, 0, &s_font_info));

  const GPoint margin = { 10, 10 }; // Canvas margins
  const int max_width = 300; // Canvas width, choose any visually pleasing width
  const int max_height = 2000; // Large size protected from overflow and automatically truncated
  const GSize max_size = { max_width, max_height };

  // FIXME: PBL-34261 graphics_text_layout_get_max_used_size reports a max of 192px in a unit test
  // with the default aplite framebuffer size. Work around this issue by creating a larger canvas.
  prv_prepare_canvas(max_size, GColorWhite);

  const GRect max_draw_frame = grect_inset_internal((GRect){ .size = max_size },
                                                    margin.x, margin.y);
  const GSize used_size = prv_draw_emoji(&s_ctx, &max_draw_frame, &s_font_info,
                                         false /* render */);

  // Resize the canvas to the used size.
  const GSize canvas_size = { max_width, used_size.h + 2 * margin.y };
  prv_prepare_canvas(canvas_size, GColorWhite);

  // Create the new canvas
  const GRect draw_frame = grect_inset_internal((GRect){ .size = canvas_size },
                                                margin.x, margin.y);
  prv_draw_emoji(&s_ctx, &draw_frame, &s_font_info, true /* render */);
}

// Tests
//////////////////////

void test_emoji_fonts__gothic_14_emoji(void) {
  prv_prepare_canvas_and_render_emoji(RESOURCE_ID_GOTHIC_14_EMOJI);
  cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
}

void test_emoji_fonts__gothic_18_emoji(void) {
  prv_prepare_canvas_and_render_emoji(RESOURCE_ID_GOTHIC_18_EMOJI);
  cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
}

void test_emoji_fonts__gothic_24_emoji(void) {
  prv_prepare_canvas_and_render_emoji(RESOURCE_ID_GOTHIC_24_EMOJI);
  cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
}

void test_emoji_fonts__gothic_28_emoji(void) {
  prv_prepare_canvas_and_render_emoji(RESOURCE_ID_GOTHIC_28_EMOJI);
  cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
}