/* 
  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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "mpdecode_core.h"

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

#include "H2064_516_sparse.h"  
#include "HRA_112_112.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;
}

void encode(struct LDPC *ldpc, unsigned char ibits[], unsigned char pbits[]) {
    unsigned int p, i, tmp, par, prev=0;
    int          ind;
    double      *H_rows = ldpc->H_rows;

    for (p=0; p<ldpc->NumberParityBits; p++) {
        par = 0; 

        for (i=0; i<ldpc->max_row_weight; i++) {
            ind = (int)H_rows[p + i*ldpc->NumberParityBits];
            par = par + ibits[ind-1];
        }

        tmp = par + prev;

        tmp &= 1;    // only retain the lsb 
        prev = tmp; 
        pbits[p] = tmp; 
    }
}

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

    if (argc < 2) {
        fprintf(stderr, "\n");
        fprintf(stderr, "usage: %s InputOneBytePerBit OutputFile [--sd] [--code CodeName]\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;
    }

    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;
    }

    while (fread(ibits, sizeof(char), ldpc.NumberParityBits, fin) == ldpc.NumberParityBits) {

        encode(&ldpc, ibits, pbits);  
        
        if (sd) {
            for (i=0; i<ldpc.NumberRowsHcols; i++)
                sdout[i] = 1.0 - 2.0 * ibits[i];
            for (i=0; i<ldpc.NumberParityBits; i++)
                sdout[i+ldpc.NumberRowsHcols] = 1.0 - 2.0 * pbits[i];
            fwrite(sdout, sizeof(double), ldpc.NumberRowsHcols+ldpc.NumberParityBits, fout); 
        }
        else {
            fwrite(ibits, sizeof(char), ldpc.NumberRowsHcols, fout); 
            fwrite(pbits, sizeof(char), ldpc.NumberParityBits, fout); 
        }
    }

    fclose(fin);  
    fclose(fout); 

    return 1;
}
