/*
 * Zebra barcode library
 *
 * Copyright 2011 Mindaugas Kavaliauskas <dbtopas at dbtopas.lt>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * 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; see the file LICENSE.txt.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

/*
     QR Code is ISO/IEC18004

     JIS-X-0510  QR Code standard in Japanese language :)
       https://osdn.jp/projects/qrcode/docs/qrcode_specification_ja/en/1/qrcode_specification_ja.pdf

   https://en.wikipedia.org/wiki/QR_Code
   http://www.qrcode.com/
   https://foxdesignsstudio.com/uploads/pdf/Three_QR_Code.pdf
   https://web.archive.org/web/www.qrme.co.uk/qr-code-resources/understanding-a-qr-code.html
   http://www.swetake.com/qrcode/index-e.html
   https://www.codeproject.com/Articles/20574/Open-Source-QRCode-Library
   https://osdn.jp/projects/reedsolomon/
   https://web.archive.org/web/twit88.com/platform/projects/show/mt-qrcode
   https://qrcode.osdn.jp/
   https://zxing.org/w/decode.jspx                         Online decoder
   http://blog.qr4.nl/Online-QR-Code_Decoder.aspx          Online decoder (not all codes are decoded)
   http://www.thonky.com/qr-code-tutorial/                 Tutorial
   https://github.com/zxing/zxing                          Java library
   http://goqr.me/                                         Online encode
   http://www.pclviewer.com/rs2/calculator.html            Reed-Solomon ECC calculator
   https://web.archive.org/web/raidenii.net/files/datasheets/misc/qr_code.pdf

 */

#include "hbzebra.h"

/* #define DEBUG_CODE */

typedef struct
{
   unsigned char uiCount;
   unsigned char uiData;
   unsigned char uiECC;
} QRBLOCKPARAM;

typedef struct
{
   unsigned int uiECC;
   QRBLOCKPARAM block[ 2 ];
} QRLEVEL, * PQRLEVEL;

typedef struct
{
   unsigned int uiTotal;          /* total number of codewords */
   QRLEVEL      level[ 4 ];
} QRVERSION, * PQRVERSION;

