/* Copyright 2021 Gustav Hatvigsson 
 * 
 * Based in part on work by Adrien Kunysz (Copyright 2010):
 * https://github.com/mjg59/tpmtotp/blob/master/base32.c
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/


#include "BaseN.h"

#define S_BASE_16_LAST 15
#define S_BASE_32_LAST 31
#define S_BASE_64_LAST 63

#define padding_mark =


#define EARLY_EXIT_ON_LEN_ZERO(inlen, outlen) {     \
   if (inlen == 0) {                                \
    outlen = sizeof ("");                           \
    sbyte * ret = s_malloc (outlen);                \
    ret[0] = '\0';                                  \
    return ret;                                     \
  }                                                 \
}

#if 0
   TEMPLATE FOR THE ASCII TABLE REVERSE
/* 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F     */
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*0*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*1*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*2*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*3*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*4*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*5*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*6*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*7*/


  LEGEND:
0..127     VALID NUMBER.

-1         INVALID

-2         IGNORE

-3         PADDING MARK


Padding mark is only not used in Base16, so it will generate an error.

#endif


/* ****************************************************************************
 ********************************* BASE 16 ************************************
 **************************************************************************** */

S_UNUSED
static const schar
S_BASE_16_ALPHABET[16] = {
  '0','1','2','3','4','5','6','7','8','9',

  'A','B','C','D','E','F'
};

S_UNUSED
static const sbyte
S_BASE_16_REVERSE[128] = {
/* 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F     */
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*0*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*1*/
  -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*2*/
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /*3*/
  -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*4*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*5*/
  -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*6*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*7*/
};

#define base_16_concat(first,last)  ((last) | (first << 4))
#define base_16_mask_shift(byte)    ((byte & 0xf0) >> 4)
#define base_16_mask(byte)          (byte & 0x0f)

schar *
s_base_16_enc (const sbyte * input_data,
               size_t in_len,
               size_t * out_len) {
  
  *out_len = (in_len * 2);
  
  schar * ret_val = s_malloc ((*out_len) + 1);
  
  s_dbg_print ("in len: %zu out_len: %zu\n", in_len, *out_len);
  
  ret_val[*out_len] = '\0';
  
  size_t pos = 0;
  
  for (sint i = 0; i < in_len; i++) {
    subyte m_byte = (subyte)input_data[i];
    ret_val [pos] = S_BASE_16_ALPHABET[base_16_mask_shift(m_byte)];
    ret_val [pos+1] = S_BASE_16_ALPHABET[base_16_mask(m_byte)];
    pos += 2;
  }
  
  return ret_val;
}

sbyte *
s_base_16_dec (const schar * base16_str,
               size_t in_len,
               size_t * out_len) {
  EARLY_EXIT_ON_LEN_ZERO (in_len, *out_len);
  *out_len = (round_up ((in_len / 2), 2) + 1);
  return s_base_16_dec_size (base16_str, in_len, *out_len);
}

sbyte *
s_base_16_dec_size (const schar * base16_str,
                   size_t in_len,
                   size_t out_len) {
  EARLY_EXIT_ON_LEN_ZERO (in_len, out_len);
  sbyte * ptr = (sbyte *) base16_str;
  sbyte * endptr = ptr + in_len;
  sbyte * ret_val = s_malloc (sizeof(sbyte) * (out_len));
  
  subyte first;
  subyte second;
  subyte concat;
  
  size_t pos = 0;
  
  
  //s_dbg_print ("first,\t second,\t concat\n");
  while (ptr != endptr) {
    if (S_BASE_16_REVERSE[(size_t)*ptr] < 0) {
      s_err_print ("Unclean Base16 data.\n");
    }
    
    first = *ptr++;
    second = *ptr++;
    concat = base_16_concat(S_BASE_16_REVERSE[first],
                            S_BASE_16_REVERSE[second]);
    
    //s_dbg_print ("%02x (%d),\t %02x (%d),\t %02x (%d)\n",first ,first, second, second, concat, concat);
    
    
    ret_val[pos] = concat;
    pos++;
  }
  
  return ret_val;
}

/* ****************************************************************************
 ********************************* BASE 32 ************************************
 **************************************************************************** */


S_UNUSED
static const schar
S_BASE_32_ALPHABET[32] = {
/* 0   1   2   3   4   5   6   7   8   9 */
  'A','B','C','D','E','F','G','H','I','J',
  'K','L','M','N','O','P','Q','R','S','T',
  'U','V','W','X','Y','Z',
                          '2','3','4','5',
  '6','7'
};


S_UNUSED
static const sbyte
S_BASE_32_REVERSE[128] = {
/* 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F     */
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*0*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*1*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*2*/
  -1, -1, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, -3, -1, -1, /*3*/
  -1,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, /*4*/
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, -1, -1, /*5*/
  -1,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, /*6*/
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, -1, -1, /*7*/
};

#define base_32_mask(byte) (byte & 0x1F)

#define base_32_get_offset(block) (8 - 5 - (5 * block) % 8)

#define base_32_shift_right(byte, offset)\
  ((offset) > 0) ? (byte >> (offset)) :\
  (byte << (-(offset)))

#define base_32_shift_left(byte, offset)\
  ((offset) > 0) ? (byte << (offset)) :\
  (byte >> (-(offset)))

#define base_32_get_octet(block) ((block*5) / 8)

