/* dotcode.c - Handles DotCode */

/*
    libzint - the open source barcode library
    Copyright (C) 2017-2020 Robin Stuart <rstuart114@gmail.com>

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in the
       documentation and/or other materials provided with the distribution.
    3. Neither the name of the project nor the names of its contributors
       may be used to endorse or promote products derived from this software
       without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.
 */
/* vim: set ts=4 sw=4 et : */

/*
 * Attempts to encode DotCode according to AIMD013 Rev 1.34a, dated Feb 19, 2009
 * Incorporating suggestions from Terry Burton at BWIPP
 */

#include <stdio.h>
#include <string.h>
#include <math.h>
#ifndef _MSC_VER
#include <stdint.h>
#else
#include "ms_stdint.h"
#include <malloc.h>
#endif
#include "common.h"
#include "gs1.h"

#define GF 113
#define PM 3
#define SCORE_UNLIT_EDGE    -99999

/* DotCode symbol character dot patterns, from Annex C */
static const unsigned short int dot_patterns[113] = {
    0x155, 0x0ab, 0x0ad, 0x0b5, 0x0d5, 0x156, 0x15a, 0x16a, 0x1aa, 0x0ae,
    0x0b6, 0x0ba, 0x0d6, 0x0da, 0x0ea, 0x12b, 0x12d, 0x135, 0x14b, 0x14d,
    0x153, 0x159, 0x165, 0x169, 0x195, 0x1a5, 0x1a9, 0x057, 0x05b, 0x05d,
    0x06b, 0x06d, 0x075, 0x097, 0x09b, 0x09d, 0x0a7, 0x0b3, 0x0b9, 0x0cb,
    0x0cd, 0x0d3, 0x0d9, 0x0e5, 0x0e9, 0x12e, 0x136, 0x13a, 0x14e, 0x15c,
    0x166, 0x16c, 0x172, 0x174, 0x196, 0x19a, 0x1a6, 0x1ac, 0x1b2, 0x1b4,
    0x1ca, 0x1d2, 0x1d4, 0x05e, 0x06e, 0x076, 0x07a, 0x09e, 0x0bc, 0x0ce,
    0x0dc, 0x0e6, 0x0ec, 0x0f2, 0x0f4, 0x117, 0x11b, 0x11d, 0x127, 0x133,
    0x139, 0x147, 0x163, 0x171, 0x18b, 0x18d, 0x193, 0x199, 0x1a3, 0x1b1,
    0x1c5, 0x1c9, 0x1d1, 0x02f, 0x037, 0x03b, 0x03d, 0x04f, 0x067, 0x073,
    0x079, 0x08f, 0x0c7, 0x0e3, 0x0f1, 0x11e, 0x13c, 0x178, 0x18e, 0x19c,
    0x1b8, 0x1c6, 0x1cc
};

static int get_dot(char Dots[], const int Hgt, const int Wid, const int x, const int y) {
    int retval = 0;

    if ((x >= 0) && (x < Wid) && (y >= 0) && (y < Hgt)) {
        if (Dots[(y * Wid) + x] == '1') {
            retval = 1;
        }
    }

    return retval;
}

static int clr_col(char *Dots, const int Hgt, const int Wid, const int x) {
    int y;
    for (y = x & 1; y < Hgt; y += 2) {
        if (get_dot(Dots, Hgt, Wid, x, y)) {
            return 0;
        }
    }

    return 1;
}

static int clr_row(char *Dots, const int Hgt, const int Wid, const int y) {
    int x;
    for (x = y & 1; x < Wid; x += 2) {
        if (get_dot(Dots, Hgt, Wid, x, y)) {
            return 0;
        }
    }

    return 1;
}