static const QRVERSION s_version[] =
{
   {  26, {{   7, {{ 1,  19,  7}, { 0,   0,  0}}},   /*  1    2 * 2 */
           {  10, {{ 1,  16, 10}, { 0,   0,  0}}},   /*       4 * 2 */
           {  13, {{ 1,  13, 13}, { 0,   0,  0}}},   /*       6 * 2 */
           {  17, {{ 1,   9, 17}, { 0,   0,  0}}}}}, /*       8 * 2 */
   {  44, {{  10, {{ 1,  34, 10}, { 0,   0,  0}}},   /*  2    4 * 2 */
           {  16, {{ 1,  28, 16}, { 0,   0,  0}}},
           {  22, {{ 1,  22, 22}, { 0,   0,  0}}},
           {  28, {{ 1,  16, 28}, { 0,   0,  0}}}}},
   {  70, {{  15, {{ 1,  55, 15}, { 0,   0,  0}}},   /*  3    7 * 2 */
           {  26, {{ 1,  44, 26}, { 0,   0,  0}}},
           {  36, {{ 2,  17, 18}, { 0,   0,  0}}},
           {  44, {{ 2,  13, 22}, { 0,   0,  0}}}}},
   { 100, {{  20, {{ 1,  80, 20}, { 0,   0,  0}}},   /*  4 */
           {  36, {{ 2,  32, 18}, { 0,   0,  0}}},
           {  52, {{ 2,  24, 26}, { 0,   0,  0}}},
           {  64, {{ 4,   9, 16}, { 0,   0,  0}}}}},
   { 134, {{  26, {{ 1, 108, 26}, { 0,   0,  0}}},   /*  5 */
           {  48, {{ 2,  43, 24}, { 0,   0,  0}}},
           {  72, {{ 2,  15, 18}, { 2,  16, 18}}},
           {  88, {{ 2,  11, 22}, { 2,  12, 22}}}}},
   { 172, {{  36, {{ 2,  68, 18}, { 0,   0,  0}}},   /*  6 */
           {  64, {{ 4,  27, 16}, { 0,   0,  0}}},
           {  96, {{ 4,  19, 24}, { 0,   0,  0}}},
           { 112, {{ 4,  15, 28}, { 0,   0,  0}}}}},
   { 196, {{  40, {{ 2,  78, 20}, { 0,   0,  0}}},   /*  7 */
           {  72, {{ 4,  31, 18}, { 0,   0,  0}}},
           { 108, {{ 2,  14, 18}, { 4,  15, 18}}},
           { 130, {{ 4,  13, 26}, { 1,  14, 26}}}}},
   { 242, {{  48, {{ 2,  97, 24}, { 0,   0,  0}}},   /*  8 */
           {  88, {{ 2,  38, 22}, { 2,  39, 22}}},
           { 132, {{ 4,  18, 22}, { 2,  19, 22}}},
           { 156, {{ 4,  14, 26}, { 2,  15, 26}}}}},
   { 292, {{  60, {{ 2, 116, 30}, { 0,   0,  0}}},   /*  9 */
           { 110, {{ 3,  36, 22}, { 2,  37, 22}}},
           { 160, {{ 4,  16, 20}, { 4,  17, 20}}},
           { 192, {{ 4,  12, 24}, { 4,  13, 24}}}}},
   { 346, {{  72, {{ 2,  68, 18}, { 2,  69, 18}}},   /* 10 */
           { 130, {{ 4,  43, 26}, { 1,  44, 26}}},
           { 192, {{ 6,  19, 24}, { 2,  20, 24}}},
           { 224, {{ 6,  15, 28}, { 2,  16, 28}}}}},
   { 404, {{  80, {{ 4,  81, 20}, { 0,   0,  0}}},   /* 11 */
           { 150, {{ 1,  50, 30}, { 4,  51, 30}}},
           { 224, {{ 4,  22, 28}, { 4,  23, 28}}},
           { 264, {{ 3,  12, 24}, { 8,  13, 24}}}}},
   { 466, {{  96, {{ 2,  92, 24}, { 2,  93, 24}}},   /* 12 */
           { 176, {{ 6,  36, 22}, { 2,  37, 22}}},
           { 260, {{ 4,  20, 26}, { 6,  21, 26}}},
           { 308, {{ 7,  14, 28}, { 4,  15, 28}}}}},
   { 532, {{ 104, {{ 4, 107, 26}, { 0,   0,  0}}},   /* 13 */
           { 198, {{ 8,  37, 22}, { 1,  38, 22}}},
           { 288, {{ 8,  20, 24}, { 4,  21, 24}}},
           { 352, {{12,  11, 22}, { 4,  12, 22}}}}},
   { 581, {{ 120, {{ 3, 115, 30}, { 1, 116, 30}}},   /* 14 */
           { 216, {{ 4,  40, 24}, { 5,  41, 24}}},
           { 320, {{11,  16, 20}, { 5,  17, 20}}},
           { 384, {{11,  12, 24}, { 5,  13, 24}}}}},
   { 655, {{ 132, {{ 5,  87, 22}, { 1,  88, 22}}},   /* 15 */
           { 240, {{ 5,  41, 24}, { 5,  42, 24}}},
           { 360, {{ 5,  24, 30}, { 7,  25, 30}}},
           { 432, {{11,  12, 24}, { 7,  13, 24}}}}},
   { 733, {{ 144, {{ 5,  98, 24}, { 1,  99, 24}}},   /* 16 */
           { 280, {{ 7,  45, 28}, { 3,  46, 28}}},
           { 408, {{15,  19, 24}, { 2,  20, 24}}},
           { 480, {{ 3,  15, 30}, {13,  16, 30}}}}},
   { 815, {{ 168, {{ 1, 107, 28}, { 5, 108, 28}}},   /* 17 */
           { 308, {{10,  46, 28}, { 1,  47, 28}}},
           { 448, {{ 1,  22, 28}, {15,  23, 28}}},
           { 532, {{ 2,  14, 28}, {17,  15, 28}}}}},
   { 901, {{ 180, {{ 5, 120, 30}, { 1, 121, 30}}},   /* 18 */
           { 338, {{ 9,  43, 26}, { 4,  44, 26}}},
           { 504, {{17,  22, 28}, { 1,  23, 28}}},
           { 588, {{ 2,  14, 28}, {19,  15, 28}}}}},
   { 991, {{ 196, {{ 3, 113, 28}, { 4, 114, 28}}},   /* 19 */
           { 364, {{ 3,  44, 26}, {11,  45, 26}}},
           { 546, {{17,  21, 26}, { 4,  22, 26}}},
           { 650, {{ 9,  13, 26}, {16,  14, 26}}}}},
   {1085, {{ 224, {{ 3, 107, 28}, { 5, 108, 28}}},   /* 20 */
           { 416, {{ 3,  41, 26}, {13,  42, 26}}},
           { 600, {{15,  24, 30}, { 5,  25, 30}}},
           { 700, {{15,  15, 28}, {10,  16, 28}}}}},
   {1156, {{ 224, {{ 4, 116, 28}, { 4, 117, 28}}},   /* 21 */
           { 442, {{17,  42, 26}, { 0,   0,  0}}},
           { 644, {{17,  22, 28}, { 6,  23, 28}}},
           { 750, {{19,  16, 30}, { 6,  17, 30}}}}},
   {1258, {{ 252, {{ 2, 111, 28}, { 7, 112, 28}}},   /* 22 */
           { 476, {{17,  46, 28}, { 0,   0,  0}}},
           { 690, {{ 7,  24, 30}, {16,  25, 30}}},
           { 816, {{34,  13, 24}, { 0,   0,  0}}}}},
   {1364, {{ 270, {{ 4, 121, 30}, { 5, 122, 30}}},   /* 23 */
           { 504, {{ 4,  47, 28}, {14,  48, 28}}},
           { 750, {{11,  24, 30}, {14,  25, 30}}},
           { 900, {{16,  15, 30}, {14,  16, 30}}}}},
   {1474, {{ 300, {{ 6, 117, 30}, { 4, 118, 30}}},   /* 24 */
           { 560, {{ 6,  45, 28}, {14,  46, 28}}},
           { 810, {{11,  24, 30}, {16,  25, 30}}},
           { 960, {{30,  16, 30}, { 2,  17, 30}}}}},
   {1588, {{ 312, {{ 8, 106, 26}, { 4, 107, 26}}},   /* 25 */
           { 588, {{ 8,  47, 28}, {13,  48, 28}}},
           { 870, {{ 7,  24, 30}, {22,  25, 30}}},
           {1050, {{22,  15, 30}, {13,  16, 30}}}}},
   {1706, {{ 336, {{10, 114, 28}, { 2, 115, 28}}},   /* 26 */
           { 644, {{19,  46, 28}, { 4,  47, 28}}},
           { 952, {{28,  22, 28}, { 6,  23, 28}}},
           {1110, {{33,  16, 30}, { 4,  17, 30}}}}},
   {1828, {{ 360, {{ 8, 122, 30}, { 4, 123, 30}}},   /* 27 */
           { 700, {{22,  45, 28}, { 3,  46, 28}}},
           {1020, {{ 8,  23, 30}, {26,  24, 30}}},
           {1200, {{12,  15, 30}, {28,  16, 30}}}}},
   {1921, {{ 390, {{ 3, 117, 30}, {10, 118, 30}}},   /* 28 */
           { 728, {{ 3,  45, 28}, {23,  46, 28}}},
           {1050, {{ 4,  24, 30}, {31,  25, 30}}},
           {1260, {{11,  15, 30}, {31,  16, 30}}}}},
   {2051, {{ 420, {{ 7, 116, 30}, { 7, 117, 30}}},   /* 29 */
           { 784, {{21,  45, 28}, { 7,  46, 28}}},
           {1140, {{ 1,  23, 30}, {37,  24, 30}}},
           {1350, {{19,  15, 30}, {26,  16, 30}}}}},
   {2185, {{ 450, {{ 5, 115, 30}, {10, 116, 30}}},   /* 30 */
           { 812, {{19,  47, 28}, {10,  48, 28}}},
           {1200, {{15,  24, 30}, {25,  25, 30}}},
           {1440, {{23,  15, 30}, {25,  16, 30}}}}},
   {2323, {{ 480, {{13, 115, 30}, { 3, 116, 30}}},   /* 31 */
           { 868, {{ 2,  46, 28}, {29,  47, 28}}},
           {1290, {{42,  24, 30}, { 1,  25, 30}}},
           {1530, {{23,  15, 30}, {28,  16, 30}}}}},
   {2465, {{ 510, {{17, 115, 30}, { 0,   0,  0}}},   /* 32 */
           { 924, {{10,  46, 28}, {23,  47, 28}}},
           {1350, {{10,  24, 30}, {35,  25, 30}}},
           {1620, {{19,  15, 30}, {35,  16, 30}}}}},
   {2611, {{ 540, {{17, 115, 30}, { 1, 116, 30}}},   /* 33 */
           { 980, {{14,  46, 28}, {21,  47, 28}}},
           {1440, {{29,  24, 30}, {19,  25, 30}}},
           {1710, {{11,  15, 30}, {46,  16, 30}}}}},
   {2761, {{ 570, {{13, 115, 30}, { 6, 116, 30}}},   /* 34 */
           {1036, {{14,  46, 28}, {23,  47, 28}}},
           {1530, {{44,  24, 30}, { 7,  25, 30}}},
           {1800, {{59,  16, 30}, { 1,  17, 30}}}}},
   {2876, {{ 570, {{12, 121, 30}, { 7, 122, 30}}},   /* 35 */
           {1064, {{12,  47, 28}, {26,  48, 28}}},
           {1590, {{39,  24, 30}, {14,  25, 30}}},
           {1890, {{22,  15, 30}, {41,  16, 30}}}}},
   {3034, {{ 600, {{ 6, 121, 30}, {14, 122, 30}}},   /* 36 */
           {1120, {{ 6,  47, 28}, {34,  48, 28}}},
           {1680, {{46,  24, 30}, {10,  25, 30}}},
           {1980, {{ 2,  15, 30}, {64,  16, 30}}}}},
   {3196, {{ 630, {{17, 122, 30}, { 4, 123, 30}}},   /* 37 */
           {1204, {{29,  46, 28}, {14,  47, 28}}},
           {1770, {{49,  24, 30}, {10,  25, 30}}},
           {2100, {{24,  15, 30}, {46,  16, 30}}}}},
   {3362, {{ 660, {{ 4, 122, 30}, {18, 123, 30}}},   /* 38 */
           {1260, {{13,  46, 28}, {32,  47, 28}}},
           {1860, {{48,  24, 30}, {14,  25, 30}}},
           {2220, {{42,  15, 30}, {32,  16, 30}}}}},
   {3532, {{ 720, {{20, 117, 30}, { 4, 118, 30}}},   /* 39 */
           {1316, {{40,  47, 28}, { 7,  48, 28}}},
           {1950, {{43,  24, 30}, {22,  25, 30}}},
           {2310, {{10,  15, 30}, {67,  16, 30}}}}},
   {3706, {{ 750, {{19, 118, 30}, { 6, 119, 30}}},   /* 40 */
           {1372, {{18,  47, 28}, {31,  48, 28}}},
           {2040, {{34,  24, 30}, {34,  25, 30}}},
           {2430, {{20,  15, 30}, {61,  16, 30}}}}}
};


