////////////////////////////////////////////////
////////////////////////////////////////////////
//////////////   BIG FUNCTIONS   ///////////////
////////////////////////////////////////////////
////////////////////////////////////////////////

#if defined(MR4TH_BUFFER_OPERATORS_BIG_FUNCTIONS_IMPL) && MR4TH_BUFFER_OPERATORS_BIG_FUNCTIONS_IMPL

// construct

MR4TH_SYMBOL BOP_Data*
bop_alloc(Arena *arena, U64 el_size, U64 el_count){
  BOP_Data *data = push_array(arena, BOP_Data, 1);
  data->data = push_array(arena, U8, el_size*el_count);
  data->el_size = el_size;
  data->el_count = el_count;
  return(data);
}

MR4TH_SYMBOL BOP_Data*
bop_ptr(Arena *arena, U64 el_size, U64 el_count, void *ptr){
  BOP_Data *data = push_array(arena, BOP_Data, 1);
  data->data = (U8*)ptr;
  data->el_size = el_size;
  data->el_count = el_count;
  return(data);
}

MR4TH_SYMBOL BOP_Data*
bop_subrange(Arena *arena, BOP_Data *data, U64 el_first, U64 el_count){
  U64 el_opl_clamped = ClampTop(el_first + el_count, data->el_count);
  U64 el_first_clamped = ClampTop(el_first, el_opl_clamped);
  
  BOP_Data *subdata = push_array(arena, BOP_Data, 1);
  subdata->data = data->data + data->el_size*el_first_clamped;
  subdata->el_size = data->el_size;
  subdata->el_count = el_opl_clamped - el_first_clamped;
  
  return(subdata);
}

// interleave

MR4TH_SYMBOL void
bop_interleave(BOP_Data *dst, BOP_Data **src, U64 lane_count){
  // TODO(allen): look at disassembly for real speed work
  
  Assert(lane_count > 0);
  U64 el_size = src[0]->el_size;
  U64 el_count = src[0]->el_count;
  
  Assert(dst->el_count == el_count*lane_count);
  Assert(dst->el_size == el_size);
  
  // heavy asserts (TODO heavy asserts system)
#if 0
  for (U64 i = 0; i < lane_count; i += 1){
    Assert(src[i]->el_size == el_size);
    Assert(src[i]->el_count == el_count);
  }
#endif
  
  U8 *wptr = dst->data;
  U64 roff = 0;
  for (U64 i = 0; i < el_count; i += 1, roff += el_size){
    for (U64 j = 0; j < lane_count; j += 1){
      MemoryCopy(wptr, src[j]->data + roff, el_size);
      wptr += el_size;
    }
  }
}

MR4TH_SYMBOL void
bop_uninterleave(BOP_Data **dst, BOP_Data *src, U64 lane_count){
  // TODO(allen): look at disassembly for real speed work
  
  Assert(lane_count > 0);
  U64 el_size = dst[0]->el_size;
  U64 el_count = dst[0]->el_count;
  
  Assert(src->el_count == el_count*lane_count);
  Assert(src->el_size == el_size);
  
  // heavy asserts (TODO heavy asserts system)
#if 0
  for (U64 i = 0; i < lane_count; i += 1){
    Assert(dst[i]->el_size == el_size);
    Assert(dst[i]->el_count == el_count);
  }
#endif
  
  // fill loop
  U8 *rptr = src->data;
  U64 woff = 0;
  for (U64 i = 0; i < el_count; i += 1, woff += el_size){
    for (U64 j = 0; j < lane_count; j += 1){
      MemoryCopy(dst[j]->data + woff, rptr, el_size);
      rptr += el_size;
    }
  }
}

// convert

MR4TH_SYMBOL void
bop_f32_from_s24(BOP_Data *dst, BOP_Data *src){
  // TODO(allen): look at disassembly for real speed work
  
  Assert(dst->el_count == src->el_count);
  Assert(dst->el_size == 4);
  Assert(src->el_size == 3);
  
  // fill loop
  U8  *rptr = src->data;
  F32 *wptr = (F32*)dst->data;
  F32 *oplwptr = wptr + dst->el_count;
  for (; wptr < oplwptr; wptr += 1){
    
    // read s24
    S32 x = (*rptr);
    rptr += 1;
    x |= (*rptr) << 8;
    rptr += 1;
    x |= (*rptr) << 16;
    rptr += 1;
    if ((x & (1 << 23)) != 0){
      x |= (0xFF << 24);
    }
    
    // divide to float
    F32 fx = 0.f;
    if (x >= 0){
      fx = ((F32)x)/(8388607.f);
    }
    else{
      fx = ((F32)x)/(8388608.f);
    }
    
    // write f32
    *wptr = fx;
  }
}