/* Dot pattern scoring routine from Annex A */
static int score_array(char Dots[], int Hgt, int Wid) {
    int x, y, worstedge, first, last, sum;
    int penalty_local = 0;
    int penalty = 0;

    // first, guard against "pathelogical" gaps in the array
    if (Hgt & 1) {
        if (Hgt < 12) {
            sum = 0;
            for (x = 1; x < Wid - 1; x++) {
                if (!(clr_col(Dots, Hgt, Wid, x))) {
                    sum = 0;
                    if (penalty_local) {
                        penalty += penalty_local;
                        penalty_local = 0;
                    }
                } else {
                    sum++;
                    if (sum == 1) {
                        penalty_local = Hgt;
                    } else {
                        penalty_local *= Hgt;
                    }
                }
            }
        }
    } else {
        if (Wid < 12) {
            sum = 0;
            for (y = 1; y < Hgt - 1; y++) {
                if (!(clr_row(Dots, Hgt, Wid, y))) {
                    sum = 0;
                    if (penalty_local) {
                        penalty += penalty_local;
                        penalty_local = 0;
                    }
                } else {
                    sum++;
                    if (sum == 1) {
                        penalty_local = Wid;
                    } else {
                        penalty_local *= Wid;
                    }
                }
            }
        }
    }

    sum = 0;
    first = -1;
    last = -1;

    // across the top edge, count printed dots and measure their extent
    for (x = 0; x < Wid; x += 2)
        if (get_dot(Dots, Hgt, Wid, x, 0)) {
            if (first < 0) {
                first = x;
            }
            last = x;
            sum++;
        }
    worstedge = sum + last - first;
    worstedge *= Hgt;

    if (sum == 0) {
        return SCORE_UNLIT_EDGE;      // guard against empty top edge
    }

    sum = 0;
    first = -1;
    last = -1;

    //across the bottom edge, ditto
    for (x = Wid & 1; x < Wid; x += 2)
        if (get_dot(Dots, Hgt, Wid, x, Hgt - 1)) {
            if (first < 0) {
                first = x;
            }
            last = x;
            sum++;
        }
    sum += last - first;
    sum *= Hgt;
    if (sum < worstedge) {
        worstedge = sum;
    }

    if (sum == 0) {
        return SCORE_UNLIT_EDGE;      // guard against empty bottom edge
    }

    sum = 0;
    first = -1;
    last = -1;

    //down the left edge, ditto
    for (y = 0; y < Hgt; y += 2)
        if (get_dot(Dots, Hgt, Wid, 0, y)) {
            if (first < 0) {
                first = y;
            }
            last = y;
            sum++;
        }
    sum += last - first;
    sum *= Wid;
    if (sum < worstedge) {
        worstedge = sum;
    }

    if (sum == 0) {
        return SCORE_UNLIT_EDGE;      // guard against empty left edge
    }

    sum = 0;
    first = -1;
    last = -1;

    //down the right edge, ditto
    for (y = Hgt & 1; y < Hgt; y += 2)
        if (get_dot(Dots, Hgt, Wid, Wid - 1, y)) {
            if (first < 0) {
                first = y;
            }
            last = y;
            sum++;
        }
    sum += last - first;
    sum *= Wid;
    if (sum < worstedge) {
        worstedge = sum;
    }

    if (sum == 0) {
        return SCORE_UNLIT_EDGE;      // guard against empty right edge
    }

    // throughout the array, count the # of unprinted 5-somes (cross patterns)
    // plus the # of printed dots surrounded by 8 unprinted neighbors
    sum = 0;
    for (y = 0; y < Hgt; y++) {
        for (x = y & 1; x < Wid; x += 2) {
            if ((!get_dot(Dots, Hgt, Wid, x - 1, y - 1))
                    && (!get_dot(Dots, Hgt, Wid, x + 1, y - 1))
                    && (!get_dot(Dots, Hgt, Wid, x - 1, y + 1))
                    && (!get_dot(Dots, Hgt, Wid, x + 1, y + 1))
                    && ((!get_dot(Dots, Hgt, Wid, x, y))
                    || ((!get_dot(Dots, Hgt, Wid, x - 2, y))
                    && (!get_dot(Dots, Hgt, Wid, x, y - 2))
                    && (!get_dot(Dots, Hgt, Wid, x + 2, y))
                    && (!get_dot(Dots, Hgt, Wid, x, y + 2))))) {
                sum++;
            }
        }
    }

    return (worstedge - sum * sum - penalty);
}

//-------------------------------------------------------------------------
// "rsencode(nd,nc)" adds "nc" R-S check words to "nd" data words in wd[]
// employing Galois Field GF, where GF is prime, with a prime modulus of PM
//-------------------------------------------------------------------------

static void rsencode(int nd, int nc, unsigned char *wd) {
    int i, j, k, nw, start, step, root[GF], c[GF];

    // Start by generating "nc" roots (antilogs):
    root[0] = 1;
    for (i = 1; (i <= nc) && (i < GF); i++)
        root[i] = (PM * root[i - 1]) % GF;

    // Here we compute how many interleaved R-S blocks will be needed
    nw = nd + nc;
    step = (nw + GF - 2) / (GF - 1);

    // ...& then for each such block:
    for (start = 0; start < step; start++) {
        int ND = (nd - start + step - 1) / step, NW = (nw - start + step - 1) / step, NC = NW - ND;

        // first compute the generator polynomial "c" of order "NC":
        for (i = 1; i <= NC; i++)
            c[i] = 0;
        c[0] = 1;

        for (i = 1; i <= NC; i++) {
            for (j = NC; j >= 1; j--) {
                c[j] = (GF + c[j] - (root[i] * c[j - 1]) % GF) % GF;
            }
        }

        // & then compute the corresponding checkword values into wd[]
        // ... (a) starting at wd[start] & (b) stepping by step
        for (i = ND; i < NW; i++)
            wd[start + i * step] = 0;
        for (i = 0; i < ND; i++) {
            k = (wd[start + i * step] + wd[start + ND * step]) % GF;
            for (j = 0; j < NC - 1; j++) {
                wd[start + (ND + j) * step] = (GF - ((c[j + 1] * k) % GF) + wd[start + (ND + j + 1) * step]) % GF;
            }
            wd[start + (ND + NC - 1) * step] = (GF - ((c[NC] * k) % GF)) % GF;
        }
        for (i = ND; i < NW; i++)
            wd[start + i * step] = (GF - wd[start + i * step]) % GF;
    }
}

/* Check if the next character is directly encodable in code set A (Annex F.II.D) */
static int datum_a(const unsigned char source[], int position, int length) {
    int retval = 0;

    if (position < length) {
        if (source[position] <= 95) {
            retval = 1;
        }
    }

    return retval;
}

/* Check if the next character is directly encodable in code set B (Annex F.II.D) */
static int datum_b(const unsigned char source[], int position, int length) {
    int retval = 0;

    if (position < length) {
        if ((source[position] >= 32) && (source[position] <= 127)) {
            retval = 1;
        }

        switch (source[position]) {
            case 9: // HT
            case 28: // FS
            case 29: // GS
            case 30: // RS
                retval = 1;
        }

        if (position != length - 2) {
            if ((source[position] == 13) && (source[position + 1] == 10)) { // CRLF
                retval = 1;
            }
        }
    }

    return retval;
}

/* Check if the next characters are directly encodable in code set C (Annex F.II.D) */
static int datum_c(const unsigned char source[], int position, int length) {
    int retval = 0;

    if (position <= length - 2) {
        if (((source[position] >= '0') && (source[position] <= '9'))
                && ((source[position + 1] >= '0') && (source[position + 1] <= '9')))
            retval = 1;
    }

    return retval;
}

