//
//  Created by Boyd Multerer on 2021-04-19
//  Copyright © 2021 Kry10 Limited. All rights reserved.
//

#include <string.h>
#include <erl_nif.h>

//=============================================================================
// utilities

//=============================================================================
// Erlang NIF stuff from here down.

//-----------------------------------------------------------------------------
static ERL_NIF_TERM
nif_put_g(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ErlNifBinary  pixels;
  unsigned int  pos;
  unsigned int  g;

  // get the parameters
  if ( !enif_inspect_binary(env, argv[0], &pixels) )    {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[1], &pos) )       {return enif_make_badarg(env);}
  if ( pos >= pixels.size ) {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[2], &g) )      {return enif_make_badarg(env);}

  // put the value
  pixels.data[pos] = g;

  return enif_make_atom(env, "ok");;
}

//-----------------------------------------------------------------------------
static ERL_NIF_TERM
nif_put_ga(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ErlNifBinary  pixels;
  unsigned int  pos;
  unsigned int  g;
  unsigned int  a;

  // get the parameters
  if ( !enif_inspect_binary(env, argv[0], &pixels) )    {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[1], &pos) )       {return enif_make_badarg(env);}
  if ( pos >= (pixels.size - 1) ) {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[2], &g) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[3], &a) )      {return enif_make_badarg(env);}

  // put the value
  pos *= 2;
  pixels.data[pos] = g;
  pixels.data[pos + 1] = a;

  return enif_make_atom(env, "ok");;
}

//-----------------------------------------------------------------------------
static ERL_NIF_TERM
nif_put_rgb(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ErlNifBinary  pixels;
  unsigned int  pos;
  unsigned int  r;
  unsigned int  g;
  unsigned int  b;

  // get the parameters
  if ( !enif_inspect_binary(env, argv[0], &pixels) )    {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[1], &pos) )       {return enif_make_badarg(env);}
  if ( pos >= (pixels.size - 2) ) {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[2], &r) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[3], &g) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[4], &b) )      {return enif_make_badarg(env);}

  // put the value
  pos *= 3;
  pixels.data[pos] = r;
  pixels.data[pos + 1] = g;
  pixels.data[pos + 2] = b;

  return enif_make_atom(env, "ok");;
}

//-----------------------------------------------------------------------------
static ERL_NIF_TERM
nif_put_rgba(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ErlNifBinary  pixels;
  unsigned int  pos;
  unsigned int  r;
  unsigned int  g;
  unsigned int  b;
  unsigned int  a;

  // get the parameters
  if ( !enif_inspect_binary(env, argv[0], &pixels) )    {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[1], &pos) )       {return enif_make_badarg(env);}
  if ( pos >= (pixels.size - 3) ) {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[2], &r) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[3], &g) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[4], &b) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[5], &a) )      {return enif_make_badarg(env);}

  // put the value
  pos *= 4;
  pixels.data[pos] = r;
  pixels.data[pos + 1] = g;
  pixels.data[pos + 2] = b;
  pixels.data[pos + 3] = a;

  return enif_make_atom(env, "ok");;
}




//-----------------------------------------------------------------------------
static ERL_NIF_TERM
nif_clear_g(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ErlNifBinary  pixels;
  unsigned int  g;

  // get the parameters
  if ( !enif_inspect_binary(env, argv[0], &pixels) )    {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[1], &g) )      {return enif_make_badarg(env);}

  // clear the pixels
  memset(pixels.data, g, pixels.size);

  return enif_make_binary( env, &pixels );
}

//-----------------------------------------------------------------------------
static ERL_NIF_TERM
nif_clear_ga(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ErlNifBinary  pixels;
  unsigned int  size;
  unsigned int  g;
  unsigned int  a;

  // get the parameters
  if ( !enif_inspect_binary(env, argv[0], &pixels) )    {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[1], &g) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[2], &a) )      {return enif_make_badarg(env);}  

  // clear the pixels
  size = pixels.size;
  for( unsigned int i = 0; i < size; i += 2) {
    pixels.data[i] = g;
    pixels.data[i+1] = a;
  }

  return enif_make_binary( env, &pixels );
}

//-----------------------------------------------------------------------------
static ERL_NIF_TERM
nif_clear_rgb(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ErlNifBinary  pixels;
  unsigned int  size;
  unsigned int  r;
  unsigned int  g;
  unsigned int  b;

  // get the parameters
  if ( !enif_inspect_binary(env, argv[0], &pixels) )    {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[1], &r) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[2], &g) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[3], &b) )      {return enif_make_badarg(env);}

  // clear the pixels
  size = pixels.size;
  for( unsigned int i = 0; i < size; i += 3) {
    pixels.data[i] = r;
    pixels.data[i+1] = g;
    pixels.data[i+2] = b;
  }

  return enif_make_binary( env, &pixels );
}

//-----------------------------------------------------------------------------
static ERL_NIF_TERM
nif_clear_rgba(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ErlNifBinary  pixels;
  unsigned int  size;
  unsigned int  r;
  unsigned int  g;
  unsigned int  b;
  unsigned int  a;

  // get the parameters
  if ( !enif_inspect_binary(env, argv[0], &pixels) )    {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[1], &r) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[2], &g) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[3], &b) )      {return enif_make_badarg(env);}
  if ( !enif_get_uint(env, argv[4], &a) )      {return enif_make_badarg(env);}

  // clear the pixels
  size = pixels.size;
  for( unsigned int i = 0; i < size; i += 4) {
    pixels.data[i] = r;
    pixels.data[i+1] = g;
    pixels.data[i+2] = b;
    pixels.data[i+3] = a;
  }

  return enif_make_binary( env, &pixels );
}


//=============================================================================
// erl housekeeping. This is the list of functions available to the erl side

static ErlNifFunc nif_funcs[] = {
  // {erl_function_name, erl_function_arity, c_function}
  {"nif_put",             3, nif_put_g,         0},
  {"nif_put",             4, nif_put_ga,        0},
  {"nif_put",             5, nif_put_rgb,       0},
  {"nif_put",             6, nif_put_rgba,      0},
  {"nif_clear",           2, nif_clear_g,       0},
  {"nif_clear",           3, nif_clear_ga,      0},
  {"nif_clear",           4, nif_clear_rgb,     0},
  {"nif_clear",           5, nif_clear_rgba,    0},
};

ERL_NIF_INIT(Elixir.Scenic.Assets.Stream.Bitmap, nif_funcs, NULL, NULL, NULL, NULL)