# Standard library imports ...
from collections import OrderedDict
import datetime
import struct

# Third party library imports ...
import numpy as np


class _ICCProfile(object):
    """Container for ICC profile information."""

    profile_class = {
        b"scnr": "input device profile",
        b"mntr": "display device profile",
        b"prtr": "output device profile",
        b"link": "devicelink profile",
        b"spac": "colorspace conversion profile",
        b"abst": "abstract profile",
        b"nmcl": "name colour profile",
    }

    colour_space_dict = {
        b"XYZ ": "XYZ",
        b"Lab ": "Lab",
        b"Luv ": "Luv",
        b"YCbr": "YCbCr",
        b"Yxy ": "Yxy",
        b"RGB ": "RGB",
        b"GRAY": "gray",
        b"HSV ": "hsv",
        b"HLS ": "hls",
        b"CMYK": "CMYK",
        b"CMY ": "cmy",
        b"2CLR": "2colour",
        b"3CLR": "3colour",
        b"4CLR": "4colour",
        b"5CLR": "5colour",
        b"6CLR": "6colour",
        b"7CLR": "7colour",
        b"8CLR": "8colour",
        b"9CLR": "9colour",
        b"ACLR": "10colour",
        b"BCLR": "11colour",
        b"CCLR": "12colour",
        b"DCLR": "13colour",
        b"ECLR": "14colour",
        b"FCLR": "15colour",
    }

    rendering_intent_dict = {
        0: "perceptual",
        1: "media-relative colorimetric",
        2: "saturation",
        3: "ICC-absolute colorimetric",
    }

    def __init__(self, read_buffer):
        self._raw_buffer = read_buffer
        header = OrderedDict()

        data = struct.unpack(">IIBB", self._raw_buffer[0:10])
        header["Size"] = data[0]
        header["Preferred CMM Type"] = data[1]
        major = data[2]
        minor = (data[3] & 0xF0) >> 4
        bugfix = data[3] & 0x0F
        header["Version"] = f"{major}.{minor}.{bugfix}"

        header["Device Class"] = self.profile_class[self._raw_buffer[12:16]]
        header["Color Space"] = self.colour_space_dict[self._raw_buffer[16:20]]
        data = self.colour_space_dict[self._raw_buffer[20:24]]
        header["Connection Space"] = data

        data = struct.unpack(">HHHHHH", self._raw_buffer[24:36])
        try:
            header["Datetime"] = datetime.datetime(*data)
        except ValueError:
            header["Datetime"] = None
        header["File Signature"] = read_buffer[36:40].decode("utf-8")
        if read_buffer[40:44] == b"\x00\x00\x00\x00":
            header["Platform"] = "unrecognized"
        else:
            header["Platform"] = read_buffer[40:44].decode("utf-8")

        (fval,) = struct.unpack(">I", read_buffer[44:48])
        header["Flags"] = (
            f"{'' if fval & 0x01 else 'not '}embedded, "
            f"{'cannot' if fval & 0x02 else 'can'} be used independently"
        )

        header["Device Manufacturer"] = read_buffer[48:52].decode("utf-8")
        if read_buffer[52:56] == b"\x00\x00\x00\x00":
            device_model = ""
        else:
            device_model = read_buffer[52:56].decode("utf-8")
        header["Device Model"] = device_model

        (val,) = struct.unpack(">Q", read_buffer[56:64])
        attr = (
            f"{'transparency' if val & 0x01 else 'reflective'}, "
            f"{'matte' if val & 0x02 else 'glossy'}, "
            f"{'negative' if val & 0x04 else 'positive'} media polarity, "
            f"{'black and white' if val & 0x08 else 'color'} media"
        )
        header["Device Attributes"] = attr

        (rval,) = struct.unpack(">I", read_buffer[64:68])
        try:
            header["Rendering Intent"] = self.rendering_intent_dict[rval]
        except KeyError:
            header["Rendering Intent"] = "unknown"

        data = struct.unpack(">iii", read_buffer[68:80])
        header["Illuminant"] = np.array(data, dtype=np.float64) / 65536

        if read_buffer[80:84] == b"\x00\x00\x00\x00":
            creator = "unrecognized"
        else:
            creator = read_buffer[80:84].decode("utf-8")
        header["Creator"] = creator

        if header["Version"][0] == "4":
            header["Profile Id"] = read_buffer[84:100]

        # Final 27 bytes are reserved.
        self.header = header