/* Returns how many consecutive digits lie immediately ahead (Annex F.II.A) */
static int n_digits(const unsigned char source[], int position, int length) {
    int i;

    for (i = position; ((source[i] >= '0') && (source[i] <= '9')) && (i < length); i++);

    return i - position;
}

/* checks ahead for 10 or more digits starting "17xxxxxx10..." (Annex F.II.B) */
static int seventeen_ten(const unsigned char source[], int position, int length) {
    int found = 0;

    if (n_digits(source, position, length) >= 10) {
        if (((source[position] == '1') && (source[position + 1] == '7'))
                && ((source[position + 8] == '1') && (source[position + 9] == '0'))) {
            found = 1;
        }
    }

    return found;
}

/*  checks how many characters ahead can be reached while datum_c is true,
 *  returning the resulting number of codewords (Annex F.II.E)
 */
static int ahead_c(const unsigned char source[], int position, int length) {
    int count = 0;
    int i;

    for (i = position; (i < length) && datum_c(source, i, length); i += 2) {
        count++;
    }

    return count;
}

/* Annex F.II.F */
static int try_c(const unsigned char source[], int position, int length) {
    int retval = 0;

    if (n_digits(source, position, length) > 0) {
        if (ahead_c(source, position, length) > ahead_c(source, position + 1, length)) {
            retval = ahead_c(source, position, length);
        }
    }

    return retval;
}

/* Annex F.II.G */
static int ahead_a(const unsigned char source[], int position, int length) {
    int count = 0;
    int i;

    for (i = position; ((i < length) && datum_a(source, i, length))
            && (try_c(source, i, length) < 2); i++) {
        count++;
    }

    return count;
}

/* Annex F.II.H */
static int ahead_b(const unsigned char source[], int position, int length) {
    int count = 0;
    int i;

    for (i = position; ((i < length) && datum_b(source, i, length))
            && (try_c(source, i, length) < 2); i++) {
        count++;
    }

    return count;
}

/* checks if the next character is in the range 128 to 255  (Annex F.II.I) */
static int binary(const unsigned char source[], int length, int position) {
    int retval = 0;

    if (position < length && source[position] >= 128) {
        retval = 1;
    }

    return retval;
}

