from flask import current_app
from time import sleep
import six
from PIL import Image
from io import BytesIO
import base64


class CustomerTicketBaseLine:
    def __init__(self):
        self.format = ""
        self.text = ""
        self.style = {}

    def from_json(self, jsonObject):
        self.format = parse_text(jsonObject.get("format"))
        self.style = parse_style(jsonObject.get("style"))


class CustomerTicketTextLine(CustomerTicketBaseLine):
    def __init__(self):
        super().__init__

    def from_json(self, jsonObject):
        super().from_json(jsonObject)
        self.text = parse_text(jsonObject.get("text"))


class CustomerTicketTextLeftRightLine(CustomerTicketBaseLine):
    def __init__(self):
        super().__init__
        self.text_left = ""
        self.text_right = ""

    def from_json(self, jsonObject):
        super().from_json(jsonObject)
        self.text_left = parse_text(jsonObject.get("text_left"))
        self.text_right = parse_text(jsonObject.get("text_right"))

        # Calcul espaces
        text_left_size = len(self.text_left)
        text_right_size = len(self.text_right)
        spaces = 48 - (text_left_size + text_right_size)
        if spaces <= 0:
            text_left_size += spaces - 4
            spaces = 4

        self.text = "%s%s%s" % (
            self.text_left[:text_left_size],
            str(" " * spaces),
            self.text_right,
        )


class CustomerTicketDividerLine(CustomerTicketBaseLine):
    def __init__(self):
        super().__init__

    def from_json(self, jsonObject):
        super().from_json(jsonObject)
        self.text = str("-" * 48)


class CustomerTicketImage(CustomerTicketTextLine):
    def __init__(self):
        super().__init__

    def from_json(self, jsonObject):
        super().from_json(jsonObject)


class CustomerTicketQRCode(CustomerTicketTextLine):
    def __init__(self):
        super().__init__
        self.size = 3

    def from_json(self, jsonObject):
        super().from_json(jsonObject)
        self.size = jsonObject.get("size")


