//==========================================================================
// Name:            varicode.h
// Purpose:         Varicode encoded and decode functions
// Created:         Nov 24, 2012
// Authors:         David Rowe
//
// To test:
//          $ gcc varicode.c -o varicode -DVARICODE_UNITTEST -Wall
//          $ ./varicode
//
// License:
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2.1,
//  as published by the Free Software Foundation.  This program is
//  distributed in the hope that it will be useful, but WITHOUT ANY
//  WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
//  License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//==========================================================================

#include "varicode.h"

#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "debug_alloc.h"
#include "varicode_table.h"

/*
  output is an unpacked array of bits of maximum size max_out.  Note
  unpacked arrays are a more suitable form for modulator input.

  Code 1 covers the entire ASCII char set.
*/

int varicode_encode1(short varicode_out[], char ascii_in[], int max_out,
                     int n_in) {
  int n_out, index, n_zeros, v_len;
  unsigned short byte1, byte2, packed;
  char c;

  n_out = 0;

  while (n_in && (n_out < max_out)) {
    c = *ascii_in;
    if ((unsigned int)c >= 128) {
      c = ' ';
    }

    index = 2 * (unsigned int)(c);
    assert(index <= 254);
    byte1 = varicode_table1[index];
    byte2 = varicode_table1[index + 1];
    packed = (byte1 << 8) + byte2;

    // printf("n_in: %d ascii_in: %c index: %d packed 0x%x\n", n_in, *ascii_in,
    // index, packed);
    ascii_in++;

    n_zeros = 0;
    v_len = 0;
    while ((n_zeros < 2) && (n_out < max_out) && (v_len <= VARICODE_MAX_BITS)) {
      if (packed & 0x8000) {
        *varicode_out = 1;
        n_zeros = 0;
      } else {
        *varicode_out = 0;
        n_zeros++;
      }
      // printf("packed: 0x%x *varicode_out: %d n_zeros: %d v_len: %d\n",
      // packed, *varicode_out, n_zeros,v_len );
      packed <<= 1;
      varicode_out++;
      n_out++;
      v_len++;
    }
    assert(v_len <= VARICODE_MAX_BITS);

    n_in--;
  }

  return n_out;
}

/*
  Code 2 covers a subset, but is more efficient that Code 1 (282
  compared to 1315 bits on unittest) Unsupported characters are
  replaced by spaces.  We encode/decode two bits at a time.
*/

int varicode_encode2(short varicode_out[], char ascii_in[], int max_out,
                     int n_in) {
  int n_out, n_zeros, v_len, i;
  unsigned short packed;

  n_out = 0;

  while (n_in && (n_out < max_out)) {
    packed = varicode_table2[0];  // default to space if char not found

    // see if our character exists
    for (i = 0; i < sizeof(varicode_table2); i += 2) {
      if (varicode_table2[i] == *ascii_in)
        packed = (unsigned short)varicode_table2[i + 1] << 8;
    }

    // printf("n_in: %d ascii_in: %c index: %d packed 0x%x\n", n_in, *ascii_in,
    // index, packed);
    ascii_in++;

    n_zeros = 0;
    v_len = 0;
    while ((n_zeros < 2) && (n_out < max_out) && (v_len <= VARICODE_MAX_BITS)) {
      if (packed & 0x8000)
        varicode_out[0] = 1;
      else
        varicode_out[0] = 0;

      if (packed & 0x4000)
        varicode_out[1] = 1;
      else
        varicode_out[1] = 0;

      if (packed & 0xc000)
        n_zeros = 0;
      else
        n_zeros += 2;

      // printf("packed: 0x%x *varicode_out: %d n_zeros: %d v_len: %d\n",
      // packed, *varicode_out, n_zeros,v_len );
      packed <<= 2;
      varicode_out += 2;
      n_out += 2;
      v_len += 2;
    }
    assert(v_len <= VARICODE_MAX_BITS);

    n_in--;
  }

  assert((n_out % 2) == 0); /* outputs two bits at a time */

  return n_out;
}

/* Code 3 simply allows the modem to pass incoming/outgoing bits to/from higher
   levels in the code. */