/* Analyse input data stream and encode using algorithm from Annex F */
static int dotcode_encode_message(struct zint_symbol *symbol, const unsigned char source[], int length, unsigned char *codeword_array, int *binary_finish) {
    int input_position, array_length, i;
    char encoding_mode;
    int inside_macro;
    int debug = (symbol->debug & ZINT_DEBUG_PRINT);
    int binary_buffer_size = 0;
    int lawrencium[6]; // Reversed radix 103 values

#if defined(_MSC_VER) && _MSC_VER == 1200
    uint64_t binary_buffer = 0;
#else
    uint64_t binary_buffer = 0ULL;
#endif

    input_position = 0;
    array_length = 0;
    encoding_mode = 'C';
    inside_macro = 0;

    if (symbol->output_options & READER_INIT) {
        codeword_array[array_length] = 109; // FNC3
        array_length++;
    }

    if ((symbol->input_mode & 0x07) != GS1_MODE) {
        if (length > 2) {
            if (((source[input_position] >= '0') && (source[input_position] <= '9')) &&
                    ((source[input_position + 1] >= '0') && (source[input_position + 1] <= '9'))) {
                codeword_array[array_length] = 107; // FNC1
                array_length++;
            }
        }
    }

    if (symbol->eci > 0) {
        codeword_array[array_length] = 108; // FNC2
        array_length++;
        if (symbol->eci <= 39) {
            codeword_array[array_length] = symbol->eci;
            array_length++;
        } else {
            // the next three codewords valued A, B & C encode the ECI value of
            // (A - 40) * 12769 + B * 113 + C + 40 (Section 5.2.1)
            int a, b, c;
            a = (symbol->eci - 40) / 12769;
            b = ((symbol->eci - 40) - (12769 * a)) / 113;
            c = (symbol->eci - 40) - (12769 * a) - (113 * b);

            codeword_array[array_length] = a + 40;
            array_length++;
            codeword_array[array_length] = b;
            array_length++;
            codeword_array[array_length] = c;
            array_length++;
        }
    }

    // Prevent encodation as a macro if a special character is in first position
    if (source[input_position] == 9) {
        codeword_array[array_length] = 101; // Latch A
        array_length++;
        codeword_array[array_length] = 73; // HT
        array_length++;
        encoding_mode = 'A';
    }

    if (source[input_position] == 28) {
        codeword_array[array_length] = 101; // Latch A
        array_length++;
        codeword_array[array_length] = 92; // FS
        array_length++;
        encoding_mode = 'A';
    }

    if (source[input_position] == 29) {
        codeword_array[array_length] = 101; // Latch A
        array_length++;
        codeword_array[array_length] = 93; // GS
        array_length++;
        encoding_mode = 'A';
    }

    if (source[input_position] == 30) {
        codeword_array[array_length] = 101; // Latch A
        array_length++;
        codeword_array[array_length] = 94; // RS
        array_length++;
        encoding_mode = 'A';
    }

    do {
        int done = 0;
        /* Step A */
        if ((input_position == length - 2) && (inside_macro != 0) && (inside_macro != 100)) {
            // inside_macro only gets set to 97, 98 or 99 if the last two characters are RS/EOT
            input_position += 2;
            done = 1;
            if (debug) {
                printf("A ");
            }
        }

        /* Step B */
        if ((input_position == length - 1) && (inside_macro == 100)) {
            // inside_macro only gets set to 100 if the last character is EOT
            input_position++;
            done = 1;
            if (debug) {
                printf("B ");
            }
        }

        /* Step C1 */
        if ((!done) && (encoding_mode == 'C')) {
            if ((array_length == 0) && (length > 9)) {
                if ((source[input_position] == '[')
                        && (source[input_position + 1] == ')')
                        && (source[input_position + 2] == '>')
                        && (source[input_position + 3] == 30) // RS
                        && (source[length - 1] == 04)) { // EOT


                    if ((source[input_position + 6] == 29) && (source[length - 2] == 30)) { // GS/RS
                        if ((source[input_position + 4] == '0') && (source[input_position + 5] == '5')) {
                            codeword_array[array_length] = 102; // Shift B
                            array_length++;
                            codeword_array[array_length] = 97; // Macro
                            array_length++;
                            input_position += 7;
                            inside_macro = 97;
                            done = 1;
                            if (debug) {
                                printf("C1/1 ");
                            }
                        }

                        if ((source[input_position + 4] == '0') && (source[input_position + 5] == '6')) {
                            codeword_array[array_length] = 102; // Shift B
                            array_length++;
                            codeword_array[array_length] = 98; // Macro
                            array_length++;
                            input_position += 7;
                            inside_macro = 98;
                            done = 1;
                            if (debug) {
                                printf("C1/2 ");
                            }
                        }

                        if ((source[input_position + 4] == '1') && (source[input_position + 5] == '2')) {
                            codeword_array[array_length] = 102; // Shift B
                            array_length++;
                            codeword_array[array_length] = 99; // Macro
                            array_length++;
                            input_position += 7;
                            inside_macro = 99;
                            done = 1;
                            if (debug) {
                                printf("C1/3 ");
                            }
                        }
                    }

                    if ((!done) && (source[input_position] >= '0') && (source[input_position] <= '9') &&
                            (source[input_position + 1] >= '0') && (source[input_position + 1] <= '9')) {
                        codeword_array[array_length] = 102; // Shift B
                        array_length++;
                        codeword_array[array_length] = 100; // Macro
                        array_length++;
                        input_position += 4;
                        inside_macro = 100;
                        done = 1;
                        if (debug) {
                            printf("C1/4 ");
                        }
                    }
                }
            }
        }

        /* Step C2 */
        if ((!done) && (encoding_mode == 'C')) {
            if (seventeen_ten(source, input_position, length)) {
                codeword_array[array_length] = 100; // (17)...(10)
                array_length++;
                codeword_array[array_length] = ((source[input_position + 2] - '0') * 10) + (source[input_position + 3] - '0');
                array_length++;
                codeword_array[array_length] = ((source[input_position + 4] - '0') * 10) + (source[input_position + 5] - '0');
                array_length++;
                codeword_array[array_length] = ((source[input_position + 6] - '0') * 10) + (source[input_position + 7] - '0');
                array_length++;
                input_position += 10;
                done = 1;
                if (debug) {
                    printf("C2/1 ");
                }
            }
        }

        if ((!done) && (encoding_mode == 'C')) {
            if (datum_c(source, input_position, length) || ((source[input_position] == '[') && ((symbol->input_mode & 0x07) == GS1_MODE))) {
                if (source[input_position] == '[') {
                    codeword_array[array_length] = 107; // FNC1
                    input_position++;
                } else {
                    codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0');
                    input_position += 2;
                }
                array_length++;
                done = 1;
                if (debug) {
                    printf("C2/2 ");
                }
            }
        }

        /* Step C3 */
        if ((!done) && (encoding_mode == 'C')) {
            if (binary(source, length, input_position)) {
                if (n_digits(source, input_position + 1, length) > 0) {
                    if ((source[input_position] - 128) < 32) {
                        codeword_array[array_length] = 110; // Bin Shift A
                        array_length++;
                        codeword_array[array_length] = source[input_position] - 128 + 64;
                        array_length++;
                    } else {
                        codeword_array[array_length] = 111; // Bin Shift B
                        array_length++;
                        codeword_array[array_length] = source[input_position] - 128 - 32;
                        array_length++;
                    }
                    input_position++;
                } else {
                    codeword_array[array_length] = 112; // Bin Latch
                    array_length++;
                    encoding_mode = 'X';
                }
                done = 1;
                if (debug) {
                    printf("C3 ");
                }
            }
        }

        /* Step C4 */
        if ((!done) && (encoding_mode == 'C')) {
            int m = ahead_a(source, input_position, length);
            int n = ahead_b(source, input_position, length);
            if (m > n) {
                codeword_array[array_length] = 101; // Latch A
                array_length++;
                encoding_mode = 'A';
            } else {
                if (n <= 4) {
                    codeword_array[array_length] = 101 + n; // nx Shift B
                    array_length++;

                    for (i = 0; i < n; i++) {
                        if (source[input_position] >= 32) {
                            codeword_array[array_length] = source[input_position] - 32;
                        } else {
                            switch(source[input_position]) {
                                case 9: codeword_array[array_length] = 97; break; // HT
                                case 28: codeword_array[array_length] = 98; break; // FS
                                case 29: codeword_array[array_length] = 99; break; // GS
                                case 30: codeword_array[array_length] = 100; break; // RS
                            }
                        }
                        array_length++;
                        input_position++;
                    }
                } else {
                    codeword_array[array_length] = 106; // Latch B
                    array_length++;
                    encoding_mode = 'B';
                }
            }
            done = 1;
            if (debug) {
                printf("C4 ");
            }
        }

        /* Step D1 */
        if ((!done) && (encoding_mode == 'B')) {
            int n = try_c(source, input_position, length);

            if (n >= 2) {
                if (n <= 4) {
                    codeword_array[array_length] = 103 + (n - 2); // nx Shift C
                    array_length++;
                    for (i = 0; i < n; i++) {
                        codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0');
                        array_length++;
                        input_position += 2;
                    }
                } else {
                    codeword_array[array_length] = 106; // Latch C
                    array_length++;
                    encoding_mode = 'C';
                }
                done = 1;
                if (debug) {
                    printf("D1 ");
                }
            }
        }

        /* Step D2 */
        if ((!done) && (encoding_mode == 'B')) {
            if ((source[input_position] == '[') && ((symbol->input_mode & 0x07) == GS1_MODE)) {
                codeword_array[array_length] = 107; // FNC1
                array_length++;
                input_position++;
                done = 1;
                if (debug) {
                    printf("D2/1 ");
                }
            } else {
                if (datum_b(source, input_position, length)) {

                    if ((source[input_position] >= 32) && (source[input_position] <= 127)) {
                        codeword_array[array_length] = source[input_position] - 32;
                        done = 1;

                    } else if (source[input_position] == 13) {
                        /* CR/LF */
                        codeword_array[array_length] = 96;
                        input_position++;
                        done = 1;

                    } else if (input_position != 0) {
                        /* HT, FS, GS and RS in the first data position would be interpreted as a macro (see table 2) */
                        switch(source[input_position]) {
                            case 9: // HT
                                codeword_array[array_length] = 97;
                                break;
                            case 28: // FS
                                codeword_array[array_length] = 98;
                                break;
                            case 29: // GS
                                codeword_array[array_length] = 99;
                                break;
                            case 30: // RS
                                codeword_array[array_length] = 100;
                                break;
                        }
                        done = 1;
                    }

                    if (done == 1) {
                        array_length++;
                        input_position++;
                        if (debug) {
                            printf("D2/2 ");
                        }
                    }
                }
            }
        }

        /* Step D3 */
        if ((!done) && (encoding_mode == 'B')) {
            if (binary(source, length, input_position)) {
                if (datum_b(source, input_position + 1, length)) {
                    if ((source[input_position] - 128) < 32) {
                        codeword_array[array_length] = 110; // Bin Shift A
                        array_length++;
                        codeword_array[array_length] = source[input_position] - 128 + 64;
                        array_length++;
                    } else {
                        codeword_array[array_length] = 111; // Bin Shift B
                        array_length++;
                        codeword_array[array_length] = source[input_position] - 128 - 32;
                        array_length++;
                    }
                    input_position++;
                } else {
                    codeword_array[array_length] = 112; // Bin Latch
                    array_length++;
                    encoding_mode = 'X';
                }
                done = 1;
                if (debug) {
                    printf("D3 ");
                }
            }
        }

        /* Step D4 */
        if ((!done) && (encoding_mode == 'B')) {
            if (ahead_a(source, input_position, length) == 1) {
                codeword_array[array_length] = 101; // Shift A
                array_length++;
                if (source[input_position] < 32) {
                    codeword_array[array_length] = source[input_position] + 64;
                } else {
                    codeword_array[array_length] = source[input_position] - 32;
                }
                array_length++;
                input_position++;
            } else {
                codeword_array[array_length] = 102; // Latch A
                array_length++;
                encoding_mode = 'A';
            }
            done = 1;
            if (debug) {
                printf("D4 ");
            }
        }

        /* Step E1 */
        if ((!done) && (encoding_mode == 'A')) {
            int n = try_c(source, input_position, length);
            if (n >= 2) {
                if (n <= 4) {
                    codeword_array[array_length] = 103 + (n - 2); // nx Shift C
                    array_length++;
                    for (i = 0; i < n; i++) {
                        codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0');
                        array_length++;
                        input_position += 2;
                    }
                } else {
                    codeword_array[array_length] = 106; // Latch C
                    array_length++;
                    encoding_mode = 'C';
                }
                done = 1;
                if (debug) {
                    printf("E1 ");
                }
            }
        }

        /* Step E2 */
        if ((!done) && (encoding_mode == 'A')) {
            if ((source[input_position] == '[') && ((symbol->input_mode & 0x07) == GS1_MODE)) {
                codeword_array[array_length] = 107; // FNC1
                array_length++;
                input_position++;
                done = 1;
                if (debug) {
                    printf("E2/1 ");
                }
            } else {
                if (datum_a(source, input_position, length)) {
                    if (source[input_position] < 32) {
                        codeword_array[array_length] = source[input_position] + 64;
                    } else {
                        codeword_array[array_length] = source[input_position] - 32;
                    }
                    array_length++;
                    input_position++;
                    done = 1;
                    if (debug) {
                        printf("E2/2 ");
                    }
                }
            }
        }

        /* Step E3 */
        if ((!done) && (encoding_mode == 'A')) {
            if (binary(source, length, input_position)) {
                if (datum_a(source, input_position + 1, length)) {
                    if ((source[input_position] - 128) < 32) {
                        codeword_array[array_length] = 110; // Bin Shift A
                        array_length++;
                        codeword_array[array_length] = source[input_position] - 128 + 64;
                        array_length++;
                    } else {
                        codeword_array[array_length] = 111; // Bin Shift B
                        array_length++;
                        codeword_array[array_length] = source[input_position] - 128 - 32;
                        array_length++;
                    }
                    input_position++;
                } else {
                    codeword_array[array_length] = 112; // Bin Latch
                    array_length++;
                    encoding_mode = 'X';
                }
                done = 1;
                if (debug) {
                    printf("E3 ");
                }
            }
        }

        /* Step E4 */
        if ((!done) && (encoding_mode == 'A')) {
            int n = ahead_b(source, input_position, length);

            if (n <= 6) {
                codeword_array[array_length] = 95 + n; // nx Shift B
                array_length++;
                for (i = 0; i < n; i++) {
                    if (source[input_position] >= 32) {
                        codeword_array[array_length] = source[input_position] - 32;
                    } else {
                        switch(source[input_position]) {
                            case 9: codeword_array[array_length] = 97; break; // HT
                            case 28: codeword_array[array_length] = 98; break; // FS
                            case 29: codeword_array[array_length] = 99; break; // GS
                            case 30: codeword_array[array_length] = 100; break; // RS
                        }
                    }
                    array_length++;
                    input_position++;
                }
            } else {
                codeword_array[array_length] = 102; // Latch B
                array_length++;
                encoding_mode = 'B';
            }
            done = 1;
            if (debug) {
                printf("E4 ");
            }
        }

        /* Step F1 */
        if ((!done) && (encoding_mode == 'X')) {
            int n = try_c(source, input_position, length);

            if (n >= 2) {
                /* Empty binary buffer */
                for (i = 0; i < (binary_buffer_size + 1); i++) {
                    lawrencium[i] = binary_buffer % 103;
                    binary_buffer /= 103;
                }

                for (i = 0; i < (binary_buffer_size + 1); i++) {
                    codeword_array[array_length] = lawrencium[binary_buffer_size - i];
                    array_length++;
                }
                binary_buffer = 0;
                binary_buffer_size = 0;

                if (n <= 7) {
                    codeword_array[array_length] = 101 + n; // Interrupt for nx Shift C
                    array_length++;
                    for (i = 0; i < n; i++) {
                        codeword_array[array_length] = ((source[input_position] - '0') * 10) + (source[input_position + 1] - '0');
                        array_length++;
                        input_position += 2;
                    }
                } else {
                    codeword_array[array_length] = 111; // Terminate with Latch to C
                    array_length++;
                    encoding_mode = 'C';
                }
                done = 1;
                if (debug) {
                    printf("F1 ");
                }
            }
        }

        /* Step F2 */
        /* Section 5.2.1.1 para D.2.i states:
         * "Groups of six codewords, each valued between 0 and 102, are radix converted from
         * base 103 into five base 259 values..."
         */
        if ((!done) && (encoding_mode == 'X')) {
            if (binary(source, length, input_position)
                    || binary(source, length, input_position + 1)
                    || binary(source, length, input_position + 2)
                    || binary(source, length, input_position + 3)) {
                binary_buffer *= 259;
                binary_buffer += source[input_position];
                binary_buffer_size++;

                if (binary_buffer_size == 5) {
                    for (i = 0; i < 6; i++) {
                        lawrencium[i] = binary_buffer % 103;
                        binary_buffer /= 103;
                    }

                    for (i = 0; i < 6; i++) {
                        codeword_array[array_length] = lawrencium[5 - i];
                        array_length++;
                    }
                    binary_buffer = 0;
                    binary_buffer_size = 0;
                }
                input_position++;
                done = 1;
                if (debug) {
                    printf("F2 ");
                }
            }
        }

        /* Step F3 */
        if ((!done) && (encoding_mode == 'X')) {
            /* Empty binary buffer */
            for (i = 0; i < (binary_buffer_size + 1); i++) {
                lawrencium[i] = binary_buffer % 103;
                binary_buffer /= 103;
            }

            for (i = 0; i < (binary_buffer_size + 1); i++) {
                codeword_array[array_length] = lawrencium[binary_buffer_size - i];
                array_length++;
            }
            binary_buffer = 0;
            binary_buffer_size = 0;

            if (ahead_a(source, input_position, length) > ahead_b(source, input_position, length)) {
                codeword_array[array_length] = 109; // Terminate with Latch to A
                encoding_mode = 'A';
            } else {
                codeword_array[array_length] = 110; // Terminate with Latch to B
                encoding_mode = 'B';
            }
            array_length++;
            done = 1;
            if (debug) {
                printf("F3 ");
            }
        }
    } while (input_position < length);

    if (encoding_mode == 'X') {
        if (binary_buffer_size != 0) {
            /* Empty binary buffer */
            for (i = 0; i < (binary_buffer_size + 1); i++) {
                lawrencium[i] = binary_buffer % 103;
                binary_buffer /= 103;
            }

            for (i = 0; i < (binary_buffer_size + 1); i++) {
                codeword_array[array_length] = lawrencium[binary_buffer_size - i];
                array_length++;
            }
        }
        *(binary_finish) = 1;
    }

    if (debug) {
        printf("\n");
    }

    return array_length;
}

