# coding=utf-8
#
# 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.
#
"""
Python barcode renderer for Code93 barcodes. Designed for use with Inkscape.
"""

from .Base import Barcode

PALLET = list("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%")
PALLET.append("($)")
PALLET.append("(/)")
PALLET.append("(+)")
PALLET.append("(%)")
PALLET.append("MARKER")

MAP = dict((PALLET[i], i) for i in range(len(PALLET)))


def get_map(array):
    """Extended ENCODE maps for full ASCII Code93"""
    result = {}
    pos = 10
    for char in array:
        result[chr(char)] = PALLET[pos]
        pos += 1
    return result


# MapA is eclectic, but B, C, D are all ASCII ranges
MAP_A = get_map(
    [
        27,
        28,
        29,
        30,
        31,
        59,
        60,
        61,
        62,
        63,
        91,
        92,
        93,
        94,
        95,
        123,
        124,
        125,
        126,
        127,
        0,
        64,
        96,
        127,
        127,
        127,
    ]
)  # %
MAP_B = get_map(range(1, 26))  # $
MAP_C = get_map(range(33, 58))  # /
MAP_D = get_map(range(97, 122))  # +

ENCODE = [
    "100010100",
    "101001000",
    "101000100",
    "101000010",
    "100101000",
    "100100100",
    "100100010",
    "101010000",
    "100010010",
    "100001010",
    "110101000",
    "110100100",
    "110100010",
    "110010100",
    "110010010",
    "110001010",
    "101101000",
    "101100100",
    "101100010",
    "100110100",
    "100011010",
    "101011000",
    "101001100",
    "101000110",
    "100101100",
    "100010110",
    "110110100",
    "110110010",
    "110101100",
    "110100110",
    "110010110",
    "110011010",
    "101101100",
    "101100110",
    "100110110",
    "100111010",
    "100101110",
    "111010100",
    "111010010",
    "111001010",
    "101101110",
    "101110110",
    "110101110",
    "100100110",
    "111011010",
    "111010110",
    "100110010",
    "101011110",
    "",
]


class Code93(Barcode):
    """Encode Code93 Barcode"""

    def encode(self, text):
        # start marker
        bits = ENCODE[MAP.get("MARKER", -1)]

        # Extend to ASCII charset ( return Array )
        text = Code93.encode_ascii(text)

        # Calculate the checksums
        text.append(Code93.checksum(text, 20))  # C
        text.append(Code93.checksum(text, 15))  # K

        # Now convert text into the ENCODE bits (black and white stripes)
        for char in text:
            bits = bits + ENCODE[MAP.get(char, -1)]

        # end marker and termination bar
        return bits + ENCODE[MAP.get("MARKER", -1)] + "1"

    @staticmethod
    def checksum(text, mod):
        """Generate a code 93 checksum"""
        weight = len(text) % mod
        check = 0
        for char in text:
            check = check + (MAP[char] * weight)
            # Reset the weight is required
            weight -= 1
            if weight == 0:
                weight = mod

        return PALLET[check % 47]

    @staticmethod
    def encode_ascii(text):
        """Some characters need re-ENCODE into the code93 specification"""
        result = []
        for char in text:
            if char in MAP:
                result.append(char)
            elif char in MAP_A:
                result.append("(%)")
                result.append(MAP_A[char])
            elif char in MAP_B:
                result.append("($)")
                result.append(MAP_B[char])
            elif char in MAP_C:
                result.append("(/)")
                result.append(MAP_C[char])
            elif char in MAP_D:
                result.append("(+)")
                result.append(MAP_D[char])
        return result