MR4TH_SYMBOL void
bop_s24_from_f32(BOP_Data *dst, BOP_Data *src){
  // TODO(allen): look at disassembly for real speed work
  
  Assert(dst->el_count == src->el_count);
  Assert(dst->el_size == 3);
  Assert(src->el_size == 4);
  
  U64 el_count = dst->el_count;
  
  // fill loop
  F32 *rptr = (F32*)src->data;
  U8  *wptr = dst->data;
  U8  *oplwptr = dst->data + el_count;
  for (; wptr < oplwptr; wptr += 3, rptr += 1){
    
    // read f32
    F32 x = (*rptr);
    F32 x_clamped = Clamp(-1.f, x, 1.f);
    
    // multiply to s24 (inside a s32)
    S32 fx = 0;
    if (x >= 0){
      fx = 0x7FFFFF*x_clamped;
    }
    else{
      fx = 0x800000*x_clamped;
    }
    
    // write s24
    wptr[0] = (fx)&0xFF;
    wptr[1] = (fx >> 8)&0xFF;
    wptr[2] = (fx >> 16)&0xFF;
  }
}

MR4TH_SYMBOL void
bop_s16_from_f32(BOP_Data *dst, BOP_Data *src){
  // TODO(allen): look at disassembly for real speed work
  
  Assert(dst->el_count == src->el_count);
  Assert(dst->el_size == 2);
  Assert(src->el_size == 4);
  
  U64 el_count = dst->el_count;
  
  // fill loop
  F32 *rptr = (F32*)src->data;
  S16 *wptr = (S16*)dst->data;
  S16 *oplwptr = wptr + el_count;
  for (; wptr < oplwptr; wptr += 1, rptr += 1){
    
    // read f32
    F32 x = (*rptr);
    F32 x_clamped = Clamp(-1.f, x, 1.f);
    
    // multiply to s16 (inside a s32)
    S32 fx = 0;
    if (x >= 0){
      fx = 0x7FFF*x_clamped;
    }
    else{
      fx = 0x8000*x_clamped;
    }
    
    // write s16
    *wptr = fx;
  }
}

MR4TH_SYMBOL void
bop_f32_from_s16(BOP_Data *dst, BOP_Data *src){
  // TODO(allen): look at disassembly for real speed work
  
  Assert(dst->el_count == src->el_count);
  Assert(dst->el_size == 4);
  Assert(src->el_size == 2);
  
  U64 el_count = dst->el_count;
  
  // fill loop
  S16 *rptr = (S16*)src->data;
  F32 *wptr = (F32*)dst->data;
  F32 *oplwptr = wptr + el_count;
  for (; wptr < oplwptr; rptr += 1, wptr += 1){
    // read s16
    S16 x = (*rptr);
    
    // divide to f32
    F32 fx = 0;
    if (x >= 0){
      fx = (F32)(x)/(F32)(0x7FFF);
    }
    else{
      fx = (F32)(x)/(F32)(0x8000);
    }
    
    // write f32
    *wptr = fx;
  }
}

MR4TH_SYMBOL void
bop_f32_from_signed(BOP_Data *dst, BOP_Data *src, U32 numbits){
  // TODO(allen): look at disassembly for real speed work
  
  Assert(dst->el_count == src->el_count);
  Assert(dst->el_size == 4);
  Assert(numbits <= src->el_size*8);
  Assert(numbits > 0);
  Assert(src->el_size <= 8);
  
  // calculate denominators
  U32 highbitidx = numbits - 1;
  F32 positive_recip = 1.f/(F32)((1llu << highbitidx) - 1);
  F32 negative_recip = 1.f/(F32)((1llu << highbitidx));
  
  // fill loop
  U8  *rptr = src->data;
  U32  rstride = src->el_size;
  F32 *wptr = (F32*)dst->data;
  F32 *oplwptr = wptr + dst->el_count;
  for (; wptr < oplwptr; wptr += 1){
    
    // read x
    U32 sig = 0;
    U64 accum = 0;
    for (U32 i = 0; i < rstride; i += 1){
      accum |= (*rptr) << sig;
      rptr += 1;
      sig += 8;
    }
    S64 x = sign_extend_S64(accum, highbitidx);
    
    // divide to f32
    F32 fx = 0;
    if (x >= 0){ fx = (F32)(x)*positive_recip; }
    else       { fx = (F32)(x)*negative_recip; }
    
    // write f32
    *wptr = fx;
  }
}

MR4TH_SYMBOL void
bop_f32_from_f64(BOP_Data *dst, BOP_Data *src){
  Assert(dst->el_count == src->el_count);
  Assert(dst->el_size == 4);
  Assert(src->el_size == 8);
  
  // fill loop
  F64 *rptr = (F64*)src->data;
  F32 *wptr = (F32*)dst->data;
  F32 *oplwptr = wptr + dst->el_count;
  for (; wptr < oplwptr; rptr += 1, wptr += 1){
    *wptr = *rptr;
  }
}

#endif /* MR4TH_BUFFER_OPERATORS_BIG_FUNCTIONS_IMPL */