/* Convert codewords to binary data stream */
static size_t make_dotstream(unsigned char masked_array[], int array_length, char dot_stream[]) {
    int i;

    dot_stream[0] = '\0';

    /* Mask value is encoded as two dots */
    bin_append(masked_array[0], 2, dot_stream);

    /* The rest of the data uses 9-bit dot patterns from Annex C */
    for (i = 1; i < array_length; i++) {
        bin_append(dot_patterns[masked_array[i]], 9, dot_stream);
    }

    return strlen(dot_stream);
}

/* Determines if a given dot is a reserved corner dot
 * to be used by one of the last six bits
 */
static int is_corner(int column, int row, int width, int height) {
    int corner = 0;

    /* Top Left */
    if ((column == 0) && (row == 0)) {
        corner = 1;
    }

    /* Top Right */
    if (height % 2) {
        if (((column == width - 2) && (row == 0))
                || ((column == width - 1) && (row == 1))) {
            corner = 1;
        }
    } else {
        if ((column == width - 1) && (row == 0)) {
            corner = 1;
        }
    }

    /* Bottom Left */
    if (height % 2) {
        if ((column == 0) && (row == height - 1)) {
            corner = 1;
        }
    } else {
        if (((column == 0) && (row == height - 2))
                || ((column == 1) && (row == height - 1))) {
            corner = 1;
        }
    }

    /* Bottom Right */
    if (((column == width - 2) && (row == height - 1))
            || ((column == width - 1) && (row == height - 2))) {
        corner = 1;
    }

    return corner;
}

