/* Dump composited frames of an animated WebP for ffmpeg.
 * Usage: webpanim input.webp output_dir
 * Writes frame-0001.pam ... and concat.txt (ffconcat, one cycle).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "webp/decode.h"
#include "webp/demux.h"

static uint8_t *read_file(const char *path, size_t *size) {
  FILE *f = fopen(path, "rb");
  uint8_t *buf;
  long len;
  if (!f) return NULL;
  if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; }
  len = ftell(f);
  if (len < 12 || len > 80L * 1024L * 1024L) { fclose(f); return NULL; }
  if (fseek(f, 0, SEEK_SET) != 0) { fclose(f); return NULL; }
  buf = (uint8_t *)malloc((size_t)len);
  if (!buf) { fclose(f); return NULL; }
  if (fread(buf, 1, (size_t)len, f) != (size_t)len) { free(buf); fclose(f); return NULL; }
  fclose(f);
  *size = (size_t)len;
  return buf;
}

static int write_pam(const char *path, const uint8_t *rgba, int w, int h) {
  FILE *f = fopen(path, "wb");
  size_t n;
  if (!f) return 0;
  fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n", w, h);
  n = (size_t)w * (size_t)h * 4u;
  if (fwrite(rgba, 1, n, f) != n) { fclose(f); return 0; }
  fclose(f);
  return 1;
}

static int frame_duration(const WebPDemuxer *demux, int index) {
  WebPIterator iter;
  int dur = 100;
  if (WebPDemuxGetFrame(demux, index, &iter)) {
    if (iter.duration > 0) dur = iter.duration;
    WebPDemuxReleaseIterator(&iter);
  }
  return dur;
}

static int igcd(int a, int b) {
  while (b) {
    int t = a % b;
    a = b;
    b = t;
  }
  return a < 1 ? 1 : a;
}

int main(int argc, char **argv) {
  size_t size = 0;
  uint8_t *bytes;
  WebPData data;
  WebPAnimDecoderOptions opts;
  WebPAnimDecoder *dec;
  WebPAnimInfo info;
  const WebPDemuxer *demux;
  FILE *concat;
  FILE *fps_file;
  char concat_path[1024];
  char fps_path[1024];
  int durations[3000];
  int index = 0;
  int w, h;
  int tick;
  int fps;
  int i;
  int repeat;
  uint8_t *copy = NULL;

  if (argc != 3) {
    fprintf(stderr, "usage: webpanim input.webp output_dir\n");
    return 2;
  }
  bytes = read_file(argv[1], &size);
  if (!bytes) {
    fprintf(stderr, "could not read %s\n", argv[1]);
    return 1;
  }
  data.bytes = bytes;
  data.size = size;
  if (!WebPAnimDecoderOptionsInit(&opts)) {
    free(bytes);
    return 1;
  }
  opts.color_mode = MODE_RGBA;
  dec = WebPAnimDecoderNew(&data, &opts);
  if (!dec || !WebPAnimDecoderGetInfo(dec, &info) || info.frame_count < 2 || info.canvas_width < 1 || info.canvas_height < 1) {
    if (dec) WebPAnimDecoderDelete(dec);
    free(bytes);
    return 3;
  }
  demux = WebPAnimDecoderGetDemuxer(dec);
  w = (int)info.canvas_width;
  h = (int)info.canvas_height;
  if (snprintf(concat_path, sizeof(concat_path), "%s/concat.txt", argv[2]) >= (int)sizeof(concat_path)) {
    WebPAnimDecoderDelete(dec);
    free(bytes);
    return 1;
  }
  concat = fopen(concat_path, "wb");
  if (!concat) {
    WebPAnimDecoderDelete(dec);
    free(bytes);
    return 1;
  }
  while (WebPAnimDecoderHasMoreFrames(dec) && index < 3000) {
    uint8_t *buf = NULL;
    int timestamp = 0;
    char frame_path[1024];
    index++;
    if (!WebPAnimDecoderGetNext(dec, &buf, &timestamp) || !buf) break;
    if (!copy) copy = (uint8_t *)malloc((size_t)w * (size_t)h * 4u);
    if (!copy) break;
    memcpy(copy, buf, (size_t)w * (size_t)h * 4u);
    if (snprintf(frame_path, sizeof(frame_path), "%s/frame-%04d.pam", argv[2], index) >= (int)sizeof(frame_path)) break;
    if (!write_pam(frame_path, copy, w, h)) break;
    durations[index - 1] = demux ? frame_duration(demux, index) : 100;
  }
  if (index < 2) {
    fclose(concat);
    free(copy);
    WebPAnimDecoderDelete(dec);
    free(bytes);
    return 1;
  }
  tick = durations[0];
  for (i = 1; i < index; i++) tick = igcd(tick, durations[i]);
  if (tick < 10 || (1000 % tick) != 0) {
    tick = 10;
    for (i = 0; i < index; i++) {
      durations[i] = durations[i] < 15 ? 10 : ((durations[i] + 5) / 10) * 10;
    }
  }
  fps = 1000 / tick;
  fputs("ffconcat version 1.0\n", concat);
  for (i = 0; i < index; i++) {
    repeat = durations[i] / tick;
    if (repeat < 1) repeat = 1;
    while (repeat--) {
      fprintf(concat, "file '%s/frame-%04d.pam'\n", argv[2], i + 1);
    }
  }
  fclose(concat);
  if (snprintf(fps_path, sizeof(fps_path), "%s/fps.txt", argv[2]) >= (int)sizeof(fps_path)) {
    free(copy);
    WebPAnimDecoderDelete(dec);
    free(bytes);
    return 1;
  }
  fps_file = fopen(fps_path, "wb");
  if (!fps_file) {
    free(copy);
    WebPAnimDecoderDelete(dec);
    free(bytes);
    return 1;
  }
  fprintf(fps_file, "%d\n", fps);
  fclose(fps_file);
  free(copy);
  WebPAnimDecoderDelete(dec);
  free(bytes);
  return index >= 2 ? 0 : 1;
}
