import json
import re
import numpy as np
from .dng import DNGTags, Tag
from .defs import *


class RaspberryPiCameraModels:
    Raspberry_Pi_Camera_V1 = "Raspberry Pi Camera V1"
    Raspberry_Pi_Camera_V2 = "Raspberry Pi Camera V2"
    Raspberry_Pi_High_Quality_Camera = "Raspberry Pi High Quality Camera"


class BaseCameraModel():
    def __init__(self) -> None:
        self.tags = DNGTags()
        self.model = "BaseCameraModel"

    def __settings__(self) -> None:
        pass

    # TODO
    def fromDict(dict : dict) -> None:  
        pass

    # TODO
    def fromJson(jsn : str) -> None:
        parameters = json.loads(jsn)

    def __repr__(self) -> DNGTags:
        return self.tags

    def __str__(self) -> str:
        return str(self.model)

class Picamera2Camera(BaseCameraModel):
    def __init__(self, fmt : dict, metadata: dict, model : str = "PiDNG / PiCamera2") -> None:
        super().__init__() 
        self.model = model
        self.fmt = fmt
        self.metadata = metadata
        self.__settings__()

    def __settings__(self) -> None:

        width, height = self.fmt.get("size", (0,0))
        fmt_str = self.fmt.get("format","").split("_")[0]
        bpp = int(re.search(r'\d+', fmt_str).group())
        self.fmt["bpp"] = bpp

        black_levels = list()
        for val in self.metadata.get("SensorBlackLevels", (0)):
            black_levels.append((val >> (16 - bpp)))
        
        camera_calibration = [[1, 1], [0, 1], [0, 1],
                              [0, 1], [1, 1], [0, 1],
                              [0, 1], [0, 1], [1, 1]]

        color_gain_div = 10000
        gain_r, gain_b = self.metadata.get("ColourGains",(1.0, 1.0))
        gain_matrix = np.array([[gain_r, 0, 0],
                                [0, 1.0, 0],
                                [0, 0, gain_b]])
        gain_r = int(gain_r * color_gain_div)
        gain_b = int(gain_b * color_gain_div)
        as_shot_neutral = [[color_gain_div, gain_r], [color_gain_div, color_gain_div], [color_gain_div, gain_b]]

        ccm1 = list()
        ccm = self.metadata.get("ColourCorrectionMatrix", (1, 0, 0, 0, 1, 0, 0, 0, 1))
	# This maxtrix from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
        rgb_to_xyz = np.array([[0.4124564, 0.3575761, 0.1804375],
		               [0.2126729, 0.7151522, 0.0721750],
		               [0.0193339, 0.1191920, 0.9503041]])
        ccm_matrix = np.array(ccm).reshape((3, 3))
        ccm = np.linalg.inv(rgb_to_xyz.dot(ccm_matrix).dot(gain_matrix))

        for color in ccm.flatten().tolist():
            ccm1.append((int(color*color_gain_div), color_gain_div))

        ci1 = CalibrationIlluminant.D65

        baseline_exp = 1

        model = self.model
        make = "RaspberryPi"

        profile_name = "PiDNG / PiCamera2 Profile"
        profile_embed = 3

        self.orientation = 1
        self.cfaPattern = None
        if "BGGR" in fmt_str:
            self.cfaPattern = CFAPattern.BGGR
        elif "GBRG" in fmt_str:
            self.cfaPattern = CFAPattern.GBRG
        elif "GRBG" in fmt_str:
            self.cfaPattern = CFAPattern.GRBG
        elif "RGGB" in fmt_str:
            self.cfaPattern = CFAPattern.RGGB

        exposure_time = int(1/(self.metadata["ExposureTime"] * 0.000001))
        total_gain = self.metadata["AnalogueGain"] * self.metadata["DigitalGain"]
        iso = int(total_gain * 100)

        self.tags.set(Tag.PhotographicSensitivity, [iso]) 
        self.tags.set(Tag.ExposureTime, [[1,exposure_time]])  
        self.tags.set(Tag.RawDataUniqueID, str(self.metadata["SensorTimestamp"]).encode("ascii"))
        self.tags.set(Tag.ImageWidth, width)
        self.tags.set(Tag.ImageLength, height)
        self.tags.set(Tag.Orientation, self.orientation)
        self.tags.set(Tag.SamplesPerPixel, 1)
        self.tags.set(Tag.BitsPerSample, bpp)
        self.tags.set(Tag.WhiteLevel, ((1 << bpp) -1) )
        self.tags.set(Tag.BaselineExposure, [[baseline_exp,1]])
        self.tags.set(Tag.Make, make)
        self.tags.set(Tag.Model, model)
        self.tags.set(Tag.ProfileName, profile_name)
        self.tags.set(Tag.ProfileEmbedPolicy, [profile_embed])

        if self.cfaPattern:
            # For colour Bayer sensors
            self.tags.set(Tag.BlackLevelRepeatDim, [2,2])
            self.tags.set(Tag.BlackLevel, black_levels)
            self.tags.set(Tag.PhotometricInterpretation, PhotometricInterpretation.Color_Filter_Array)
            self.tags.set(Tag.CFARepeatPatternDim, [2,2])
            self.tags.set(Tag.CFAPattern, self.cfaPattern)
            self.tags.set(Tag.ColorMatrix1, ccm1)
            self.tags.set(Tag.CameraCalibration1, camera_calibration)
            self.tags.set(Tag.CameraCalibration2, camera_calibration)
            self.tags.set(Tag.CalibrationIlluminant1, ci1)
            self.tags.set(Tag.AsShotNeutral, as_shot_neutral)
        else:
            # For mono raw sensors
            self.tags.set(Tag.BlackLevelRepeatDim, [1,1])
            self.tags.set(Tag.BlackLevel, [black_levels[0]])
            self.tags.set(Tag.PhotometricInterpretation, PhotometricInterpretation.Linear_Raw)
        
