/*
 * barcode.c
 *
 * Created: 10/30/2012 9:32:29 AM
 *  Author: andy.gao
 */ 
/*
 * EAN 13 digits format bar code decoder
 *
 * 
 */
/********************************************************************/
/*                      Headers                                     */
/********************************************************************/
#include "barcode.h"
/********************************************************************/
/*                      Local variables                             */
/********************************************************************/
const uint8_t mod_table[30] = {
/* Left, odd parity (0 - 9)
 0b0001101, 0b0011001, 0b0010011, 0b0111101, 0b0100011,
 0b0110001, 0b0101111, 0b0111011, 0b0110111, 0b0001011,
*/
 0x0d, 0x19, 0x13, 0x3d, 0x23, 0x31, 0x2f, 0x3b, 0x37, 0x0b,
/* Left, even parity (10 - 19)
 0b0100111, 0b0110011, 0b0011011, 0b0100001, 0b0011101,
 0b0111001, 0b0000101, 0b0010001, 0b0001001, 0b0010111,
*/
 0x27, 0x33, 0x1b, 0x21, 0x1d, 0x39, 0x05, 0x11, 0x09, 0x17,
/* Right, check (20-29)
 0b1110010, 0b1100110, 0b1101100, 0b1000010, 0b1011100,
 0b1001110, 0b1010000, 0b1000100, 0b1001000, 0b1110100,
*/
 0x72, 0x66, 0x6c, 0x42, 0x5c, 0x4e, 0x50, 0x44, 0x48, 0x74,
};

/*
 * parity_table[] is used to find first prefix character.
 *
 * 0b000000, 0b001011, 0b001101, 0b001110, 0b010011,	0..4
 * 0b011001, 0b011100, 0b010101, 0b010110, 0b011010,	5..9
 */
static const uint8_t parity_tab[10] = {
 0x00, 0x0b, 0x0d, 0x0e, 0x13,
 0x19, 0x1d, 0x15, 0x16, 0x1a,
};

static const uint8_t otab[5] = { 0, 1, 3, 7, 15 };

#define	BC_BUFFER_END	(bc_buffer+IMAGE_WIDTH)

bool captured = false;
uint8_t thres;
uint8_t bc_buffer[IMAGE_WIDTH];	/* One raw barcode image data */
short bar_width[JAN_BARS];
short histogram[Num_HIST];

char  hist_mark[Num_HIST];
uint8_t *g_p_uc_cap_dest_buf;
int bin_gray_img_ohno()
  { 
    uint8_t *bp;
    uint8_t i,thres; 
    int lowest, peak1, peak2, pdiff, diff;   
    // Image data is given as gray scale.
    memset(histogram, 0, sizeof(histogram));
    memset(hist_mark, 0, sizeof(hist_mark));
    bp = g_p_uc_cap_dest_buf; 
    for (i = 0; i < IMAGE_WIDTH * SCAN_LINES; i++) {
        histogram[*bp++]++;
        }
  /* Mark peak positions */  
    pdiff = histogram[1] - histogram[0];
    if (pdiff > 0)    
      hist_mark[0] = 'B';
      else if (pdiff < 0)
              hist_mark[0] = 'T';
    for (i = 1; i < Num_HIST-1; i++) {
        diff = histogram[i+1] - histogram[i];
        if (diff < 0) {
           if (pdiff >= 0) {
                    hist_mark[i] = 'T';
            }
    
          } 
        else if (diff > 0) {
                     if (pdiff <= 0) {
                          hist_mark[i] = 'B';
                        }
                    }
            pdiff = diff;
          }
     if (pdiff > 0)
          hist_mark[i] = 'T';
      else if (pdiff < 0)
          hist_mark[i] = 'B';
        /* Find two peaks */
      peak1 = peak2 = 0;
     for (i = 0; i < Num_HIST; i++) {
          if (hist_mark[i] == 'T') {
             if (histogram[i] > histogram[peak1]) {
                 peak2 = peak1;
                 peak1 = i;
                 } 
             else if (histogram[i] >= histogram[peak2]) {
                    peak2 = i;
                       }
             }
         }
     /*
   * Find bottom position located between two peaks?*/
     if (peak1 < peak2) {
          lowest = peak1;
          for (i = peak1; i < peak2; i++) {
              if (hist_mark[i] == 'B') {
                  if (histogram[i] < histogram[lowest])
                      lowest = i;
                  }
              }
          } 
        else {  
          /* peak1 >= peak2 */
              lowest = peak2;
              for (i = peak2; i < peak1; i++) {
                  if (hist_mark[i] == 'B') {
                      if (histogram[i] < histogram[lowest])
                          lowest = i;
                      }
                  }
              }
            thres = lowest ;
        return thres;
}


 int bin_gray_img_otsu()
{
  int i,n,n1,n2;
  uint8_t *bp;
  uint8_t thres; 
  float m1,m2,sum,csum,gmax,sb;
  int size=IMAGE_WIDTH*SCAN_LINES;
  // Image data is given as gray scale.
  memset(histogram, 0, sizeof(histogram)); 
  bp = g_p_uc_cap_dest_buf;
  // Setup a Histogram for the image
  for (i = 0; i < size; i++) { 
    histogram[*bp++ ]++; 
    }
  sum = csum = 0.0;
  n = 0;
  for (i = 0; i < Num_HIST; i++) {
	sum += (double) i * (double) histogram[i]; // x*f(x) 
	n += histogram[i]; //f(x) 
	}
// do the otsu global thresholding method
  gmax = -1;
  n1 = 0;
  thres = 128;
  for (i = 0; i < Num_HIST; i++) {
      n1 += histogram[i];
      n2 = n - n1;
      if (n2 == 0) { break; }
      csum += (double) i * histogram[i];
      m1 = csum / n1;
      m2 = (sum - csum) / n2;
      sb = (double) n1 *(double) n2 *(m1 - m2) * (m1 - m2);
      if (sb > gmax) {
	gmax = sb;
        thres = i;
	}
      }
  return thres;
}