/* Place the dots in the symbol*/
static void fold_dotstream(char dot_stream[], int width, int height, char dot_array[]) {
    int column, row;
    int input_position = 0;

    if (height % 2) {
        /* Horizontal folding */
        for (row = 0; row < height; row++) {
            for (column = 0; column < width; column++) {
                if (!((column + row) % 2)) {
                    if (is_corner(column, row, width, height)) {
                        dot_array[(row * width) + column] = 'C';
                    } else {
                        dot_array[((height - row - 1) * width) + column] = dot_stream[input_position];
                        input_position++;
                    }
                } else {
                    dot_array[((height - row - 1) * width) + column] = ' '; // Non-data position
                }
            }
        }

        /* Corners */
        dot_array[width - 2] = dot_stream[input_position];
        input_position++;
        dot_array[(height * width) - 2] = dot_stream[input_position];
        input_position++;
        dot_array[(width * 2) - 1] = dot_stream[input_position];
        input_position++;
        dot_array[((height - 1) * width) - 1] = dot_stream[input_position];
        input_position++;
        dot_array[0] = dot_stream[input_position];
        input_position++;
        dot_array[(height - 1) * width] = dot_stream[input_position];
    } else {
        /* Vertical folding */
        for (column = 0; column < width; column++) {
            for (row = 0; row < height; row++) {
                if (!((column + row) % 2)) {
                    if (is_corner(column, row, width, height)) {
                        dot_array[(row * width) + column] = 'C';
                    } else {
                        dot_array[(row * width) + column] = dot_stream[input_position];
                        input_position++;
                    }
                } else {
                    dot_array[(row * width) + column] = ' '; // Non-data position
                }
            }
        }

        /* Corners */
        dot_array[((height - 1) * width) - 1] = dot_stream[input_position];
        input_position++;
        dot_array[(height - 2) * width] = dot_stream[input_position];
        input_position++;
        dot_array[(height * width) - 2] = dot_stream[input_position];
        input_position++;
        dot_array[((height - 1) * width) + 1] = dot_stream[input_position];
        input_position++;
        dot_array[width - 1] = dot_stream[input_position];
        input_position++;
        dot_array[0] = dot_stream[input_position];
    }
}