class RaspberryPiHqCamera(BaseCameraModel):
    def __init__(self, sensor_mode : int, cfaPattern=CFAPattern.BGGR, orientation=Orientation.Horizontal) -> None:
        super().__init__()
        self.model = RaspberryPiCameraModels.Raspberry_Pi_High_Quality_Camera
        self.mode = sensor_mode
        self.orientation = orientation
        self.cfaPattern = cfaPattern
        self.__settings__()

    def __settings__(self) -> None:
        width = None
        height = None
        bpp = None

        if self.mode == 1:
            width = 2028
            height = 1080
        if self.mode == 2:
            width = 2028
            height = 1520
        if self.mode == 3:
            width = 4056
            height = 3040
        if self.mode == 4:
            width = 1012
            height = 760

        if self.mode < 4:
            bpp = 12
        else: 
            bpp = 10

        profile_name = "Repro 2_5D no LUT - D65 is really 5960K"
        profile_embed = 3

        ccm1 = [[6759, 10000], [-2379, 10000], [751, 10000],
                [-4432, 10000], [13871, 10000], [5465, 10000],
                [-401, 10000], [1664, 10000], [7845, 10000]]

        ccm2 = [[5603, 10000], [-1351, 10000], [-600, 10000],
                [-2872, 10000], [11180, 10000], [2132, 10000],
                [600, 10000], [453, 10000], [5821, 10000]]

        fm1 = [[7889, 10000], [1273, 10000], [482, 10000],
                [2401, 10000], [9705, 10000], [-2106, 10000],
                [-26, 10000], [-4406, 10000], [12683, 10000]]

        fm2 = [[6591, 10000], [3034, 10000], [18, 10000],
                [1991, 10000], [10585, 10000], [-2575, 10000],
                [-493, 10000], [-919, 10000], [9663, 10000]]

        camera_calibration = [[1, 1], [0, 1], [0, 1],
                              [0, 1], [1, 1], [0, 1],
                              [0, 1], [0, 1], [1, 1]]

        gain_r = 2500
        gain_b = 2000

        as_shot_neutral = [[1000, gain_r], [1000, 1000], [1000, gain_b]]

        ci1 = CalibrationIlluminant.Standard_Light_A
        ci2 = CalibrationIlluminant.D65

        model = " ".join(self.model.split(" ")[2:])
        make = " ".join(self.model.split(" ")[:-3])

        baseline_exp = 1

        self.tags.set(Tag.ImageWidth, width)
        self.tags.set(Tag.ImageLength, height)
        self.tags.set(Tag.Orientation, self.orientation)
        self.tags.set(Tag.PhotometricInterpretation, PhotometricInterpretation.Color_Filter_Array)
        self.tags.set(Tag.SamplesPerPixel, 1)
        self.tags.set(Tag.BitsPerSample, bpp)
        self.tags.set(Tag.CFARepeatPatternDim, [2,2])
        self.tags.set(Tag.CFAPattern, self.cfaPattern)
        self.tags.set(Tag.BlackLevel, (4096 >> (16 - bpp)))
        self.tags.set(Tag.WhiteLevel, ((1 << bpp) -1) )
        self.tags.set(Tag.ColorMatrix1, ccm1)
        self.tags.set(Tag.ColorMatrix2, ccm2)
        self.tags.set(Tag.ForwardMatrix1, fm1)
        self.tags.set(Tag.ForwardMatrix2, fm2)
        self.tags.set(Tag.CameraCalibration1, camera_calibration)
        self.tags.set(Tag.CameraCalibration2, camera_calibration)
        self.tags.set(Tag.CalibrationIlluminant1, ci1)
        self.tags.set(Tag.CalibrationIlluminant2, ci2)
        self.tags.set(Tag.BaselineExposure, [[baseline_exp,1]])
        self.tags.set(Tag.AsShotNeutral, as_shot_neutral)
        self.tags.set(Tag.Make, make)
        self.tags.set(Tag.Model, model)
        self.tags.set(Tag.ProfileName, profile_name)
        self.tags.set(Tag.ProfileEmbedPolicy, [profile_embed])


# TODO
class RaspberryPiCameraV2(BaseCameraModel):
    pass



# TODO
class RaspberryPiCameraV1(BaseCameraModel):
    pass