int find_char_val(int zflag, int *plen)
{
   int i, val, nb;

   val = i = 0;
   if (zflag) {	/* Left side. starts with white bar */
     while (i < 4) {
       nb = plen[i];
       if (i&1) {	/* odd number bar is black */
         val = val << nb;
         val |= otab[nb];	/* shift and set bits */
       } else {		/* even number bar is white */
         val = val << nb;	/* shift only */
       }
       i++;
     }
   } else {	/* Right side. strts with black bar */
     while (i < 4) {
       nb = plen[i];
       if (i&1) {	/* odd number bar is white */
         val = val << nb;
       } else {		/* event number bar is black */
         val = val << nb;
         val |= otab[nb];	/* shift and set bits */
       }
       i++;
     }
   }
   /* Try to find the value in our module table */
   for (i = 0; i < sizeof(mod_table); i++) {
     if (val == mod_table[i])
       return(i);
   }
   return -1;	/* Not found. Faild to decode */
}

/*
 * Decode data character consists from seven modules
 */
int decode7(int index, short *wtab)
{
  int i, mod_len[4];
  int wval[4];
  int av, remain;
  int maxid;

  /* We are going to find module counts of each bars.
   * It is obvious that each bar has at least one bar width.
   */
  for (i = 0; i < 4; i++) {  /* Total four bars */
    mod_len[i] = 1;          /* Each bar has at least one module */
    wval[i] = wtab[i] << 2;
  }
  /* Because data character always have 7 modules,
   * we have three modules remain.
   */
  remain = 3;
  /* Compute average module width */
  av = 0;
  for (i = 0; i < 4; i++) av += wval[i];
  av = av / 7;
  for (i = 0; i < 4; i++) wval[i] -= av;

  /* Allocate module length from bigger bars */
  while (remain > 0) {
    /* Find biggest */
    maxid = 0;
    if (wval[1] > wval[0]) maxid = 1;
    if (wval[2] > wval[maxid]) maxid = 2;
    if (wval[3] > wval[maxid]) maxid = 3;
    mod_len[maxid]++;
    wval[maxid] -= av;
    remain--;
  }

  if (index < 6) {	/* Left side digit character */

    i = find_char_val(1, mod_len);
  } else {		/* Right side character */

    i = find_char_val(0, mod_len);
  }

  return i;
}

/*
 * Decoded 13 digits number is stored in bar_digits[] array.
 */
char bar_digits[13+1];

