#include <git2.h>
#include "git_repo.h"
#include "git_repo_log.h"

#include "../templ/duc_git_repo_log.h"

static orb_rc_t fetch(orb_module_t *self) {
  duc_git_repo_log_t *data = self->data;
  git_repo_log_param_list_t *param_list = self->param_list;
  if (!param_list) return ORB_ERR(1);
  if (!param_list->repo) return ORB_ERR(2);
  data->repo = param_list->repo;
  git_revwalk *walker = NULL;
  git_revwalk_new(&walker, param_list->repo->handle);
  git_revwalk_sorting(walker, GIT_SORT_TIME);
  int code = 0;
  if (param_list->repo->ref) {
    git_oid ref_oid;
    git_oid_fromstr(&ref_oid, param_list->repo->ref->oid);
    code = git_revwalk_push(walker, &ref_oid);
  }
  else {
    code = git_revwalk_push_head(walker);
  }
  if (code != 0) {
    git_revwalk_free(walker);
    return ORB_ERR(code);
  }
  git_oid oid;
  while (git_revwalk_next(&oid, walker) == 0) {
    git_commit *commit_handle = NULL;
    git_commit_lookup(&commit_handle, param_list->repo->handle, &oid);
    data->commit_list = realloc(data->commit_list, sizeof(git_repo_commit_t) * (data->commit_list_len + 1));
    git_repo_commit_t *commit = &data->commit_list[data->commit_list_len];
    memset(commit, 0, sizeof(git_repo_commit_t));
    commit->oid = malloc(sizeof(char) * (GIT_OID_HEXSZ + 1));
    git_oid_tostr(commit->oid, GIT_OID_HEXSZ + 1, &oid);
    commit->message = strdup(git_commit_message(commit_handle));
    commit->summary = strdup(git_commit_summary(commit_handle));
    const char *body = git_commit_body(commit_handle);
    if (body) commit->body = strdup(body);
    commit->author_name = strdup(git_commit_author(commit_handle)->name);
    commit->author_email = strdup(git_commit_author(commit_handle)->email);
    commit->time = (time_t) git_commit_time(commit_handle);
    git_commit_free(commit_handle);
    data->commit_list_len++;
  }
  git_revwalk_free(walker);
  return ORB_OK;
}

static orb_rc_t render(orb_module_t *self, void *out) {
  return ORB_ERR(duc_git_repo_log_render(out, (duc_git_repo_log_t *) self->data));
}

static void cleanup(orb_module_t *self) {
  duc_git_repo_log_t *data = self->data;
  if (data->commit_list && data->commit_list_len > 0) {
    for (size_t i = 0; i < data->commit_list_len; i++) {
      git_repo_commit_t *commit = &data->commit_list[i];
      free(commit->oid);
      free(commit->message);
      free(commit->summary);
      if (commit->body) free(commit->body);
      free(commit->author_name);
      free(commit->author_email);
    }
    free(data->commit_list);
    data->commit_list = NULL;
    data->commit_list_len = 0;
  }
}

const orb_module_type_t GIT_REPO_LOG_TYPE = {
  .size = sizeof(duc_git_repo_log_t),
  .fetch = fetch,
  .render = render,
  .cleanup = cleanup,
};