static const unsigned char s_rev[ 256 ] =
{
#define R2( n )  n,       n + 2 * 64,       n + 1 * 64,       n + 3 * 64
#define R4( n )  R2( n ), R2( n + 2 * 16 ), R2( n + 1 * 16 ), R2( n + 3 * 16 )
#define R6( n )  R4( n ), R4( n + 2 * 4 ),  R4( n + 1 * 4 ),  R4( n + 3 * 4 )
   R6( 0 ), R6( 2 ), R6( 1 ), R6( 3 )
};


/* zero terminated align patterns positions */
static unsigned char s_align01[] = { 0 };
static unsigned char s_align02[] = { 6, 18, 0 };
static unsigned char s_align03[] = { 6, 22, 0 };
static unsigned char s_align04[] = { 6, 26, 0 };
static unsigned char s_align05[] = { 6, 30, 0 };
static unsigned char s_align06[] = { 6, 34, 0 };
static unsigned char s_align07[] = { 6, 22, 38, 0 };
static unsigned char s_align08[] = { 6, 24, 42, 0 };
static unsigned char s_align09[] = { 6, 26, 46, 0 };
static unsigned char s_align00[] = { 6, 28, 50, 0 };
static unsigned char s_align11[] = { 6, 30, 54, 0 };
static unsigned char s_align12[] = { 6, 32, 58, 0 };
static unsigned char s_align13[] = { 6, 34, 62, 0 };
static unsigned char s_align14[] = { 6, 26, 46, 66, 0 };
static unsigned char s_align15[] = { 6, 26, 48, 70, 0 };
static unsigned char s_align16[] = { 6, 26, 50, 74, 0 };
static unsigned char s_align17[] = { 6, 30, 54, 78, 0 };
static unsigned char s_align18[] = { 6, 30, 56, 82, 0 };
static unsigned char s_align19[] = { 6, 30, 58, 86, 0 };
static unsigned char s_align10[] = { 6, 34, 62, 90, 0 };
static unsigned char s_align21[] = { 6, 28, 50, 72,  94, 0 };
static unsigned char s_align22[] = { 6, 26, 50, 74,  98, 0 };
static unsigned char s_align23[] = { 6, 30, 54, 78, 102, 0 };
static unsigned char s_align24[] = { 6, 28, 54, 80, 106, 0 };
static unsigned char s_align25[] = { 6, 32, 58, 84, 110, 0 };
static unsigned char s_align26[] = { 6, 30, 58, 86, 114, 0 };
static unsigned char s_align27[] = { 6, 34, 62, 90, 118, 0 };
static unsigned char s_align28[] = { 6, 26, 50, 74,  98, 122, 0 };
static unsigned char s_align29[] = { 6, 30, 54, 78, 102, 126, 0 };
static unsigned char s_align20[] = { 6, 26, 52, 78, 104, 130, 0 };
static unsigned char s_align31[] = { 6, 30, 56, 82, 108, 134, 0 };
static unsigned char s_align32[] = { 6, 34, 60, 86, 112, 138, 0 };
static unsigned char s_align33[] = { 6, 30, 58, 86, 114, 142, 0 };
static unsigned char s_align34[] = { 6, 34, 62, 90, 118, 146, 0 };
static unsigned char s_align35[] = { 6, 30, 54, 78, 102, 126, 150, 0 };
static unsigned char s_align36[] = { 6, 24, 50, 76, 102, 128, 154, 0 };
static unsigned char s_align37[] = { 6, 28, 54, 80, 106, 132, 158, 0 };
static unsigned char s_align38[] = { 6, 32, 58, 84, 110, 136, 162, 0 };
static unsigned char s_align39[] = { 6, 26, 54, 82, 110, 138, 166, 0 };
static unsigned char s_align40[] = { 6, 30, 58, 86, 114, 142, 170, 0 };

static unsigned char * s_align[ 40 ] = {
   s_align01,
   s_align02,
   s_align03,
   s_align04,
   s_align05,
   s_align06,
   s_align07,
   s_align08,
   s_align09,
   s_align00,
   s_align11,
   s_align12,
   s_align13,
   s_align14,
   s_align15,
   s_align16,
   s_align17,
   s_align18,
   s_align19,
   s_align10,
   s_align21,
   s_align22,
   s_align23,
   s_align24,
   s_align25,
   s_align26,
   s_align27,
   s_align28,
   s_align29,
   s_align20,
   s_align31,
   s_align32,
   s_align33,
   s_align34,
   s_align35,
   s_align36,
   s_align37,
   s_align38,
   s_align39,
   s_align40
};


#ifdef DEBUG_CODE
static int _qr_check_version_table( void )
{
   int iV, iL;
   unsigned int uiSumD, uiSumE;

   for( iV = 1; iV <= 40; iV++ )
   {
      const QRVERSION * pQRVersion = &s_version[ iV - 1 ];

      for( iL = 0; iL < 4; iL++ )
      {
         uiSumE = ( unsigned int ) ( pQRVersion->level[ iL ].block[ 0 ].uiCount ) * ( pQRVersion->level[ iL ].block[ 0 ].uiECC ) +
                  ( unsigned int ) ( pQRVersion->level[ iL ].block[ 1 ].uiCount ) * ( pQRVersion->level[ iL ].block[ 1 ].uiECC );
         if( uiSumE != pQRVersion->level[ iL ].uiECC )
            return iV + 10000 + ( iL + 1 ) * 1000;

         uiSumD = ( unsigned int ) ( pQRVersion->level[ iL ].block[ 0 ].uiCount ) * ( pQRVersion->level[ iL ].block[ 0 ].uiData ) +
                  ( unsigned int ) ( pQRVersion->level[ iL ].block[ 1 ].uiCount ) * ( pQRVersion->level[ iL ].block[ 1 ].uiData );
         if( uiSumD + uiSumE != pQRVersion->uiTotal )
            return iV + 20000 + ( iL + 1 ) * 1000;

         if( pQRVersion->level[ iL ].block[ 1 ].uiCount > 0 )
         {
            if( pQRVersion->level[ iL ].block[ 0 ].uiData > pQRVersion->level[ iL ].block[ 1 ].uiData )
               return iV + 30000 + ( iL + 1 ) * 1000;

            if( pQRVersion->level[ iL ].block[ 0 ].uiECC != pQRVersion->level[ iL ].block[ 1 ].uiECC )
               return iV + 40000 + ( iL + 1 ) * 1000;
         }
      }
   }
   return 0;
}

