/*
 * 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.
 */

#pragma once

#include "applib/graphics/gtypes.h"

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>

#define PATH_STRING_LENGTH 512

extern GBitmapDataRowInfo prv_gbitmap_get_data_row_info(const GBitmap *bitmap, uint16_t y);

bool write_gbitmap_to_pbi(GBitmap *bmp, const char *filepath, const char *pbi2png_path) {
  char pbi_path[PATH_STRING_LENGTH];
  char png_path[PATH_STRING_LENGTH];
  char *ext = NULL;

  strncpy(pbi_path, filepath, sizeof(pbi_path));
  ext = strrchr(pbi_path, '.');
  strncpy(ext + 1, "pbi", 3);

  strncpy(png_path, filepath, sizeof(png_path));
  ext = strrchr(png_path, '.');
  strncpy(ext + 1, "png", 3);

  FILE *file = fopen(pbi_path, "w");
  if (!file) {
    printf("Unable to open file: %s\n", pbi_path);
    return false;
  }
  // Just in case this output bitmap was created by hand.
  bmp->info.version = GBITMAP_VERSION_CURRENT;

  // PBL-24228 Support Circular PBIs
  uint16_t info_flags = bmp->info_flags;
#ifdef PLATFORM_SPALDING
  if(bmp->info.format == GBitmapFormat8BitCircular) {
    // Have to force output format to 8Bit;
    ((BitmapInfo*)&info_flags)->format = GBitmapFormat8Bit;
  }
#endif

  // use entire bounds to include entire image
  GRect entire_bounds = GRect(0, 0, 
                              bmp->bounds.origin.x + bmp->bounds.size.w, 
                              bmp->bounds.origin.y + bmp->bounds.size.h);

  fwrite(&bmp->row_size_bytes, sizeof(bmp->row_size_bytes), 1, file);
  fwrite(&info_flags, 2, 1, file);
  fwrite(&entire_bounds, sizeof(GRect), 1, file);

#ifdef PLATFORM_SPALDING
  if(bmp->info.format == GBitmapFormat8BitCircular) {
    for (int y = 0; y < entire_bounds.size.h; ++y) {
      // 8-Bit circular buffer is centered in padded rows, so just grab row and write DISP_COLS
      const GBitmapDataRowInfo dest_row_info = prv_gbitmap_get_data_row_info(bmp, y);
      uint8_t *bmp_row = dest_row_info.data;
      int x = 0;
      const uint8_t blank = 0;
      // PBL-24229 missing mask: data outside of circle is garbage from previous and next rows
      // Pad with blanks before min_x
      while (x < dest_row_info.min_x) {
        fwrite(&blank, 1, 1, file);
        x++;
      }
      if (x <= dest_row_info.max_x) {
        int length = dest_row_info.max_x - x + 1;
        fwrite(&bmp_row[dest_row_info.min_x], 1, length, file);
        x += length;
      }
      // Pad with blanks after max_x
      while (x < entire_bounds.size.w) {
        fwrite(&blank, 1, 1, file);
        x++;
      }
    }
  } else {
#endif
    size_t data_size = bmp->row_size_bytes * (entire_bounds.size.h);
    fwrite(bmp->addr, 1, data_size, file);
#ifdef PLATFORM_SPALDING
  }
#endif

  uint8_t palette_size = gbitmap_get_palette_size(gbitmap_get_format(bmp));
  if (palette_size > 0) {
    fwrite(bmp->palette, 1, palette_size * sizeof(*bmp->palette), file);
  }
  fclose(file);
  printf("PBI file written to: %s\n", pbi_path);

  int pid = fork();
  if (pid == 0) {
    char *args[] = {
      "python",
      (char *)pbi2png_path,
      (char *)pbi_path,
      (char *)png_path,
      NULL,
    };
    execvp("python", args);

    perror("execv"); // Error internal to execve
    exit(EXIT_FAILURE);
  }

  int status;
  if (wait(&status) >= 0 && !WIFEXITED(status)) {
    printf("FAILURE: pbi2png.py process exited with %d status. PNG file not written\n",
           WEXITSTATUS(status));
  } else {
    printf("PNG file written to: %s\n", png_path);
  }
  return true;
}