sint
_base_32_decode_sequence (const schar * sequence, 
                          sbyte * out,
                          const sbyte reverse_table[]) {
  sint offset,
       octet;
  sbyte c,
        r;
  out[0] = 0;
  for (sint b = 0; b < 8; b++) {
    offset = base_32_get_offset (b);
    octet = base_32_get_octet (b);
    r = sequence[b];
    c = reverse_table[r];
    if (c < 0) { // invalid char.
     return octet;
    }
    out[octet] |= base_32_shift_left (c, offset);
    if (offset < 0) {
      out[octet + 1] = base_32_shift_left (c, offset + 8);
    }
  }
  return 5;
}

/* TODO */
schar *
s_base_32_enc (const sbyte * input_data,
               size_t in_len,
               size_t * out_len);

/* TODO */
sbyte *
s_base_32_dec (const schar * base32_str,
               size_t in_len,
               size_t * out_len) {
  EARLY_EXIT_ON_LEN_ZERO (in_len, *out_len);
  *out_len = in_len;
  return s_base_32_dec_size (base32_str,
                             in_len,
                             *out_len);
}

/* TODO */
sbyte *
s_base_32_dec_size (const schar * base32_str,
                   size_t in_len,
                   size_t out_len) {
  EARLY_EXIT_ON_LEN_ZERO (in_len, out_len);
  sbyte * ret_val = s_malloc (out_len);
  
  
  
  return ret_val;
}


/* * BASE 32 HEX * */

S_UNUSED
static const schar
S_BASE_32_HEX_ALPHABET[32] = {
/* 0   1   2   3   4   5   6   7   8   9 */
  '0','1','2','3','4','5','6','7','8','9',

  'A','B','C','D','E','F','G','H','I','J',
  'K','L','M','N','O','P','Q','R','S','T',
  'U','V'
};


S_UNUSED
static const sbyte
S_BASE_32_HEX_REVERSE[128] = {
/* 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F     */
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*0*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*1*/
  -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*2*/
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /*3*/
  -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, /*4*/
  25, 26, 27, 28, 29, 30, 31, 31, -1, -1, -1, -1, -1, -1, -1, -1, /*5*/
  -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, /*7*/
  25, 26, 27, 28, 29, 30, 31, 31, -1, -1, -1, -1, -1, -1, -1, -1, /*8*/
};

/* TODO */
schar *
s_base_32_hex_enc (const sbyte * input_data,
                   size_t in_len,
                   size_t * out_len);

/* TODO */
sbyte *
s_base_32_hex_dec (const schar * base32_str,
                   size_t in_len,
                   size_t * out_len);

/* TODO */
sbyte *
s_base_32_hex_dec_size (const schar * base32_str,
                   size_t in_len,
                   size_t * out_len);


/* ****************************************************************************
 ********************************* BASE 64 ************************************
 **************************************************************************** */

S_UNUSED
static const schar
S_BASE_64_ALPHABET[64] = {
/* 0   1   2   3   4   5   6   7   8   9 */
  'A','B','C','D','E','F','G','H','I','J',
  'K','L','M','N','O','P','Q','R','S','T',
  'U','V','W','X','Y','Z',
                          'a','b','c','d',
  'e','f','g','h','i','j','k','l','m','n',
  'o','p','q','r','s','t','u','v','w','x',
  'y','z',
          '0','1','2','3','4','5','6','7',
  '8','9',
          '+','/'
};

/* TODO */
S_UNUSED
static const sbyte
S_BASE_64_REVERSE[128] = {
/* 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F     */
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*0*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*1*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 63, -1, -1, -1, 64, /*2*/
  53, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, -1, -1, -3, -1, -1, /*3*/
  -1,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, /*4*/
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, -1, -1, /*5*/
  -1, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, /*6*/
  42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, -1, -1, -1, -1, -1, /*7*/
};

schar *
s_base_64_enc (const sbyte * input_data,
               size_t in_len,
               size_t * out_len);

sbyte *
s_base_64_dec (const schar * base64_str,
               size_t in_len,
               size_t * out_len);

/* * BASE 64 SAFE * */

S_UNUSED
static const schar
S_BASE_64_SAFE_ALPHABET[64] = {
/* 0   1   2   3   4   5   6   7   8   9 */
  'A','B','C','D','E','F','G','H','I','J',
  'K','L','M','N','O','P','Q','R','S','T',
  'U','V','W','X','Y','Z',
                          'a','b','c','d',
  'e','f','g','h','i','j','k','l','m','n',
  'o','p','q','r','s','t','u','v','w','x',
  'y','z',
          '0','1','2','3','4','5','6','7',
  '8','9',
          '-','_'
};

/* TODO */
S_UNUSED
static const sbyte
S_BASE_64_SAFE_REVERSE[128] = {
/* 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F     */
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*0*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*1*/
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 63, -1, -1, /*2*/
  53, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, -1, -1, -3, -1, -1, /*3*/
  -1,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, /*4*/
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, -1, 64, /*5*/
  -1, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, /*6*/
  42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, -1, -1, -1, -1, -1, /*7*/
};

/* TODO */
schar *
s_base_64_safe_enc (const sbyte * input_data,
                    size_t in_len,
                    size_t * out_len);

/* TODO */
sbyte *
s_base_64_safe_dec (const schar * base64_str,
                    size_t in_len,
                    size_t * out_len);

