#include "img.h"
#include "mem_arena.h"
#include "misc_utils.h"
#include "obj.h"
#include "render.h"
#include "shaders.h"
#include "str.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>

#define IMAGE_DIMENSION 1200

enum {
  TINY_EXIT_SUCCESS,
  TINY_EXIT_MISSING_ARGS,
  TINY_EXIT_OBJ_NOT_EXIST,
  TINY_EXIT_ARENA_INIT_FAILED,
  TINY_EXIT_RENDER_INIT_FAILED,
  TINY_EXIT_MODEL_LOAD_FAILED,
};

typedef struct tiny_args TinyArgs;
struct tiny_args {
  Str8 obj;
  Str8 diffuse;
  Str8 nm_tangent;
};

internal TinyArgs parse_args(Arena *arena, int argc, char *argv[]);
internal i32 tinyrenderer(Arena *arena, TinyArgs args);
internal bool file_exists(const Str8 *path);

i32 main(int argc, char *argv[]) {
  Arena *arena = NULL;
  if (!wapp_mem_arena_init(&arena, GB(10))) {
    return TINY_EXIT_ARENA_INIT_FAILED;
  }

  TinyArgs args = parse_args(arena, argc, argv);
  i32 output = tinyrenderer(arena, args);

  wapp_mem_arena_destroy(&arena);

  return output;
}

internal TinyArgs parse_args(Arena *arena, int argc, char *argv[]) {
  if (argc < 2) {
    exit(TINY_EXIT_MISSING_ARGS);
  }

  TinyArgs args = {
      .obj = str8_lit(argv[1]),
  };

  if (!file_exists(&args.obj)) {
    exit(TINY_EXIT_OBJ_NOT_EXIST);
  }

  u64 substr_end = args.obj.length - 4;

  args.diffuse = str8_substr(arena, &args.obj, 0, substr_end);
  str8_concat(arena, &args.diffuse, "_diffuse.pnm");
  if (!file_exists(&args.diffuse)) {
    args.diffuse = (Str8){0};
  }

  args.nm_tangent = str8_substr(arena, &args.obj, 0, substr_end);
  str8_concat(arena, &args.nm_tangent, "_nm_tangent.pnm");
  if (!file_exists(&args.nm_tangent)) {
    args.nm_tangent = (Str8){0};
  }

  return args;
}

internal i32 tinyrenderer(Arena *arena, TinyArgs args) {
  Colour bg = {.r = 42, .g = 45, .b = 52, .a = 255};
  Colour main_colour = {.r = 14, .g = 156, .b = 208, .a = 255};
  Render render;
  if (!init_render(arena, &render, IMAGE_DIMENSION, IMAGE_DIMENSION)) {
    return TINY_EXIT_RENDER_INIT_FAILED;
  }

  Model obj = load_obj_file(
      arena, args.obj.str, args.diffuse.length > 0 ? args.diffuse.str : NULL,
      args.nm_tangent.length > 0 ? args.nm_tangent.str : NULL);
  if (IS_INVALID_MODEL(obj)) {
    return TINY_EXIT_MODEL_LOAD_FAILED;
  }

  load_shaders();

  clear_buffer(&(render.img), &bg);
  render_model(&obj, &render, perspective_diffuse, RENDER_TYPE_SHADED,
               main_colour);
  save_image(&(render.img), "result.pam");

  return TINY_EXIT_SUCCESS;
}

internal bool file_exists(const Str8 *path) {
  struct stat st;
  return stat(path->str, &st) == 0;
}