HB_FUNC( _QR_CHECK_VERSION_TABLE )
{
   hb_retni( _qr_check_version_table() );
}
#endif


static int _qr_version_crc( int iVersion )
{
   int i, iValue, iVersionRev;

   iVersionRev = 0;
   for( i = 0; i < 6; i++ )
   {
      iVersionRev <<= 1;
      if( iVersion & 1 )
         iVersionRev |= 1;
      iVersion >>= 1;
   }

   iValue = iVersionRev;
   for( i = 0; i < 6; i++ )
   {
      if( iValue & 1 )
         iValue ^= 0x149F; /* 0x149F - reversed 12-bit polynom */
      iValue >>= 1;
   }
   iValue <<= 6;
   iValue |= iVersionRev;
   return iValue; /* 18-bit return value */
}


static int _qr_format_crc( int iLevel, int iMask )
{
   int i, iValue, iRev;

   iRev = ( ( iLevel & 1 ) ? 0 : 2 ) | ( ( iLevel & 2 ) ? 1 : 0 );  /* 0->1, 1->0, 2->3, 3->2 + reverse bits */
   iRev |= ( ( iMask & 1 ) ? 16 : 0 ) | ( ( iMask & 2 ) ? 8 : 0 ) | ( ( iMask & 4 ) ? 4 : 0 ); /* reverse bits */

   iValue = iRev;
   for( i = 0; i < 5; i++ )
   {
      if( iValue & 1 )
         iValue ^= 0x765;  /* 0x765 - reversed 10-bit polynom */
      iValue >>= 1;
   }
   iValue <<= 5;
   iValue |= iRev;
   return iValue ^ 0x2415; /* 15-bit return value */
}


#ifdef DEBUG_CODE
HB_FUNC( _QR_VERSION_CRC )
{
   hb_retni( _qr_version_crc( hb_parni( 1 ) ) );
}


HB_FUNC( _QR_FORMAT_CRC )
{
   hb_retni( _qr_format_crc( hb_parni( 1 ), hb_parni( 2 ) ) );
}
#endif


static int _qr_versionlength( int iVersion )
{
   return iVersion * 4 + 17;
}


static int _qr_fixed( int iVersion, int iRow, int iCol )
{
   int iLength = _qr_versionlength( iVersion );
   const unsigned char * pi;

   /* position detection markers and version info */
   if( iRow < 9 && iCol < 9 )
      return 1;
   if( iRow < 9 && iCol >= iLength - 8 )
      return 1;
   if( iRow >= iLength - 8 && iCol < 9 )
      return 1;

   /* timing patterns */
   if( iRow == 6 || iCol == 6 )
      return 1;

   /* alignment patterns */
   pi = s_align[ iVersion - 1 ];
   for( ; *pi; pi++ )
   {
      const unsigned char * pj = s_align[ iVersion - 1 ];
      for( ; *pj; pj++ )
      {
         if( iRow - 2 <= ( int ) *pi && ( int ) *pi <= iRow + 2 &&
             iCol - 2 <= ( int ) *pj && ( int ) *pj <= iCol + 2 )
         {
            if( ( *pi == 6 && ( int ) *pj > iLength - 10 ) ||
                ( ( int ) *pi > iLength - 10 && *pj == 6 ) )
               break;

            return 1;
         }
      }
   }

   /* format info */
   if( iVersion >= 7 &&
       ( ( iCol < 6 && iRow >= iLength - 11 ) || ( iCol >= iLength - 11 && iRow < 6 ) ) )
      return 1;

   return 0;
}


static PHB_BITBUFFER _qr_interlace( PHB_BITBUFFER pData, unsigned char * pECC, int iVersion, int iLevel )
{
   const QRVERSION * pVersion = &s_version[ iVersion - 1 ];
   const QRLEVEL * pLevel = &( pVersion->level[ iLevel ] );
   PHB_BITBUFFER pRet;
   HB_BYTE * pDataBuf, * pRetBuf;
   unsigned int uiDst, uiSrc, uiPos, uiBlock;

   pRet = hb_bitbuffer_create();
   hb_bitbuffer_set( pRet, pVersion->uiTotal * 8, HB_FALSE ); /* Allocate */

   pRetBuf = hb_bitbuffer_buffer( pRet );
   pDataBuf = hb_bitbuffer_buffer( pData );

   uiDst = 0;
   for( uiPos = 0; uiPos < ( unsigned int ) pLevel->block[ 0 ].uiData || uiPos < ( unsigned int ) pLevel->block[ 1 ].uiData; uiPos++ )
   {
      uiSrc = 0;
      for( uiBlock = 0; uiBlock < ( unsigned int ) pLevel->block[ 0 ].uiCount; uiBlock++ )
      {
         if( uiPos < ( unsigned int ) pLevel->block[ 0 ].uiData )
         {
            pRetBuf[ uiDst++ ] = pDataBuf[ uiPos + uiSrc ];
         }
         uiSrc += pLevel->block[ 0 ].uiData;
      }
      if( pLevel->block[ 1 ].uiCount )
      {
         for( uiBlock = 0; uiBlock < ( unsigned int ) pLevel->block[ 1 ].uiCount; uiBlock++ )
         {
            pRetBuf[ uiDst++ ] = pDataBuf[ uiPos + uiSrc ];
            uiSrc += pLevel->block[ 1 ].uiData;
         }
      }
   }

   for( uiPos = 0; uiPos < ( unsigned int ) pLevel->block[ 0 ].uiECC; uiPos++ )
   {
      uiSrc = 0;
      for( uiBlock = 0; uiBlock < ( unsigned int ) pLevel->block[ 0 ].uiCount; uiBlock++ )
      {
         if( uiPos < ( unsigned int ) pLevel->block[ 0 ].uiECC )
         {
            pRetBuf[ uiDst++ ] = s_rev[ pECC[ uiPos + uiSrc ] ];
         }
         uiSrc += pLevel->block[ 0 ].uiECC;
      }
      if( pLevel->block[ 1 ].uiCount )
      {
         for( uiBlock = 0; uiBlock < ( unsigned int ) pLevel->block[ 1 ].uiCount; uiBlock++ )
         {
            pRetBuf[ uiDst++ ] = s_rev[ pECC[ uiPos + uiSrc ] ];
            uiSrc += pLevel->block[ 1 ].uiECC;
         }
      }
   }

#ifdef DEBUG_CODE
   if( uiDst != pVersion->uiTotal )
      HB_TRACE( HB_TR_ALWAYS, ( "ERROR!!! uiDst:%d  pVersion->uiTotal:%d", uiDst, pVersion->uiTotal ) );

   for( uiPos = 0; uiPos < pVersion->uiTotal; uiPos++ )
      HB_TRACE( HB_TR_ALWAYS, ( "interlaced:%3d %02X", ( int ) s_rev[ ( unsigned char ) pRetBuf[ uiPos ] ], ( int ) s_rev[ ( unsigned char ) pRetBuf[ uiPos ] ] ) );
#endif
   return pRet;
}