int varicode_encode3(short varicode_out[], char ascii_in[], int max_out,
                     int n_in) {
  // We only support one bit at a time.
  assert(max_out >= 1 && n_in == 1);

  varicode_out[0] = ascii_in[0] != 0;
  return 1;
}

int varicode_encode(short varicode_out[], char ascii_in[], int max_out,
                    int n_in, int code_num) {
  assert((code_num == 1) || (code_num == 2) || (code_num == 3));

  if (code_num == 1)
    return varicode_encode1(varicode_out, ascii_in, max_out, n_in);
  else if (code_num == 2)
    return varicode_encode2(varicode_out, ascii_in, max_out, n_in);
  else
    return varicode_encode3(varicode_out, ascii_in, max_out, n_in);
}

void varicode_decode_init(struct VARICODE_DEC *dec_states, int code_num) {
  assert((code_num == 1) || (code_num == 2) || (code_num == 3));

  dec_states->state = 0;
  dec_states->n_zeros = 0;
  dec_states->v_len = 0;
  dec_states->packed = 0;
  dec_states->code_num = code_num;
  dec_states->n_in = 0;
  dec_states->in[0] = dec_states->in[1] = 0;
}

void varicode_set_code_num(struct VARICODE_DEC *dec_states, int code_num) {
  assert((code_num == 1) || (code_num == 2) || (code_num == 3));
  dec_states->code_num = code_num;
}

/* Code 1 decode function, accepts one bit at a time */

static int decode_one_bit(struct VARICODE_DEC *s, char *single_ascii,
                          short varicode_in, int long_code) {
  int found = 0, i;
  unsigned short byte1, byte2;

  // printf("decode_one_bit : state: %d varicode_in: %d packed: 0x%x n_zeros:
  // %d\n",
  //        s->state, varicode_in, s->packed, s->n_zeros);

  if (s->state == 0) {
    if (!varicode_in)
      return 0;
    else
      s->state = 1;
  }

  if (s->state == 1) {
    if (varicode_in) {
      s->packed |= (0x8000 >> s->v_len);
      s->n_zeros = 0;
    } else {
      s->n_zeros++;
    }
    s->v_len++;
    found = 0;

    /* end of character code */

    if (s->n_zeros == 2) {
      if (s->v_len) {
        /* run thru table but note with bit errors we might not actually find a
         * match */

        byte1 = s->packed >> 8;
        // printf("looking for byte1 : 0x%x ... ", byte1);
        byte2 = s->packed & 0xff;

        for (i = 0; i < 128; i++) {
          if ((byte1 == varicode_table1[2 * i]) &&
              (byte2 == varicode_table1[2 * i + 1])) {
            found = 1;
            *single_ascii = i;
          }
        }
      }
      varicode_decode_init(s, s->code_num);
    }

    /* code can run too long if we have a bit error */

    if (s->v_len > VARICODE_MAX_BITS) varicode_decode_init(s, s->code_num);
  }

  return found;
}

/* Code 2 decode function, accepts two bits at a time */

static int decode_two_bits(struct VARICODE_DEC *s, char *single_ascii,
                           short varicode_in1, short varicode_in2) {
  int found = 0, i;
  unsigned short byte1;

  if (s->state == 0) {
    if (!(varicode_in1 || varicode_in2))
      return 0;
    else
      s->state = 1;
  }

  if (s->state == 1) {
    if (varicode_in1) s->packed |= (0x8000 >> s->v_len);
    if (varicode_in2) s->packed |= (0x4000 >> s->v_len);
    if (varicode_in1 || varicode_in2)
      s->n_zeros = 0;
    else
      s->n_zeros += 2;

    s->v_len += 2;

    found = 0;

    /* end of character code */

    if (s->n_zeros == 2) {
      if (s->v_len) {
        /* run thru table but note with bit errors we might not actually find a
         * match */

        byte1 = s->packed >> 8;
        // printf("looking for byte1 : 0x%x ... ", byte1);
        for (i = 0; i < sizeof(varicode_table2); i += 2) {
          // printf("byte1: 0x%x 0x%x\n", byte1, (unsigned
          // char)varicode_table2[i+1]);
          if (byte1 == (unsigned char)varicode_table2[i + 1]) {
            found = 1;
            *single_ascii = varicode_table2[i];
            // printf("found: %d i=%d char=%c ", found, i, *single_ascii);
          }
        }
      }
      varicode_decode_init(s, s->code_num);
    }

    /* code can run too long if we have a bit error */

    if (s->v_len > VARICODE_MAX_BITS) varicode_decode_init(s, s->code_num);
  }

  return found;
}

