# coding=utf-8
#
# Authored by Martin Owens <doctormo@gmail.com>
# Debugged by Ralf Heinecke & Martin Siepmann 2007-09-07
#             Horst Schottky 2010-02-27
#
# Copyright (C) 2007 Martin Owens
#
# 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 of the License, 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; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA.
#
"""
Renderer for Code128/EAN128 codes. Designed for use with Inkscape.
"""

import re

from .Base import Barcode

CODE_MAP = [
    "11011001100",
    "11001101100",
    "11001100110",
    "10010011000",
    "10010001100",
    "10001001100",
    "10011001000",
    "10011000100",
    "10001100100",
    "11001001000",
    "11001000100",
    "11000100100",
    "10110011100",
    "10011011100",
    "10011001110",
    "10111001100",
    "10011101100",
    "10011100110",
    "11001110010",
    "11001011100",
    "11001001110",
    "11011100100",
    "11001110100",
    "11101101110",
    "11101001100",
    "11100101100",
    "11100100110",
    "11101100100",
    "11100110100",
    "11100110010",
    "11011011000",
    "11011000110",
    "11000110110",
    "10100011000",
    "10001011000",
    "10001000110",
    "10110001000",
    "10001101000",
    "10001100010",
    "11010001000",
    "11000101000",
    "11000100010",
    "10110111000",
    "10110001110",
    "10001101110",
    "10111011000",
    "10111000110",
    "10001110110",
    "11101110110",
    "11010001110",
    "11000101110",
    "11011101000",
    "11011100010",
    "11011101110",
    "11101011000",
    "11101000110",
    "11100010110",
    "11101101000",
    "11101100010",
    "11100011010",
    "11101111010",
    "11001000010",
    "11110001010",
    "10100110000",
    "10100001100",
    "10010110000",
    "10010000110",
    "10000101100",
    "10000100110",
    "10110010000",
    "10110000100",
    "10011010000",
    "10011000010",
    "10000110100",
    "10000110010",
    "11000010010",
    "11001010000",
    "11110111010",
    "11000010100",
    "10001111010",
    "10100111100",
    "10010111100",
    "10010011110",
    "10111100100",
    "10011110100",
    "10011110010",
    "11110100100",
    "11110010100",
    "11110010010",
    "11011011110",
    "11011110110",
    "11110110110",
    "10101111000",
    "10100011110",
    "10001011110",
    "10111101000",
    "10111100010",
    "11110101000",
    "11110100010",
    "10111011110",
    "10111101110",
    "11101011110",
    "11110101110",
    "11010000100",
    "11010010000",
    "11010011100",
    "11000111010",
    "11",
]


def map_extra(data, chars):
    """Maps the data into the chars"""
    result = list(data)
    for char in chars:
        result.append(chr(char))
    result.append("FNC3")
    result.append("FNC2")
    result.append("SHIFT")
    return result


# The map_extra method is used to slim down the amount
# of pre code and instead we generate the lists
CHAR_AB = list(" !\"#$%&'()*+,-./0123456789:;<=>?@" "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_")
CHAR_A = map_extra(CHAR_AB, range(0, 31))  # Offset 64
CHAR_B = map_extra(CHAR_AB, range(96, 127))  # Offset -32


class Code128(Barcode):
    """Main barcode object, generates the encoding bits here"""

    def encode(self, text):
        blocks = []
        block = ""

        # Split up into sections of numbers, or characters
        # This makes sure that all the characters are encoded
        # In the best way possible for Code128
        for datum in re.findall(r"(?:(?:\d\d){2,})|(?:^\d\d)|.", text):
            if len(datum) == 1:
                block = block + datum
            else:
                if block:
                    blocks.append(self.best_block(block))
                    block = ""
                blocks.append(["C", datum])

        if block:
            blocks.append(Code128.best_block(block))
            block = ""

        return Code128.encode_blocks(blocks)

    @staticmethod
    def best_block(block):
        """If this has characters above 63, select B over A"""
        if any(ord(x) > 63 for x in block):
            return ["B", block]
        return ["A", block]

    @staticmethod
    def encode_blocks(blocks):
        """Encode the given blocks into A, B or C codes"""
        encode = ""
        total = 0
        pos = 0

        for block in blocks:
            b_set = block[0]
            datum = block[1]

            # POS :   0,   1
            # A   : 101, 103
            # B   : 100, 104
            # C   :  99, 105
            num = 0
            if b_set == "A":
                num = 103
            elif b_set == "B":
                num = 104
            elif b_set == "C":
                num = 105

            i = pos
            if pos:
                num = 204 - num
            else:
                i = 1

            total = total + num * i
            encode = encode + CODE_MAP[num]
            pos += 1

            if b_set in ("A", "B"):
                chars = CHAR_B
                if b_set == "A":
                    chars = CHAR_A

                for char in datum:
                    total = total + (chars.index(char) * pos)
                    encode = encode + CODE_MAP[chars.index(char)]
                    pos += 1
            else:
                for char in (datum[i : i + 2] for i in range(0, len(datum), 2)):
                    total = total + (int(char) * pos)
                    encode = encode + CODE_MAP[int(char)]
                    pos += 1

        checksum = total % 103
        encode = encode + CODE_MAP[checksum]
        encode = encode + CODE_MAP[106]
        encode = encode + CODE_MAP[107]

        return encode