int bar_decode(int dump)
{
  uint8_t i, index, len;
  uint8_t mark, *bp;
  int prefix, error;
  char *cp;
  short *wp;
  error = 0;

  /* Measure each bar width */
  index = 0;
  bp = bc_buffer;
  mark = 0;
  while (index < JAN_BARS && bp < BC_BUFFER_END) {
    len = 0;
    while (bp < BC_BUFFER_END && *bp == mark) {
      len++;
      bp++;
    }
    bar_width[index] = len;
    mark ^= 1;
    index++;
  }
  /* We should find total 61 bars. If not, report as error */
  if (index != JAN_BARS) {
   	LCDD_DrawRectangleWithFill(0,0,20,20,COLOR_RED);
    return 1;
  }

  /* We should have long enough margins */
  if (bar_width[0] < 15 || bar_width[60] < 15 ) {
	   LCDD_DrawRectangleWithFill(0,0,20,20,COLOR_BLUEVIOLET);
    //return 200;
  }

  prefix = 0;
  /* Decode left 6 digits */
  wp = bar_width + 4;
  cp = &bar_digits[1];
  for (i = 0; i < 6; i++) {	/* Left side index starts from zero */
    index = decode7(i, wp);
    prefix <<= 1;
    if (index < 0 || index > 19) { /* Error */
      *cp = '?';
      error++;
    } else {
      if (index >= 10) {	/* Even parity */
        prefix++;
      }
      *cp = (index % 10) + '0';
    }
    wp += 4;
    cp++;
  }
  /* Find first prefix character */
  for (i = 0; i < 10; i++) {
    if (parity_tab[i] == prefix) {
      bar_digits[0] = i + '0';
      break;
    }
  }
  if (i == 10) {
    bar_digits[0] = '?';
    error++;
  }

  /* Decode right 6 digits */
  wp = bar_width + 33;
  cp = &bar_digits[7];
  for (i = 6; i < 12; i++) {	/* right side index starts from six */
    index = decode7(i, wp);
    if (index < 20) { /* Error */
      *cp = '?';
      error++;
    } else {
      *cp = index - 20 + '0';
    }
    wp += 4;
    cp++;
  }
  *cp = 0;
  if (error == 0) {	/* validate check digit */
    short even_sum, odd_sum;
    int dval;

    even_sum = odd_sum = 0;
    for (i = 0; i < 12; i++) {
      dval = bar_digits[i] - '0';
      if (i & 1)
        odd_sum += dval;
      else
        even_sum += dval;
    }
    even_sum += odd_sum * 3;
    i = 10 - (even_sum % 10);
    if (i == 10) i = 0;

    if (error == 0) {
      if (('0' + i) != bar_digits[12]) {
        error++;
      }
    }
  }
  if (error<2 ) {
	    LCDD_DrawRectangleWithFill(0,0,20,20,COLOR_YELLOWGREEN);
		if (captured ==false){
		  LCDD_DrawString(20,20,(uint8_t*)bar_digits,COLOR_YELLOWGREEN);
		}	
  }
  return error;
}

void clear_numbers()
{
  int i;
  for (i = 0; i < 13; i++) bar_digits[i] = ' ';
  bar_digits[13] = 0;

}

int scan_bar_code( int option)
{
  int i,res;
  int dump=0;
  uint8_t *bp, *dp;
  g_p_uc_cap_dest_buf = (uint8_t*)CAP_DEST;
  bp = g_p_uc_cap_dest_buf + IMAGE_WIDTH * (SCAN_LINES/2);
  dp = bc_buffer;

  if (option ==0){ 
      thres = bin_gray_img_ohno();
      }
  else{
      thres= bin_gray_img_otsu();
      } 
				
  for (i = 0; i < IMAGE_WIDTH; i++) {
      if (*bp >= thres){
	*dp++ = 0;
         } 
	else {
	     *dp++ = 1;
	      }
	bp++;
	}
  res = bar_decode(dump);
  if (res){   
    /* * Decode failed.
     * Try to decode data scanned from right to left direction.
     */
      bp = g_p_uc_cap_dest_buf + IMAGE_WIDTH * (SCAN_LINES/2);
      dp = BC_BUFFER_END;
      for(i = 0; i < IMAGE_WIDTH; i++) {
         if (*bp >= thres) {
            *--dp = 0;
            } 
         else {
           *--dp = 1;
           }
           bp++;
         }
      res = bar_decode(dump);
     }
   if (res == 0){ 
   	/* Decode success.  */
     LCDD_Initialize();
     LCD_On();
     LCDD_Fill(COLOR_TURQUOISE);
     LCDD_DrawString(0,10,"BAR Scanner Result:",COLOR_BLACK);
     LCDD_DrawString(0,50,(uint8_t *)bar_digits,COLOR_BLACK);
     Wait(3000);
     LCDD_Fill(COLOR_BLACK);
     } 
    return res;
}