static void apply_mask(int mask, int data_length, unsigned char *masked_codeword_array, unsigned char *codeword_array, int ecc_length) {
    int weight = 0;
    int j;

    switch (mask) {
        case 0:
            masked_codeword_array[0] = 0;
            for (j = 0; j < data_length; j++) {
                masked_codeword_array[j + 1] = codeword_array[j];
            }
            break;
        case 1:
            masked_codeword_array[0] = 1;
            for (j = 0; j < data_length; j++) {
                masked_codeword_array[j + 1] = (weight + codeword_array[j]) % 113;
                weight += 3;
            }
            break;
        case 2:
            masked_codeword_array[0] = 2;
            for (j = 0; j < data_length; j++) {
                masked_codeword_array[j + 1] = (weight + codeword_array[j]) % 113;
                weight += 7;
            }
            break;
        case 3:
            masked_codeword_array[0] = 3;
            for (j = 0; j < data_length; j++) {
                masked_codeword_array[j + 1] = (weight + codeword_array[j]) % 113;
                weight += 17;
            }
            break;
    }

    rsencode(data_length + 1, ecc_length, masked_codeword_array);
}

static void force_corners(int width, int height, char *dot_array) {
    if (width % 2) {
        // "Vertical" symbol
        dot_array[0] = '1';
        dot_array[width - 1] = '1';
        dot_array[(height - 2) * width] = '1';
        dot_array[((height - 1) * width) - 1] = '1';
        dot_array[((height - 1) * width) + 1] = '1';
        dot_array[(height * width) - 2] = '1';
    } else {
        // "Horizontal" symbol
        dot_array[0] = '1';
        dot_array[width - 2] = '1';
        dot_array[(2 * width) - 1] = '1';
        dot_array[((height - 1) * width) - 1] = '1';
        dot_array[(height - 1) * width] = '1';
        dot_array[(height * width) - 2] = '1';
    }
}