static int _qr_alphanumeric_no( char ch )
{
   if( '0' <= ch && ch <= '9' )
      return ch - '0';

   if( 'A' <= ch && ch <= 'Z' )
      return ch - 'A' + 10;

   switch( ch )
   {
      case ' ':
         return 36;
      case '$':
         return 37;
      case '%':
         return 38;
      case '*':
         return 39;
      case '+':
         return 40;
      case '-':
         return 41;
      case '.':
         return 42;
      case '/':
         return 43;
      case ':':
         return 44;
   }
   return -1;
}

static int _qr_cci_len( int iVersion, int iMode ) /* Character Count Indicator */
{
   if( iMode == 1 )
      return iVersion <= 9 ? 10 : ( iVersion <= 26 ? 12 : 14 );
   if( iMode == 2 )
      return iVersion <= 9 ? 9 : ( iVersion <= 26 ? 11 : 13 );
   if( iMode == 4 )
      return iVersion <= 9 ? 8 : 16;
   return 0;
}


static int _qr_dataencode( const char * szCode, HB_SIZE nSize, PHB_BITBUFFER pData, int iLevel )
{
   int i, iVersion, iMode;
   HB_ISIZ iLen, iDataLen, m;
   HB_SIZE n;

   /* Select encoding mode */
   iMode = 1;  /* 1=Numeric, 2=Alphanumeric, 4=8-bit, 8=Kanji. Not modes: 0=terminator, 3=Structured append, 7=ECI, 5=FNC1(1), 9=FNC1(2)*/
   for( n = 0; n < nSize; n++ )
   {
      char ch = szCode[ n ];
      if( '0' <= ch && ch <= '9' )
      {
      }
      else if( ( 'A' <= ch && ch <= 'Z' ) ||
               ch == ' ' || ch == '$' || ch == '%' || ch == '*' || ch == '+' || ch == '-' || ch == '.' || ch == '/' || ch == ':' )
         iMode = 2;
      else
      {
         iMode = 4;
         break;
      }
   }

   /* Calculate data length in bits */
   if( iMode == 1 )
      iLen = ( nSize / 3 ) * 10 + ( ( nSize % 3 ) == 0 ? 0 : ( ( nSize % 3 ) == 1 ? 4 : 7 ) );
   else if( iMode == 2 )
      iLen = ( nSize / 2 ) * 11 + ( nSize % 2 ) * 6;
   else /* if( iMode == 4 ) */
      iLen = nSize * 8;

   iLen += 4; /* Mode indicator length */

   /* Select version */
   iDataLen = 0; /* to pacify warning with some C compilers (MSVS 2010) */
   iVersion = 0; /* to pacify warning */
   for( i = 1; i <= 40; i++ )
   {
      iDataLen = ( int ) ( s_version[ i - 1 ].uiTotal - s_version[ i - 1 ].level[ iLevel ].uiECC );
      if( iDataLen * 8 >= iLen + _qr_cci_len( i, iMode ) )
      {
         iVersion = i;
         break;
      }
   }
   if( i > 40 )
      return 0; /* Too large */

#ifdef DEBUG_CODE
   HB_TRACE( HB_TR_ALWAYS, ( "iMode:%d iLen:%" HB_PFS "d iDataLen:%d iVersion:%d", iMode, iLen, iDataLen, iVersion ) );
#endif

   /* Encode */
   hb_bitbuffer_cat_int_rev( pData, iMode, 4 );
   hb_bitbuffer_cat_int_rev( pData, ( int ) nSize, _qr_cci_len( iVersion, iMode ) );
   if( iMode == 1 )
   {
      for( n = 0; n + 2 < nSize; n += 3 )
      {
         hb_bitbuffer_cat_int_rev( pData, 100 * ( int )( unsigned char ) ( szCode[ n     ] - '0' ) +
                                           10 * ( int )( unsigned char ) ( szCode[ n + 1 ] - '0' ) +
                                                ( int )( unsigned char ) ( szCode[ n + 2 ] - '0' ), 10 );
      }
      if( n + 1 == nSize )
         hb_bitbuffer_cat_int_rev( pData, ( int )( unsigned char ) ( szCode[ n ] - '0' ), 4 );
      else
         hb_bitbuffer_cat_int_rev( pData, 10 * ( int )( unsigned char ) ( szCode[ n     ] - '0' ) +
                                               ( int )( unsigned char ) ( szCode[ n + 1 ] - '0' ), 7 );
   }
   else if( iMode == 2 )
   {
      for( n = 0; n + 1 < nSize; n += 2 )
      {
         hb_bitbuffer_cat_int_rev( pData, 45 * _qr_alphanumeric_no( szCode[ n ] ) + _qr_alphanumeric_no( szCode[ n + 1 ] ), 11 );
      }
      if( n != nSize )
      {
         hb_bitbuffer_cat_int_rev( pData, _qr_alphanumeric_no( szCode[ n ] ), 6 );
      }
   }
   else if( iMode == 4 )
   {
      for( n = 0; n < nSize; n++ )
         hb_bitbuffer_cat_int_rev( pData, ( int ) ( unsigned char ) szCode[ n ], 8 );
   }

   /* Terminator */
   if( iDataLen * 8 >= ( int ) hb_bitbuffer_len( pData ) + 4 )
      hb_bitbuffer_cat_int_rev( pData, 0, 4 );

   /* Padding */
   if( hb_bitbuffer_len( pData ) & 7 )
      hb_bitbuffer_cat_int_rev( pData, 0, 8 - ( hb_bitbuffer_len( pData ) & 7 ) );

   iLen = iDataLen - hb_bitbuffer_len( pData ) / 8;
   for( m = 0; m < iLen; m++ )
   {
      hb_bitbuffer_cat_int_rev( pData, ( m & 1 ) ? 0x11 : 0xEC, 8 );
   }

#ifdef DEBUG_CODE
   for( m = 0; m < iDataLen; m++ )
      HB_TRACE( HB_TR_ALWAYS, ( "data:%3d %02X", s_rev[ *( hb_bitbuffer_buffer( pData ) + m ) ], s_rev[ *( hb_bitbuffer_buffer( pData ) + m ) ] ) );
#endif
   return iVersion;
}


static void _reed_solomon_encode( unsigned char * pData, int iDataLen, unsigned char * pECC, int iECCLen, int * pPoly, int * pExp, int * pLog, int iMod )
{
   int i, j;

   for( i = 0; i < iECCLen; i++ )
      pECC[ i ] = 0;

   for( i = 0; i < iDataLen; i++ )
   {
      unsigned char iM = s_rev[ pData[ i ] ] ^ pECC[ iECCLen - 1 ];

      for( j = iECCLen - 1; j > 0; j-- )
      {
         if( iM && pPoly[ j ] )
            pECC[ j ] = ( unsigned char ) ( pECC[ j - 1 ] ^ pExp[ ( pLog[ iM ] + pLog[ pPoly[ j ] ] ) % iMod ] );
         else
            pECC[ j ] = pECC[ j - 1 ];
      }
      if( iM && pPoly[ 0 ] )
         pECC[ 0 ] = ( unsigned char ) ( pExp[ ( pLog[ iM ] + pLog[ pPoly[ 0 ] ] ) % iMod ] );
      else
         pECC[ 0 ] = 0;
   }
}


