from reportlab.graphics import renderPDF
from reportlab.graphics.barcode import code39, code93, code128, common, eanbc, qr, usps, usps4s
from reportlab.graphics.shapes import Drawing

from pyametista.engine.commonutilities import add_attr2attributes, replace_text


def draw_basic_barcode(report, barcode_type, element, attributes):
    """
    Draw barcode on report_info.
    :param report: dictionary holding report information
    :param barcode_type: class + method to draw barcode depending of barcode type
    :param element: jrxml element list
    :param attributes: dictionary of attributes to use when drawing an object
    """
    code_expression = element.get("codeExpression")
    if code_expression is not None:
        data_value = str(replace_text(report, code_expression.get("value").strip('"'), attributes))
        if data_value is not None and data_value != "":
            if attributes.get("checksumRequired", False):
                check_sum = 1
            else:
                check_sum = 0

            report["canvas"].saveState()

            barcode = barcode_type(
                data_value,
                barHeight=attributes.get("barHeight", attributes["height"]),
                stop=1,
                checksum=check_sum,
            )

            # scale barcode to specified width
            report["canvas"].translate(
                attributes["x"], report["cur_y"] - attributes["y"] - attributes["height"]
            )
            report["canvas"].scale(
                attributes.get("barWidth", attributes["width"]) / barcode.width,
                attributes.get("barHeight", attributes["height"]) / barcode.height,
            )
            barcode.drawOn(report["canvas"], 0, 0)

            report["canvas"].restoreState()


def draw_advanced_barcode(report, barcode_type, element, attributes):
    """
    Advanced barcodes are barcodes that requires to be drawn to Drawing objects first.
    Barcodes that drawn to Drawing objects and then put on a report_info.
    :param report: dictionary holding report information
    :param barcode_type:
    :param element:
    :param attributes:
    """
    code_expression = element.get("codeExpression")
    if code_expression is not None:
        data_value = str(replace_text(report, code_expression.get("value").strip('"'), attributes))
        if data_value is not None and data_value != "":
            barcode = barcode_type(data_value)
            bounds = barcode.getBounds()
            width = bounds[2] - bounds[0]
            height = bounds[3] - bounds[1]

            report["canvas"].saveState()
            d = Drawing(
                attributes["width"],
                attributes["height"],
                transform=[
                    attributes.get("barWidth", attributes["width"]) / width,
                    0,
                    0,
                    attributes.get("barHeight", attributes["height"]) / height,
                    0,
                    0,
                ],
            )
            d.add(barcode)
            renderPDF.draw(
                d,
                report["canvas"],
                attributes["x"],
                report["cur_y"] - attributes["y"] - attributes["height"],
            )

            report["canvas"].restoreState()


"""
Values are (def to used to draw barcode on report_info, barcode component/def)
List of supported Barbecue barcodes. Supported list of Barcode4j barcodes must also be defined in
components.components.py
"""
barcode_types_dict = {
    "Codabar": (draw_basic_barcode, common.Codabar),
    "Code11": (draw_basic_barcode, common.Code11),
    "Code39": (draw_basic_barcode, code39.Standard39),
    "Code39 (Extended)": (draw_basic_barcode, code39.Extended39),
    "Code93": (draw_basic_barcode, code93.Standard93),
    "Code93 (Extended)": (draw_basic_barcode, code93.Extended93),
    "Code128": (draw_basic_barcode, code128.Code128),
    "EAN8": (draw_advanced_barcode, eanbc.Ean8BarcodeWidget),
    "EAN13": (draw_advanced_barcode, eanbc.Ean13BarcodeWidget),
    "EAN128": None,
    "FIM": (draw_basic_barcode, usps.FIM),  # TODO need to check
    "Interleaved2Of5": (draw_basic_barcode, common.I2of5),
    "Int2of5": (draw_basic_barcode, common.I2of5),
    "MSI": (draw_basic_barcode, common.MSI),  # TODO need to check
    "PostNet": (draw_basic_barcode, usps.POSTNET),
    "POSTNET": (draw_basic_barcode, usps.POSTNET),
    "QRCode": (draw_advanced_barcode, qr.QrCodeWidget),
    "USPS_4State": (draw_basic_barcode, usps4s.USPS_4State),
}


def process_barbecue(report, key, element, attributes):
    """
    Process jrxml barbecue component elements.
    :param report: dictionary holding report information
    :param element: jrxml barbecue element
    :param attributes: attributes of this element
    """
    add_attr2attributes(element, attributes)
    type = attributes.get("type")
    if type is not None:
        barcode_element_list = element.get("child")
        if barcode_element_list is not None:
            barcode_type = barcode_types_dict.get(type)
            if barcode_type is not None:
                barcode_type[0](report, barcode_type[1], barcode_element_list[0], attributes)


def process_barcode4j(report, key, element, attributes):
    """
    Process jrxml jarcode4j element.
    :param report: dictionary holding report information
    :param key:
    :param element:
    :param attributes:
    # :param row_data:  a row from data source
    """
    barcode_type = barcode_types_dict.get(key)

    if barcode_type is not None:
        barcode_type[0](report, barcode_type[1], element.get("child")[0], attributes)
