/*
  FILE...: ldpc_enc.c
  AUTHOR.: Bill Cowley, David Rowe
  CREATED: Sep 2016

  RA LDPC encoder program. Using the elegant back substitution of RA
  LDPC codes.

  building: gcc ldpc_enc.c -o ldpc_enc -Wall -g
*/

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "mpdecode_core_test.h"
#include "ofdm_internal.h"

/* generated by ldpc_fsk_lib.m:ldpc_decode() */

#include "H2064_516_sparse_test.h"
#include "HRA_112_112_test.h"

int opt_exists(char *argv[], int argc, char opt[]) {
  int i;
  for (i = 0; i < argc; i++) {
    if (strcmp(argv[i], opt) == 0) {
      return i;
    }
  }
  return 0;
}

int main(int argc, char *argv[]) {
  unsigned char ibits[NUMBERROWSHCOLS];
  unsigned char pbits[NUMBERPARITYBITS];
  FILE *fin, *fout;
  int arg, sd, i, frames, codename, testframes, Nframes, data_bits_per_frame;
  double sdout[NUMBERROWSHCOLS + NUMBERPARITYBITS];
  struct LDPC ldpc;

  if (argc < 2) {
    fprintf(stderr, "\n");
    fprintf(stderr,
            "usage: %s InputOneBytePerBit OutputFile [--sd] [--code CodeName] "
            "[--testframes Nframes]\n",
            argv[0]);
    fprintf(stderr, "\n");
    fprintf(stderr, "usage: %s --listcodes\n\n", argv[0]);
    fprintf(stderr,
            "  List supported codes (more can be added via using Octave ldpc "
            "scripts)\n");
    fprintf(stderr, "\n");
    exit(0);
  }

  /* todo: put this in a function file to share with ldpc_dec.c */

  if ((codename = opt_exists(argv, argc, "--listcodes")) != 0) {
    fprintf(stderr, "\n");
    fprintf(stderr, "H2064_516_sparse\n");
    fprintf(stderr, "HRA_112_112\n");
    fprintf(stderr, "\n");
    exit(0);
  }

  /* set up LDPC code from include file constants */

  if ((codename = opt_exists(argv, argc, "--code")) != 0) {
    /* short rate 1/2 code for FreeDV HF digital voice */

    if (strcmp(argv[codename + 1], "HRA_112_112") == 0) {
      fprintf(stderr, "code: %s\n", argv[codename + 1]);
      ldpc.CodeLength = HRA_112_112_CODELENGTH;
      ldpc.NumberParityBits = HRA_112_112_NUMBERPARITYBITS;
      ldpc.NumberRowsHcols = HRA_112_112_NUMBERROWSHCOLS;
      ldpc.max_row_weight = HRA_112_112_MAX_ROW_WEIGHT;
      ldpc.max_col_weight = HRA_112_112_MAX_COL_WEIGHT;
      ldpc.H_rows = HRA_112_112_H_rows;
      ldpc.H_cols = HRA_112_112_H_cols;
    }
  } else {
    /* default Wenet High Alitiude Balloon rate 0.8 code */

    ldpc.CodeLength = CODELENGTH;
    ldpc.NumberParityBits = NUMBERPARITYBITS;
    ldpc.NumberRowsHcols = NUMBERROWSHCOLS;
    ldpc.max_row_weight = MAX_ROW_WEIGHT;
    ldpc.max_col_weight = MAX_COL_WEIGHT;
    ldpc.H_rows = H_rows;
    ldpc.H_cols = H_cols;
  }
  data_bits_per_frame = ldpc.NumberRowsHcols;

  if (strcmp(argv[1], "-") == 0)
    fin = stdin;
  else if ((fin = fopen(argv[1], "rb")) == NULL) {
    fprintf(stderr, "Error opening input bit file: %s: %s.\n", argv[1],
            strerror(errno));
    exit(1);
  }

  if (strcmp(argv[2], "-") == 0)
    fout = stdout;
  else if ((fout = fopen(argv[2], "wb")) == NULL) {
    fprintf(stderr, "Error opening output bit file: %s: %s.\n", argv[2],
            strerror(errno));
    exit(1);
  }

  sd = 0;
  if (opt_exists(argv, argc, "--sd")) {
    sd = 1;
  }

  testframes = Nframes = 0;

  if ((arg = (opt_exists(argv, argc, "--testframes")))) {
    testframes = 1;
    Nframes = atoi(argv[arg + 1]);
    fprintf(stderr, "Nframes: %d\n", Nframes);
  }

  frames = 0;

  while (fread(ibits, sizeof(char), data_bits_per_frame, fin) ==
         data_bits_per_frame) {
    if (testframes) {
      uint16_t r[data_bits_per_frame];
      ofdm_rand(r, data_bits_per_frame);

      for (i = 0; i < data_bits_per_frame; i++) {
        ibits[i] = r[i] > 16384;
      }
    }

    encode(&ldpc, ibits, pbits);

    if (sd) {
      /* map to BPSK symbols */
      for (i = 0; i < data_bits_per_frame; i++) sdout[i] = 1.0 - 2.0 * ibits[i];
      for (i = 0; i < ldpc.NumberParityBits; i++)
        sdout[i + data_bits_per_frame] = 1.0 - 2.0 * pbits[i];
      fwrite(sdout, sizeof(double), data_bits_per_frame + ldpc.NumberParityBits,
             fout);
    } else {
      fwrite(ibits, sizeof(char), data_bits_per_frame, fout);
      fwrite(pbits, sizeof(char), ldpc.NumberParityBits, fout);
    }

    frames++;
    if (testframes && (frames >= Nframes)) {
      goto finished;
    }
  }

finished:

  fclose(fin);
  fclose(fout);

  return 1;
}