static unsigned char * _qr_checksum( PHB_BITBUFFER pData, int iVersion, int iLevel )
{
   const QRVERSION * pVersion = &s_version[ iVersion - 1 ];
   const QRLEVEL * pLevel = &( pVersion->level[ iLevel ] );
   HB_BYTE * pDataBuf = hb_bitbuffer_buffer( pData );
   int * pPoly, * pExp, * pLog;
   int i, j, iBits, iMod, iPoly, iECCLen, iIndex;
   unsigned char * pECC, * pECCPtr, ui, ui2;

   /* Init Galois field. Parameters: iPoly */
   iPoly = 0x11D;

   j = iPoly;
   for( iBits = 0; j > 1; iBits++ )
      j >>= 1;

   iMod = ( 1 << iBits ) - 1;
   pExp = ( int * ) hb_xgrab( sizeof( int ) * iMod );          /* exponent function */
   pLog = ( int * ) hb_xgrab( sizeof( int ) * ( iMod + 1 ) );  /* logarithm function */
   j = 1;
   pLog[ 0 ] = iMod;
   for( i = 0; i < iMod; i++ )
   {
      pExp[ i ] = j;
      pLog[ j ] = i;
      j <<= 1;
      if( j & ( 1 << iBits ) )
         j ^= iPoly;
   }

   /* Init Reed-Solomon encode. Parameters: iECCLen, iIndex */
   iECCLen = pLevel->block[ 0 ].uiECC;
   iIndex = 0; /* why this parameter is different from DataMatrix ??? */

   pPoly = ( int * ) hb_xgrab( sizeof( int ) * ( iECCLen + 1 ) );
   pPoly[ 0 ] = 1;
   for( i = 1; i <= iECCLen; i++ )
   {
      pPoly[ i ] = 1;
      for( j = i - 1; j > 0; j-- )
      {
         if( pPoly[ j ] )
            pPoly[ j ] = pExp[ ( pLog[ pPoly[ j ] ] + iIndex ) % iMod ];

         pPoly[ j ] ^= pPoly[ j - 1 ];
      }
      pPoly[ 0 ] = pExp[ ( pLog[ pPoly[ 0 ] ] + iIndex ) % iMod ];
      iIndex++;
   }

#ifdef DEBUG_CODE
   for( i = 0; i <= iECCLen; i++ )
      HB_TRACE( HB_TR_ALWAYS, ( "POLY[%3d %02X]:%3d %02X", i, i, pPoly[ i ], pPoly[ i ] ) );
#endif

   pECC = ( unsigned char * ) hb_xgrab( pLevel->block[ 0 ].uiECC * ( pLevel->block[ 0 ].uiCount + pLevel->block[ 1 ].uiCount ) );
   pECCPtr = pECC;

   /* Divide data into blocks and do Reed-Solomon encoding for each block */
   for( ui = 0; ui < pLevel->block[ 0 ].uiCount; ui++ )
   {
      /* Calculate Reed-Solomon ECC for one block */
      _reed_solomon_encode( pDataBuf, pLevel->block[ 0 ].uiData, pECCPtr, iECCLen, pPoly, pExp, pLog, iMod );
      pDataBuf += pLevel->block[ 0 ].uiData;
      for( i = 0; i < iECCLen / 2; i++ )
      {
         ui2 = pECCPtr[ i ];
         pECCPtr[ i ] = pECCPtr[ iECCLen - 1 - i ];
         pECCPtr[ iECCLen - 1 - i ] = ui2;
      }
      pECCPtr += iECCLen;
   }
   for( ui = 0; ui < pLevel->block[ 1 ].uiCount; ui++ )
   {
      /* Calculate Reed-Solomon ECC for one block */
      _reed_solomon_encode( pDataBuf, pLevel->block[ 1 ].uiData, pECCPtr, iECCLen, pPoly, pExp, pLog, iMod );
      pDataBuf += pLevel->block[ 1 ].uiData;
      for( i = 0; i < iECCLen / 2; i++ )
      {
         ui2 = pECCPtr[ i ];
         pECCPtr[ i ] = pECCPtr[ iECCLen - 1 - i ];
         pECCPtr[ iECCLen - 1 - i ] = ui2;
      }
      pECCPtr += iECCLen;
   }

   hb_xfree( pExp );
   hb_xfree( pLog );
   hb_xfree( pPoly );

#ifdef DEBUG_CODE
   iECCLen = pLevel->block[ 0 ].uiECC * ( pLevel->block[ 0 ].uiCount + pLevel->block[ 1 ].uiCount );
   for( i = 0; i < iECCLen; i++ )
      HB_TRACE( HB_TR_ALWAYS, ( "ecc:%3d %02X", ( int ) ( unsigned char ) pECC[ i ], ( int ) ( unsigned char ) pECC[ i ] ) );
#endif
   return pECC;
}