class CustomerTicket:
    def __init__(self):
        self.format = ""
        self.checkout_ip = ""
        self.backup_ip = ""
        self.open_cash_drawer = False
        self.lines = []

    def from_json(self, jsonObject):
        """
        Extraire les attributs du jsonObject.

        Exemple de json attendu :
        {
            "format": "Customer",
            "checkout_ip": "192.168.1.89",
            "backup_ip": "192.168.1.87",
            "openCashDrawer": false,
            "lines": [
                {
                    "format": "Text",
                    "text": "BOUTIQUE XXXX",
                    "style": {
                        "align": "center",
                        "width": 3,
                        "height": 3,
                        "text_type": "B",
                        "invert": 1
                    }
                },
                {
                    "format": "Text",
                    "text": "Remise a zero du 29-11-2021 09:59:10 - 29-11-2021 19:34:46",
                    "style": {
                        "align": "center",
                        "width": 3,
                        "height": 3,
                        "text_type": "B",
                        "invert": 1
                    }
                },
                {
                    "format": "Text",
                    "text": "Date : 07/12/2021 09:37"
                },
                {
                    "format": "Text",
                    "text": "Boutique : 531"
                },
                {
                    "format": "Text",
                    "text": "Caissier : (null)"
                },
                {
                    "format": "Divider"
                },
                {
                    "format": "Text",
                    "text": "Chiffre d'affaires TTC :"
                },
                {
                    "format": "Divider"
                },
                {
                    "format": "TextLeftRight",
                    "text_left": "CA BRUT TTC",
                    "text_right": "13716.00 MAD"
                },
                {
                    "format": "TextLeftRight",
                    "text_left": "- Remise",
                    "text_right": "3099.00 MAD"
                },
                {
                    "format": "Divider"
                },
                {
                    "format": "TextLeftRight",
                    "text_left": "CA NET TTC",
                    "text_right": "10741.00 MAD"
                },
                {
                    "format": "Divider"
                },
                {
                    "format": "QRCode",
                    "size": 7,
                    "text": "Some content",
                    "style": {
                        "align": "center"
                    }
                },
                {
                    "format": "Divider"
                },
                {
                    "format": "Image",
                    "text": "base64 image content",
                    "style": {
                        "align": "center"
                    }
                }
                {
                    "format": "Divider"
                }
            ]
        }

        Attributs Style :
        {
            "align":"center" | "left" | "right",
            "font":"a" | "b",
            "width": 1-8,
            "height":1-8,
            "invert":0-1,
            "text_type":"B" | "U" | "BU"
        }
        """

        self.format = parse_text(jsonObject.get("format"))
        self.checkout_ip = parse_text(jsonObject.get("checkout_ip"))
        self.backup_ip = parse_text(jsonObject.get("backup_ip"))
        self.open_cash_drawer = jsonObject.get("openCashDrawer")

        if "lines" in jsonObject and isinstance(jsonObject.get("lines"), list):
            for item in jsonObject.get("lines"):
                line = None
                if "format" in item:
                    if item.get("format") == "TextLeftRight":
                        line = CustomerTicketTextLeftRightLine()
                        line.from_json(item)
                        self.lines.append(line)
                    elif item.get("format") == "Divider":
                        line = CustomerTicketDividerLine()
                        line.from_json(item)
                        self.lines.append(line)
                    elif item.get("format") == "QRCode":
                        line = CustomerTicketQRCode()
                        line.from_json(item)
                        self.lines.append(line)
                    elif item.get("format") == "Image":
                        line = CustomerTicketImage()
                        line.from_json(item)
                        self.lines.append(line)

                if line == None:
                    line = CustomerTicketTextLine()
                    line.from_json(item)
                    self.lines.append(line)

    def to_str(self):
        out = ""

        # Generate content as string
        if self.lines:
            for line in self.lines:
                if isinstance(line, CustomerTicketQRCode):
                    out += "\n"
                    out += str(line.text) + "\n"
                    out += "\n"
                    out += "<QRCode>" + "\n"
                    out += "\n"
                elif isinstance(line, CustomerTicketImage):
                    out += "\n"
                    out += "<Image>" + "\n"
                    out += "\n"
                else:
                    out += str(line.text) + "\n"

        return out

    def to_printer(self, printer):

        # Make sound
        printer._raw(b"\x1b" + b"\x42" + six.int2byte(2) + six.int2byte(1))
        # Do nothing for 1 second
        sleep(1)

        # Send content to printer
        if self.lines:
            for line in self.lines:
                if isinstance(line, CustomerTicketQRCode):
                    printer.set(**parse_style(line.style))
                    printer.text("\n")
                    printer.text(line.text)
                    printer.text("\n")
                    printer.qr(line.text, size=line.size)
                    printer.text("\n")
                elif isinstance(line, CustomerTicketImage):
                    printer.set(**parse_style(line.style))
                    printer.text("\n")
                    printer.image(
                        Image.open(BytesIO(base64.b64decode(line.text)))
                    )
                    printer.text("\n")
                else:
                    printer.set(**parse_style(line.style))
                    printer.text(line.text)
                    printer.text("\n")

            # end for

            # Reset Style
            printer.set()
            # Cut the paper
            printer.cut()
            # Make sound
            printer._raw(b"\x1b" + b"\x42" + six.int2byte(2) + six.int2byte(2))
            # Sleep for some seconds to prevent overflow on the next print
            sleep(current_app.config["TICKET_TIMEOUT"])

        # end if

        # Open Cash Drawer
        #
        # https://github.com/python-escpos/python-escpos/blob/master/src/escpos/constants.py
        # https://www.beaglehardware.com/howtoprogramcashdrawer.html
        #
        # RAW Command: ESC + b"p" + <pin> + six.int2byte(<t1>) + six.int2byte(<t2>)
        #   ESC     = b"\x1b"
        #   b"p"    = b"\x70" (Yes! It is the letter 'p')
        #   <pin>   = b"\x00"
        #   <t1>    = six.int2byte(50) (default to 50) => On Time   = 2 mS x t1
        #   <t2>    = six.int2byte(50) (default to 50) => Off Time  = 2 mS x t2
        # printer._raw(b"\x1b" + b"\x70" + b"\x00" + six.int2byte(25) + six.int2byte(100))
        # printer._raw(b"\x1b" + b"\x70" + b"\x00" + six.int2byte(75) + six.int2byte(25))
        if self.open_cash_drawer:
            printer._raw(b"\x1b" + b"\x70" + b"\x00")


# Common helpers
def parse_text(text_json):
    text = ""
    if text_json:
        text = text_json

    return text


def parse_style(style_json):
    """
    set(align="center", font="a",width=2, height=2, invert=True, text_type="B")
    """
    styles = {}

    if style_json == None:
        return styles

    if "align" in style_json:
        if style_json["align"] in ("center", "left", "right"):
            styles["align"] = style_json["align"]

    if "font" in style_json:
        if style_json["font"] in ("a", "b"):
            styles["font"] = style_json["font"]

    if "width" in style_json:
        width = int(style_json["width"])
        if width >= 1 and width <= 8:
            styles["width"] = width

    if "height" in style_json:
        height = int(style_json["height"])
        if height >= 1 and width <= 8:
            styles["height"] = height

    if "invert" in style_json:
        invert = int(style_json["invert"])
        styles["invert"] = bool(invert)

    if "text_type" in style_json:
        if style_json["text_type"] in ("B", "U", "BU"):
            styles["text_type"] = style_json["text_type"]

    return styles
