from ctypes import *
from .dp_utils import *
from .link_status_common import DpLinkEncoding
from UniTAP.common.video_mode import VideoMode


class DpMsa(Structure):
    class PixelAttributes(Structure):
        _fields_ = [
            ("n_video_or_freq_low", c_uint32, 24),
            ("", c_uint32, 8),
            ("m_video_or_freq_high", c_uint32, 24),
            ("", c_uint32, 8),
        ]

    _fields_ = [
        ('attributes', PixelAttributes),
        ('h_total', c_uint16),
        ('v_total', c_uint16),
        ('h_active', c_uint16),
        ('v_active', c_uint16),
        ('h_sync', c_int16),
        ('v_sync', c_int16),
        ('h_sync_start', c_uint16),
        ('v_sync_start', c_uint16),
        ('misc0', c_uint8),
        ('misc1', c_uint8),
        ('vbid', c_uint8),
        ('port_number', c_uint8),

        ('vsc_sdp_db16', c_uint8),
        ('vsc_sdp_db17', c_uint8),
        ('vsc_sdp_db18', c_uint8),
        ('', c_uint8),

        ('', c_uint32 * 8)
    ]


class DpCrc(Structure):
    _fields_ = [
        ('r', c_uint16),
        ('g', c_uint16),
        ('b', c_uint16),
        ('', c_uint16)
    ]


class VCPTable(Structure):
    _fields_ = [
        ('port_number', c_int),
        ('stream_id', c_int),
        ('req_pbn', c_int),
        ('alloc_pbn', c_int),
        ('first_slot', c_int),
        ('slot_num', c_int),
        ('reserved1', c_int),
        ('reserved2', c_int),
    ]


class LinkVoltageSwing(Structure):
    _fields_ = [
        ('vs_l0', c_uint32, 2),
        ('', c_uint32, 6),
        ('vs_l1', c_uint32, 2),
        ('', c_uint32, 6),
        ('vs_l2', c_uint32, 2),
        ('', c_uint32, 1),
        ('vs_l3', c_uint32, 2),
        ('', c_uint32, 6),
    ]


class LinkPreEmphasis(Structure):
    _fields_ = [
        ('pe_l0', c_uint32, 2),
        ('', c_uint32, 6),
        ('pe_l1', c_uint32, 2),
        ('', c_uint32, 6),
        ('pe_l2', c_uint32, 2),
        ('', c_uint32, 1),
        ('pe_l3', c_uint32, 2),
        ('', c_uint32, 6),
    ]


class MSAInfo:

    def __init__(self, dp_msa: DpMsa):
        self.n_video = dp_msa.attributes.n_video_or_freq_low
        self.m_video = dp_msa.attributes.m_video_or_freq_high
        self.video_frequency = (dp_msa.attributes.m_video_or_freq_high << 24) | dp_msa.attributes.n_video_or_freq_low
        self.h_total = dp_msa.h_total
        self.v_total = dp_msa.v_total
        self.h_active = dp_msa.h_active
        self.v_active = dp_msa.v_active
        self.h_sync = dp_msa.h_sync
        self.v_sync = dp_msa.v_sync
        self.h_sync_start = dp_msa.h_sync_start
        self.v_sync_start = dp_msa.v_sync_start
        self.misc0 = dp_msa.misc0
        self.misc1 = dp_msa.misc1
        self.vbid = dp_msa.vbid
        self.port_number = dp_msa.port_number

        self.vsc_sdp_db16 = dp_msa.vsc_sdp_db16
        self.vsc_sdp_db17 = dp_msa.vsc_sdp_db17
        self.vsc_sdp_db18 = dp_msa.vsc_sdp_db18

    def is_eq(self, other, enc128b132b: bool) -> bool:
        if enc128b132b:
            if self.video_frequency != other.video_frequency:
                return False
        else:
            # m_video changes frequently
            if self.n_video != other.n_video:
                return False

        return self.h_total == other.h_total and self.v_total == other.v_total and self.h_active == other.h_active and \
               self.v_active == other.v_active and self.h_sync == other.h_sync and self.v_sync == other.v_sync and \
               self.h_sync_start == other.h_sync_start and self.v_sync_start == other.v_sync_start and \
               self.misc0 == other.misc0 and self.misc1 == other.misc1 and self.vbid == other.vbid and \
               self.vsc_sdp_db16 == self.vsc_sdp_db16 and self.vsc_sdp_db17 == self.vsc_sdp_db17 and \
               self.vsc_sdp_db18 == self.vsc_sdp_db18


def msa_info_to_video_mode(msa_info: MSAInfo, f_ls_clock: float, link_encoding: DpLinkEncoding):
    video_mode = VideoMode()

    video_mode.timing.hactive = msa_info.h_active
    video_mode.timing.vactive = msa_info.v_active
    video_mode.timing.htotal = msa_info.h_total
    video_mode.timing.vtotal = msa_info.v_total
    video_mode.timing.hstart = msa_info.h_sync_start
    video_mode.timing.vstart = msa_info.v_sync_start
    video_mode.timing.hswidth = msa_info.h_sync
    video_mode.timing.vswidth = msa_info.v_sync

    video_mode.color_info.colorimetry = get_vm_colorimetry(msa_info.vsc_sdp_db16)
    video_mode.color_info.color_format = get_vm_color_format(msa_info.vsc_sdp_db16)
    video_mode.color_info.bpc = get_vm_bpc(msa_info.vsc_sdp_db16, msa_info.vsc_sdp_db17)
    video_mode.color_info.dynamic_range = get_vm_dynamic_range(msa_info.vsc_sdp_db17)

    frame_rate = 0
    if (msa_info.v_total * msa_info.h_total) > 0:
        if link_encoding == DpLinkEncoding.LE_8b10b and msa_info.n_video > 0:
            f_strm_clock = f_ls_clock * (msa_info.m_video / msa_info.n_video)
            frame_rate = 1000 * f_strm_clock / (msa_info.v_total * msa_info.h_total)
        elif link_encoding == DpLinkEncoding.LE_128b132b:
            frame_rate = 1000 * msa_info.video_frequency / (msa_info.v_total * msa_info.h_total)
        else:
            pass

    video_mode.timing.frame_rate = round(frame_rate)

    return video_mode