INTERNAL int dotcode(struct zint_symbol *symbol, const unsigned char source[], int length) {
    int i, j, k;
    size_t jc, n_dots;
    int data_length, ecc_length;
    int min_dots, min_area;
    int height, width;
    int mask_score[8];
    size_t dot_stream_length;
    int high_score, best_mask;
    int binary_finish = 0;
    int debug = symbol->debug;
    int padding_dots, is_first;
    int codeword_array_len = length * 4 + 8; /* Allow up to 4 codewords per input + 2 (FNC) + 4 (ECI) + 2 (special char 1st position) */
#ifdef _MSC_VER
    unsigned char* masked_codeword_array;
#endif

#ifndef _MSC_VER
    unsigned char codeword_array[codeword_array_len];
#else
    char* dot_stream;
    char* dot_array;
    unsigned char* codeword_array = (unsigned char *) _alloca(codeword_array_len);
#endif /* _MSC_VER */

    if (symbol->eci > 811799) {
        strcpy(symbol->errtxt, "525: Invalid ECI");
        return ZINT_ERROR_INVALID_OPTION;
    }

    data_length = dotcode_encode_message(symbol, source, length, codeword_array, &binary_finish);

    ecc_length = 3 + (data_length / 2);

    if (debug) {
        printf("Codeword length = %d, ECC length = %d\n", data_length, ecc_length);
        printf("Codewords: ");
        for (i = 0; i < data_length; i++) {
            printf("[%d] ",codeword_array[i]);
        }
        printf("\n");
    }

    min_dots = 9 * (data_length + 3 + (data_length / 2)) + 2;
    min_area = min_dots * 2;

    if (symbol->option_2 == 0) {
        /* Automatic sizing */
        /* Following Rule 3 (Section 5.2.2) and applying a recommended width to height ratio 3:2 */
        /* Eliminates under sized symbols */

        float h = (float) (sqrt(min_area * 0.666));
        float w = (float) (sqrt(min_area * 1.5));

        height = (int) h;
        width = (int) w;

        if ((width + height) % 2 == 1) {
            if ((width * height) < min_area) {
                width++;
                height++;
            }
        } else {
            if ((h * width) < (w * height)) {
                width++;
                if ((width * height) < min_area) {
                    width--;
                    height++;
                    if ((width * height) < min_area) {
                        width += 2;
                    }
                }
            } else {
                height++;
                if ((width * height) < min_area) {
                    width++;
                    height--;
                    if ((width * height) < min_area) {
                        height += 2;
                    }
                }
            }
        }

    } else {
        /* User defined width */
        /* Eliminates under sized symbols */

        width = symbol->option_2;
        height = (min_area + (width - 1)) / width;

        if (!((width + height) % 2)) {
            height++;
        }
    }

    if ((height > 200) || (width > 200)) {
        strcpy(symbol->errtxt, "526: Specified symbol size is too large");
        return ZINT_ERROR_INVALID_OPTION;
    }

    if ((height < 5) || (width < 5)) {
        strcpy(symbol->errtxt, "527: Specified symbol size has a dimension which is too small");
        return ZINT_ERROR_INVALID_OPTION;
    }

    n_dots = (height * width) / 2;

#ifndef _MSC_VER
    char dot_stream[height * width * 3];
    char dot_array[width * height * sizeof (char) ];
#else
    dot_stream = (char *) _alloca(height * width * 3);
    if (!dot_stream) return ZINT_ERROR_MEMORY;

    dot_array = (char *) _alloca(width * height * sizeof (char));
    if (!dot_array) return ZINT_ERROR_MEMORY;
#endif

    /* Add pad characters */
    padding_dots = n_dots - min_dots; /* get the number of free dots available for padding */
    is_first = 1; /* first padding character flag */

    while (padding_dots >= 9) {
        if (padding_dots < 18 && ((data_length % 2) == 0))
            padding_dots -= 9;

        else if (padding_dots >= 18) {
            if ((data_length % 2) == 0)
                padding_dots -= 9;
            else
                padding_dots -= 18;
        } else
            break; /* not enough padding dots left for padding */

        if ((is_first == 1) && (binary_finish == 1))
            codeword_array[data_length] = 109;
        else
            codeword_array[data_length] = 106;

        data_length++;
        is_first = 0;
    }

    ecc_length = 3 + (data_length / 2);


#ifndef _MSC_VER
    unsigned char masked_codeword_array[data_length + 1 + ecc_length];
#else
    masked_codeword_array = (unsigned char *) _alloca((data_length + 1 + ecc_length) * sizeof (unsigned char));
#endif /* _MSC_VER */

    /* Evaluate data mask options */
    for (i = 0; i < 4; i++) {

        apply_mask(i, data_length, masked_codeword_array, codeword_array, ecc_length);

        dot_stream_length = make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream);

        /* Add pad bits */
        for (jc = dot_stream_length; jc < n_dots; jc++) {
            strcat(dot_stream, "1");
        }

        fold_dotstream(dot_stream, width, height, dot_array);

        mask_score[i] = score_array(dot_array, height, width);

        if (debug) {
            printf("Mask %d score is %d\n", i, mask_score[i]);
        }
    }

    high_score = mask_score[0];
    best_mask = 0;

    for (i = 1; i < 4; i++) {
        if (mask_score[i] >= high_score) {
            high_score = mask_score[i];
            best_mask = i;
        }
    }

    /* Re-evaluate using forced corners if needed */
    if (best_mask <= (height * width) / 2) {
        for (i = 0; i < 4; i++) {

            apply_mask(i, data_length, masked_codeword_array, codeword_array, ecc_length);

            dot_stream_length = make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream);

            /* Add pad bits */
            for (jc = dot_stream_length; jc < n_dots; jc++) {
                strcat(dot_stream, "1");
            }

            fold_dotstream(dot_stream, width, height, dot_array);

            force_corners(width, height, dot_array);

            mask_score[i + 4] = score_array(dot_array, height, width);

            if (debug) {
                printf("Mask %d score is %d\n", i + 4, mask_score[i + 4]);
            }
        }

        for (i = 4; i < 8; i++) {
            if (mask_score[i] >= high_score) {
                high_score = mask_score[i];
                best_mask = i;
            }
        }
    }

    if (debug) {
        printf("Applying mask %d\n", best_mask);
    }

    /* Apply best mask */
    apply_mask(best_mask % 4, data_length, masked_codeword_array, codeword_array, ecc_length);

    dot_stream_length = make_dotstream(masked_codeword_array, (data_length + ecc_length + 1), dot_stream);

    /* Add pad bits */
    for (jc = dot_stream_length; jc < n_dots; jc++) {
        strcat(dot_stream, "1");
    }

    fold_dotstream(dot_stream, width, height, dot_array);

    if (best_mask >= 4) {
        force_corners(width, height, dot_array);
    }

    /* Copy values to symbol */
    symbol->width = width;
    symbol->rows = height;

    for (k = 0; k < height; k++) {
        for (j = 0; j < width; j++) {
            if (dot_array[(k * width) + j] == '1') {
                set_module(symbol, k, j);
            }
        }
        symbol->row_height[k] = 1;
    }

    if (!(symbol->output_options & BARCODE_DOTTY_MODE)) {
        symbol->output_options += BARCODE_DOTTY_MODE;
    }

    return 0;
}