static void _qr_draw( PHB_BITBUFFER pBits, PHB_BITBUFFER pCWBits, int iVersion )
{
   int i, j, iLength;
   const unsigned char * pi;

   HB_SYMBOL_UNUSED( pCWBits );

   iLength = _qr_versionlength( iVersion );

   /* draw position detection markers */
   for( i = 0; i < 7; i++ )
   {
      hb_bitbuffer_set( pBits, i, 1 );
      hb_bitbuffer_set( pBits, i + 6 * iLength, 1 );
      hb_bitbuffer_set( pBits, i * iLength, 1 );
      hb_bitbuffer_set( pBits, i * iLength + 6, 1 );

      hb_bitbuffer_set( pBits, i + ( iLength - 7 ) * iLength, 1 );
      hb_bitbuffer_set( pBits, i + ( iLength - 1 ) * iLength, 1 );
      hb_bitbuffer_set( pBits, ( iLength - 7 + i ) * iLength, 1 );
      hb_bitbuffer_set( pBits, ( iLength - 7 + i ) * iLength + 6, 1 );

      hb_bitbuffer_set( pBits, iLength - 7 + i, 1 );
      hb_bitbuffer_set( pBits, iLength - 7 + i + 6 * iLength, 1 );
      hb_bitbuffer_set( pBits, iLength - 7 + i * iLength, 1 );
      hb_bitbuffer_set( pBits, iLength - 1 + i * iLength, 1 );
   }
   for( i = 2; i < 5; i++ )
   {
      for( j = 2; j < 5; j++ )
      {
         hb_bitbuffer_set( pBits, i * iLength + j, 1 );
         hb_bitbuffer_set( pBits, i * iLength + j + iLength - 7, 1 );
         hb_bitbuffer_set( pBits, ( i + iLength - 7 ) * iLength + j, 1 );
      }
   }
   /* draw timing patterns */
   for( i = 8; i < iLength - 8; i += 2 )
   {
      hb_bitbuffer_set( pBits, i + 6 * iLength, 1 );
      hb_bitbuffer_set( pBits, i * iLength + 6, 1 );
   }
   /* draw alignment patterns */
   pi = s_align[ iVersion - 1 ];
   for( ; *pi; pi++ )
   {
      const unsigned char * pj = s_align[ iVersion - 1 ];
      for( ; *pj; pj++ )
      {
         if( ( *pi > 10 && *pi < iLength - 10 ) ||
             ( *pj > 10 && *pj < iLength - 10 ) ||
             ( *pi > 10 && *pj > 10 ))
         {
            hb_bitbuffer_set( pBits, iLength * *pi + *pj - 1, 0 );
            hb_bitbuffer_set( pBits, iLength * *pi + *pj + 1, 0 );
            hb_bitbuffer_set( pBits, iLength * ( *pi - 1 ) + *pj, 0 );
            hb_bitbuffer_set( pBits, iLength * ( *pi + 1 ) + *pj, 0 );

            hb_bitbuffer_set( pBits, iLength * *pi + *pj, 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi - 2 ) + *pj - 2, 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi - 2 ) + *pj - 1, 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi - 2 ) + *pj    , 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi - 2 ) + *pj + 1, 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi - 2 ) + *pj + 2, 1 );
            hb_bitbuffer_set( pBits, ( *pi - 1 ) * iLength + *pj - 2, 1 );
            hb_bitbuffer_set( pBits, *pi * iLength + *pj - 2, 1 );
            hb_bitbuffer_set( pBits, ( *pi + 1 ) * iLength + *pj - 2, 1 );
            hb_bitbuffer_set( pBits, ( *pi - 1 ) * iLength + *pj + 2, 1 );
            hb_bitbuffer_set( pBits, *pi * iLength + *pj + 2, 1 );
            hb_bitbuffer_set( pBits, ( *pi + 1 ) * iLength + *pj + 2, 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi + 2 ) + *pj - 2, 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi + 2 ) + *pj - 1, 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi + 2 ) + *pj    , 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi + 2 ) + *pj + 1, 1 );
            hb_bitbuffer_set( pBits, iLength * ( *pi + 2 ) + *pj + 2, 1 );
         }
      }
   }

   /* Dark module */
   hb_bitbuffer_set( pBits, 8 + iLength * ( iLength - 8 ), 1 );


   /* Draw data. Note: pCWBits == NULL is used only for debugging */
   if( pCWBits )
   {
      int no, up, right;

      i = j = iLength - 1;
      right = 1;
      up = 1;
      no = 0;
      while( i >= 0 && j >= 0 )
      {
         if( ! _qr_fixed( iVersion, i, j ) )
            hb_bitbuffer_set( pBits, i * iLength + j, hb_bitbuffer_get( pCWBits, no++ ) );

         if( right )
         {
            j--;
         }
         else
         {
            if( up )
            {
               if( i > 0 )
               {
                  i--;
                  j++;
               }
               else
               {
                  up = 0;
                  j--;
               }
            }
            else
            {
               if( i < iLength - 1 )
               {
                  i++;
                  j++;
               }
               else
               {
                  up = 1;
                  j--;
               }
            }
         }
         right = ! right;
         if( j == 6 )
            j--;
      }
   }
}


static int _qr_penalty( PHB_BITBUFFER pBits, int iVersion )
{
   int i, j, k, iPenalty = 0, iLen = _qr_versionlength( iVersion );
   HB_BOOL bBit;

   /* 1. Same color modules in row/column */
   for( i = 0; i < iLen; i++ )
   {
      /* Row */
      HB_BOOL bBitLast = hb_bitbuffer_get( pBits, i * iLen );
      k = 1;
      for( j = 1; j < iLen; j++ )
      {
         bBit = hb_bitbuffer_get( pBits, i * iLen + j );
         if( bBit != bBitLast )
         {
            if( k >= 5 )
               iPenalty += 3 + ( k - 5 );

            bBitLast = bBit;
            k = 1;
         }
         else
            k++;
      }
      if( k >= 5 )
         iPenalty = 3 + ( k - 5 );

      /* Col */
      bBitLast = hb_bitbuffer_get( pBits, i );
      k = 1;
      for( j = 1; j < iLen; j++ )
      {
         bBit = hb_bitbuffer_get( pBits, i + iLen * j );
         if( bBit != bBitLast )
         {
            if( k >= 5 )
               iPenalty += 3 + ( k - 5 );

            bBitLast = bBit;
            k = 1;
         }
         else
            k++;
      }
      if( k >= 5 )
         iPenalty = 3 + ( k - 5 );
   }

   /* 2. Block of same color modules */
   /* Instead of looking for non-overlapped MxN block, we can search for 2x2 overlapping blocks,
      penalty value is the same for both of these methods */
   for( i = 0; i < iLen - 1; i++ )
   {
      for( j = 0; j < iLen - 1; j++ )
      {
         bBit = hb_bitbuffer_get( pBits, i * iLen + j );
         if( hb_bitbuffer_get( pBits, i * iLen + j + 1 ) == bBit &&
             hb_bitbuffer_get( pBits, ( i + 1 ) * iLen + j ) == bBit &&
             hb_bitbuffer_get( pBits, ( i + 1 ) * iLen + j + 1 ) == bBit )
            iPenalty += 3;
      }
   }

   /* 3. Black-White-Black-Black-Black-White-Black modules */
   for( i = 0; i < iLen - 6; i++ )
   {
      for( j = 0; j < iLen - 6; j++ )
      {
         if(   hb_bitbuffer_get( pBits, i * iLen + j     ) &&
             ! hb_bitbuffer_get( pBits, i * iLen + j + 1 ) &&
               hb_bitbuffer_get( pBits, i * iLen + j + 2 ) &&
               hb_bitbuffer_get( pBits, i * iLen + j + 3 ) &&
               hb_bitbuffer_get( pBits, i * iLen + j + 4 ) &&
             ! hb_bitbuffer_get( pBits, i * iLen + j + 5 ) &&
               hb_bitbuffer_get( pBits, i * iLen + j + 6 ) )
            iPenalty += 40;

         if(   hb_bitbuffer_get( pBits, i         * iLen + j ) &&
             ! hb_bitbuffer_get( pBits, ( i + 1 ) * iLen + j ) &&
               hb_bitbuffer_get( pBits, ( i + 2 ) * iLen + j ) &&
               hb_bitbuffer_get( pBits, ( i + 3 ) * iLen + j ) &&
               hb_bitbuffer_get( pBits, ( i + 4 ) * iLen + j ) &&
             ! hb_bitbuffer_get( pBits, ( i + 5 ) * iLen + j ) &&
               hb_bitbuffer_get( pBits, ( i + 6 ) * iLen + j ) )
            iPenalty += 40;
      }
   }

   /* 4. Proportion of color modules */
   k = 0;
   for( i = 0; i < iLen; i++ )
      for( j = 0; j < iLen; j++ )
         if( hb_bitbuffer_get( pBits, i * iLen + j ) )
            k++;
   k = k * 100 / ( iLen * iLen );
   if( k < 50 )
      k = 100 - k;
   iPenalty += ( k / 5 ) * 10;

   return iPenalty;
}