int varicode_decode1(struct VARICODE_DEC *dec_states, char ascii_out[],
                     short varicode_in[], int max_out, int n_in) {
  int output, n_out;
  char single_ascii = 0;

  n_out = 0;

  // printf("varicode_decode: n_in: %d\n", n_in);

  while (n_in && (n_out < max_out)) {
    output = decode_one_bit(dec_states, &single_ascii, varicode_in[0], 0);
    varicode_in++;
    n_in--;

    if (output) {
      *ascii_out++ = single_ascii;
      n_out++;
    }
  }

  return n_out;
}

int varicode_decode2(struct VARICODE_DEC *dec_states, char ascii_out[],
                     short varicode_in[], int max_out, int n_in) {
  int output, n_out;
  char single_ascii = 0;

  n_out = 0;

  // printf("varicode_decode2: n_in: %d varicode_in[0] %d dec_states->n_in:
  // %d\n", n_in, varicode_in[0], dec_states->n_in); printf("%d ",
  // varicode_in[0]);
  while (n_in && (n_out < max_out)) {
    // keep two bit buffer so we can process two at a time

    dec_states->in[0] = dec_states->in[1];
    dec_states->in[1] = varicode_in[0];
    dec_states->n_in++;
    varicode_in++;
    n_in--;

    if (dec_states->n_in == 2) {
      output = decode_two_bits(dec_states, &single_ascii, dec_states->in[0],
                               dec_states->in[1]);

      dec_states->n_in = 0;

      if (output) {
        // printf("  output: %d single_ascii: 0x%x %c\n", output,
        // (int)single_ascii, single_ascii);
        *ascii_out++ = single_ascii;
        n_out++;
      }
    }
  }

  return n_out;
}

int varicode_decode3(struct VARICODE_DEC *dec_states, char ascii_out[],
                     short varicode_in[], int max_out, int n_in) {
  // We only handle one bit at a time.
  assert(max_out == 1 && n_in == 1);

  ascii_out[0] = varicode_in[0] != 0;
  return 1;
}

int varicode_decode(struct VARICODE_DEC *dec_states, char ascii_out[],
                    short varicode_in[], int max_out, int n_in) {
  if (dec_states->code_num == 1)
    return varicode_decode1(dec_states, ascii_out, varicode_in, max_out, n_in);
  else if (dec_states->code_num == 2)
    return varicode_decode2(dec_states, ascii_out, varicode_in, max_out, n_in);
  else
    return varicode_decode3(dec_states, ascii_out, varicode_in, max_out, n_in);
}

