#include "model.h"

#include "stb_ds.h"

#include "render.h"

#include "HandmadeMath.h"

#include "math.h"
#include "time.h"

#include <cgltf.h>

#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "jsffi.h"

SDL_GPUBuffer *texcoord_floats(float *f, int n)
{
  return NULL;
}

SDL_GPUBuffer *float_buffer(float *f, int v)
{
  return NULL;
}

SDL_GPUBuffer *index_buffer(float *f, int verts)
{
  return NULL;
}

uint32_t pack_int10_n2(float *norm)
{
  uint32_t ret = 0;
  for (int i = 0; i < 3; i++) {
    int n = (norm[i]+1.0)*511;
    ret |= (n & 0x3ff) << (10*i);
  }
  return ret;
}

// Pack an array of normals into 
SDL_GPUBuffer *normal_floats(float *f, int n)
{
  return float_buffer(f, n);
}

SDL_GPUBuffer *ubyten_buffer(float *f, int v)
{
  return NULL;
}

SDL_GPUBuffer *ubyte_buffer(float *f, int v)
{
  return NULL;
}

SDL_GPUBuffer *accessor2buffer(cgltf_accessor *a, int type)
{
  return NULL;
}

void packFloats(float *src, float *dest, int srcLength) {
  int i, j;
  for (i = 0, j = 0; i < srcLength; i += 3, j += 4) {
    dest[j] = src[i];
    dest[j + 1] = src[i + 1];
    dest[j + 2] = src[i + 2];
    dest[j + 3] = 0.0f;
  }
}

static md5joint *node2joint(skin *sk, cgltf_node *n, cgltf_skin *skin)
{
  int k = 0;
  while (skin->joints[k] != n && k < skin->joints_count) k++;
  return sk->joints+k;
}

animation *gltf_anim(cgltf_animation *anim, skin *sk, cgltf_skin *skin)
{
  animation *ret = calloc(sizeof(*ret), 1);
  animation an = *ret;
  arrsetlen(an.samplers, anim->samplers_count);
  
  for (int i = 0; i < anim->samplers_count; i++) {
    cgltf_animation_sampler s = anim->samplers[i];
    sampler samp = (sampler){0};
    int n = cgltf_accessor_unpack_floats(s.input, NULL, 0);
    arrsetlen(samp.times, n);
    cgltf_accessor_unpack_floats(s.input, samp.times, n);

    n = cgltf_accessor_unpack_floats(s.output, NULL, 0);
    int comp = cgltf_num_components(s.output->type);
    arrsetlen(samp.data, n/comp);
    if (comp == 4)
      cgltf_accessor_unpack_floats(s.output, samp.data, n);
    else {
      float *out = malloc(sizeof(*out)*n);
      cgltf_accessor_unpack_floats(s.output, out, n);
      packFloats(out, samp.data, n);
      free(out);
    }
    
    samp.type = s.interpolation;
    
    if (samp.type == LINEAR && comp == 4)
      samp.type = SLERP;
    
    an.samplers[i] = samp;
  }
    
  for (int i = 0; i < anim->channels_count; i++) {
    cgltf_animation_channel ch = anim->channels[i];
    struct anim_channel ach = (struct anim_channel){0};
    md5joint *md = node2joint(sk, ch.target_node, skin);
    switch(ch.target_path) {
      case cgltf_animation_path_type_translation:
        ach.target = &md->pos;
        break;
      case cgltf_animation_path_type_rotation:
        ach.target = &md->rot;
        break;
      case cgltf_animation_path_type_scale:
        ach.target = &md->scale;
        break;
      default: break;
    }
    ach.sampler = an.samplers+(ch.sampler-anim->samplers);
    
    arrput(an.channels, ach);
  }

  an.time = 0;

  *ret = an;
  return ret;
}

skin *make_gltf_skin(cgltf_skin *skin, cgltf_data *data)
{
  int n = cgltf_accessor_unpack_floats(skin->inverse_bind_matrices, NULL, 0);
  struct skin *sk = NULL;
  sk = calloc(sizeof(*sk),1);
  arrsetlen(sk->invbind, n/16);
  cgltf_accessor_unpack_floats(skin->inverse_bind_matrices, sk->invbind, n);
  
  arrsetlen(sk->joints, skin->joints_count);
  
  for (int i = 0; i < 50; i++)
    sk->binds[i] = MAT1;
  
  for (int i = 0; i < skin->joints_count; i++) {
    cgltf_node *n = skin->joints[i];
    md5joint *j = sk->joints+i;

    if (n == skin->skeleton)
      j->parent = NULL;
    else
      j->parent = node2joint(sk, n->parent, skin);

    for (int i = 0; i < 3; i++) {
      j->pos.e[i] = n->translation[i];
      j->scale.e[i] = n->scale[i];
    }
    for (int i = 0; i < 4; i++)
      j->rot.e[i] = n->rotation[i];
  }

  sk->anim = gltf_anim(data->animations+0, sk, skin);

  return sk;
}

void skin_calculate(skin *sk)
{
//  animation_run(sk->anim, apptime());
  for (int i = 0; i < arrlen(sk->joints); i++) {
    md5joint *md = sk->joints+i;
    md->t = HMM_M4TRS(md->pos.xyz, md->rot, md->scale.xyz);
    if (md->parent)
      md->t = HMM_MulM4(md->parent->t, md->t);

    sk->binds[i] = HMM_MulM4(md->t, sk->invbind[i]);
  }
}