static void _qr_mask_pattern( PHB_BITBUFFER pBits, int iVersion, int iMask )
{
   int i, j, k = 0, iLen = _qr_versionlength( iVersion );

   for( i = 0; i < iLen; i++ )
   {
      for( j = 0; j < iLen; j++ )
      {
         if( ! _qr_fixed( iVersion, i, j ) )
         {
            switch( iMask )
            {
               case 0:
                  k = ( i + j ) % 2 == 0;
                  break;
               case 1:
                  k = i % 2 == 0;
                  break;
               case 2:
                  k = j % 3 == 0;
                  break;
               case 3:
                  k = ( i + j ) % 3 == 0;
                  break;
               case 4:
                  k = ( i / 2 + j / 3 ) % 2 == 0;
                  break;
               case 5:
                  k = ( i * j ) % 2 + ( i * j ) % 3 == 0;
                  break;
               case 6:
                  k = ( ( i * j ) % 2 + ( i * j ) % 3 ) % 2 == 0;
                  break;
               case 7:
                  k = ( ( i * j ) % 3 + ( i + j ) % 2 ) % 2 == 0;
                  break;
            }
            if( k )
               hb_bitbuffer_not( pBits, i * iLen + j );
         }
      }
   }
}


static int _qr_mask( PHB_BITBUFFER pBits, int iVersion )
{
   int i, iPenaltyMin = 0, iMaskMin = 0;

   for( i = 0; i < 8; i++ )
   {
      int iPenalty;

      _qr_mask_pattern( pBits, iVersion, i );
      iPenalty = _qr_penalty( pBits, iVersion );
#ifdef DEBUG_CODE
      HB_TRACE( HB_TR_ALWAYS, ( "mask:%d penalty:%d", i, iPenalty ) );
#endif
      if( i == 0 || iPenalty < iPenaltyMin )
      {
         iPenaltyMin = iPenalty;
         iMaskMin = i;
      }
      _qr_mask_pattern( pBits, iVersion, i );
   }
#ifdef DEBUG_CODE
   HB_TRACE( HB_TR_ALWAYS, ( "mask:%d", iMaskMin ) );
   #if 0
   iMaskMin = 0;
   #endif
   HB_TRACE( HB_TR_ALWAYS, ( "mask applied:%d", iMaskMin ) );
#endif
   _qr_mask_pattern( pBits, iVersion, iMaskMin );
   return iMaskMin;
}


static void _qr_draw_version_format( PHB_BITBUFFER pBits, int iVersion, int iLevel, int iMask )
{
   int i, iCRC, iLen = _qr_versionlength( iVersion );

   if( iVersion >= 7 )
   {
      iCRC = _qr_version_crc( iVersion );
      for( i = 0; i < 18; i++ )
      {
         if( iCRC & ( 1 << 17 ) )
         {
            hb_bitbuffer_set( pBits, iLen - 11 + ( i % 3 ) + iLen * ( i / 3 ), 1 );
            hb_bitbuffer_set( pBits, ( iLen - 11 + ( i % 3 ) ) * iLen + ( i / 3 ), 1 );
         }
         iCRC <<= 1;
      }
   }
   iCRC = _qr_format_crc( iLevel, iMask );
   for( i = 0; i < 15; i++ )
   {
      if( iCRC & ( 1 << 14 ) )
      {
         /* Top left */
         if( i <= 7 )
            hb_bitbuffer_set( pBits, ( i + ( i >= 6 ? 1 : 0 ) ) * iLen + 8, 1 );
         else
            hb_bitbuffer_set( pBits, ( 14 - i ) + ( i == 8 ? 1 : 0 ) + iLen * 8, 1 );

         /* Top right and bottom left */
         if( i <= 7 )
            hb_bitbuffer_set( pBits, iLen - 1 - i + iLen * 8, 1 );
         else
            hb_bitbuffer_set( pBits, ( iLen - 15 + i ) * iLen + 8, 1 );
      }
      iCRC <<= 1;
   }

#if 0
   /* _qr_fixed() test code */
   for( i = 0; i < iLen; i++ )
   {
      int j;
      for( j = 0; j < iLen; j++ )
         hb_bitbuffer_set( pBits, i * iLen + j, _qr_fixed( iVersion, i, j ) );
   }
#endif
}


PHB_ZEBRA hb_zebra_create_qrcode( const char * szCode, HB_SIZE nLen, int iFlags )
{
   PHB_ZEBRA       pZebra;
   PHB_BITBUFFER   pData, pFinal;
   unsigned char * pECC;
   int             iVersion, iLevel, iMask;

   pZebra = hb_zebra_create();
   pZebra->iType = HB_ZEBRA_TYPE_QRCODE;

   if( nLen > 7089 )
   {
      pZebra->iError = HB_ZEBRA_ERROR_TOOLARGE;
      return pZebra;
   }

   switch( iFlags & HB_ZEBRA_FLAG_QR_LEVEL_MASK )
   {
      case HB_ZEBRA_FLAG_QR_LEVEL_M:
         iLevel = 1;
         break;
      case HB_ZEBRA_FLAG_QR_LEVEL_Q:
         iLevel = 2;
         break;
      case HB_ZEBRA_FLAG_QR_LEVEL_H:
         iLevel = 3;
         break;
      default:
         iLevel = 0;
         break;
   }

#ifdef DEBUG_CODE
   HB_TRACE( HB_TR_ALWAYS, ( "qr1 iLevel:%d", iLevel ) );
#endif

   pData = hb_bitbuffer_create();
   iVersion = _qr_dataencode( szCode, nLen, pData, iLevel );

#ifdef DEBUG_CODE
   HB_TRACE( HB_TR_ALWAYS, ( "qr3 iVersion:%d", iVersion ) );
#endif

   if( iVersion == 0 )
   {
      hb_bitbuffer_destroy( pData );
      pZebra->iError = HB_ZEBRA_ERROR_TOOLARGE;
      return pZebra;
   }

   pZebra->iCol = _qr_versionlength( iVersion );

   pZebra->szCode = hb_strdup( szCode );

   pECC = _qr_checksum( pData, iVersion, iLevel );

   pFinal = _qr_interlace( pData, pECC, iVersion, iLevel );
   hb_bitbuffer_destroy( pData );
   hb_xfree( pECC );

   pZebra->pBits = hb_bitbuffer_create();
   _qr_draw( pZebra->pBits, pFinal, iVersion );
   hb_bitbuffer_destroy( pFinal );

   iMask = _qr_mask( pZebra->pBits, iVersion );

   _qr_draw_version_format( pZebra->pBits, iVersion, iLevel, iMask );
   return pZebra;
}


HB_FUNC( HB_ZEBRA_CREATE_QRCODE )
{
   PHB_ITEM pItem = hb_param( 1, HB_IT_STRING );

   if( pItem )
      hb_zebra_ret( hb_zebra_create_qrcode( hb_itemGetCPtr( pItem ), hb_itemGetCLen( pItem ), hb_parni( 2 ) ) );
   else
      hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}