#ifdef VARICODE_UNITTEST
void test_varicode(int code_num) {
  char *ascii_in;
  short *varicode;
  int i, n_varicode_bits_out, n_ascii_chars_out, length, half, n_out, j, len;
  char *ascii_out;
  struct VARICODE_DEC dec_states;

  if (code_num == 1) {
    printf("long code:\n");
    length = sizeof(varicode_table1) / 2;
  } else {
    printf("short code:\n");
    length = sizeof(varicode_table2) / 2;
  }
  // length = 10;
  ascii_in = (char *)MALLOC(length);
  varicode = (short *)MALLOC(VARICODE_MAX_BITS * sizeof(short) * length);
  ascii_out = (char *)MALLOC(length);

  // 1. test all Varicode codes
  // -------------------------------------------------------------

  if (code_num == 1) {
    for (i = 0; i < length; i++) ascii_in[i] = (char)i;
  } else {
    for (i = 0; i < length; i++) ascii_in[i] = varicode_table2[2 * i];
  }
  // printf("  ascii_in: %s\n", ascii_in);
  n_varicode_bits_out = varicode_encode(
      varicode, ascii_in, VARICODE_MAX_BITS * length, length, code_num);

  printf("  n_varicode_bits_out: %d\n", n_varicode_bits_out);
  // for(i=0; i<n_varicode_bits_out; i++) {
  //     printf("%d \n", varicode[i]);
  // }

  // split decode in half to test how it preserves state between calls

  varicode_decode_init(&dec_states, code_num);
  half = n_varicode_bits_out / 2;
  n_ascii_chars_out =
      varicode_decode(&dec_states, ascii_out, varicode, length, half);
  // printf("  n_ascii_chars_out: %d\n", n_ascii_chars_out);

  n_ascii_chars_out += varicode_decode(
      &dec_states, &ascii_out[n_ascii_chars_out], &varicode[half],
      length - n_ascii_chars_out, n_varicode_bits_out - half);
  assert(n_ascii_chars_out == length);

  printf("  n_ascii_chars_out: %d\n", n_ascii_chars_out);
  printf("  average bits/character: %3.2f\n",
         (float)n_varicode_bits_out / n_ascii_chars_out);

  // printf("ascii_out: %s\n", ascii_out);

  if (memcmp(ascii_in, ascii_out, length) == 0)
    printf("  Test 1 Pass\n");
  else
    printf("  Test 1 Fail\n");

  // 2. Test some ascii with a run of zeros
  // -----------------------------------------------------

  sprintf(ascii_in, "CQ CQ CQ this is VK5DGR");

  assert(strlen(ascii_in) < length);
  if (code_num == 2)
    for (i = 0; i < strlen(ascii_in); i++) ascii_in[i] = tolower(ascii_in[i]);

  for (i = 0; i < 3; i++) {
    n_varicode_bits_out =
        varicode_encode(varicode, ascii_in, VARICODE_MAX_BITS * length,
                        strlen(ascii_in), code_num);
    n_ascii_chars_out = varicode_decode(&dec_states, ascii_out, varicode,
                                        length, n_varicode_bits_out);
    ascii_out[n_ascii_chars_out] = 0;

    printf("  ascii_out: %s\n", ascii_out);
    if (strcmp(ascii_in, ascii_out) == 0)
      printf("  Test 2 Pass\n");
    else
      printf("  Test 2 Fail\n");

    memset(varicode, 0, sizeof(short) * 20);
    n_ascii_chars_out =
        varicode_decode(&dec_states, ascii_out, varicode, length, 20);
    assert(n_ascii_chars_out == 0);
  }

  // 3. Test receiving one bit at a time
  // -----------------------------------------------------

  sprintf(ascii_in, "s=vk5dgr qth=adelaide");
  len = strlen(ascii_in);
  ascii_in[len] = 13;
  ascii_in[len + 1] = 0;

  assert(strlen(ascii_in) < length);
  if (code_num == 2)
    for (i = 0; i < strlen(ascii_in); i++) ascii_in[i] = tolower(ascii_in[i]);

  for (i = 0; i < 3; i++) {
    n_varicode_bits_out =
        varicode_encode(varicode, ascii_in, VARICODE_MAX_BITS * length,
                        strlen(ascii_in), code_num);
    printf("n_varicode_bits_out: %d\n", n_varicode_bits_out);

    n_ascii_chars_out = 0;
    for (j = 0; j < n_varicode_bits_out; j++) {
      n_out = varicode_decode(&dec_states, &ascii_out[n_ascii_chars_out],
                              &varicode[j], 1, 1);
      if (n_out) n_ascii_chars_out++;
    }
    ascii_out[n_ascii_chars_out] = 0;

    printf("  ascii_out: %s\n", ascii_out);
    if (strcmp(ascii_in, ascii_out) == 0)
      printf("  Test 3 Pass\n");
    else
      printf("  Test 3 Fail\n");
  }

  FREE(ascii_in);
  FREE(ascii_out);
  FREE(varicode);
}

int main(void) {
  test_varicode(1);
  test_varicode(2);
  return 0;
}
#endif
