"""Version: 5.31026.20260322
We use ctypes to call into the miinam.dll/miinam.so API,
the python class Miinam is a thin wrapper class to the native api of miinam.dll/miinam.so.
"""
import sys, ctypes, os.path

MIINAM_MAX = 64

MIINAM_PARA_UNKNOWN                = 0x00
MIINAM_PARA_EXPOTIME               = 0x01    # exposure time
MIINAM_PARA_AGAIN                  = 0x02    # gain
MIINAM_PARA_AEXPOTARGET            = 0x03    # auto exposure target
MIINAM_PARA_TEMP                   = 0x04    # color temperature
MIINAM_PARA_TINT                   = 0x05
MIINAM_PARA_CONTRAST               = 0x06    # contrast
MIINAM_PARA_HUE                    = 0x07    # hue
MIINAM_PARA_SATURATION             = 0x08    # saturation
MIINAM_PARA_BRIGHTNESS             = 0x09    # brightness
MIINAM_PARA_GAMMA                  = 0x0a    # gamma
MIINAM_PARA_AEXPO                  = 0x0b    # auto exposure
MIINAM_PARA_AWB                    = 0x0c    # XCAM1080P:once;  XCAM4K:(0:manual;1:global auto;2:roi)
MIINAM_PARA_BINSKIP                = 0x0d    # bin / skip
MIINAM_PARA_HZ                     = 0x0e    # power supply: 0 -> 60HZ AC;  1 -> 50Hz AC;   2 -> DC
MIINAM_PARA_BPS                    = 0x0f    # bits per second, kbps
MIINAM_PARA_KEYFRAME               = 0x10    # key frame interval
MIINAM_PARA_LOWLIGHTCOMPENSATION   = 0x11    # low light compensation
MIINAM_PARA_SHARPNESS              = 0x12    # sharpness
MIINAM_PARA_WBREDGAIN              = 0x13    # white balance red gain
MIINAM_PARA_WBGREENGAIN            = 0x14    # white balance green gain
MIINAM_PARA_WBBLUEGAIN             = 0x15    # white balance blue gain
MIINAM_PARA_DENOISE                = 0x16    # denoise
MIINAM_PARA_APSTA                  = 0x17    # ap/sta
MIINAM_PARA_CODEC                  = 0x18    # codec, H264, H265, etc
MIINAM_PARA_AFPOSITION             = 0x19    # auto focus sensor board positon
MIINAM_PARA_AFMODE                 = 0x1a    # auto focus mode (0:manul focus; 1:auto focus; 2:once focus; 3:conjugate calibration)
MIINAM_PARA_AFZONE                 = 0x1b    # auto focus zone:
                                              #   the whole resolution is divided in w * h zones:
                                              #     w = imax >> 16
                                              #     h = imax & 0xffff
MIINAM_PARA_AFFEEDBACK             = 0x1c    # auto focus information feedback
                                              # AFDM:
                                              #   0: unknown
                                              #   1: focused
                                              #   2: focusing
                                              #   3: defocuse (out of focus)
                                              #   4: up (workbench move up)
                                              #   5: down (workbench move down)
                                              #
                                              # EFL:
                                              #   NA = 0x0,           /* Not available */
                                              #   PEAKPOINT = 0x1,    /* Focus completed, find the focus position */
                                              #   DEFOCUS      = 0x2, /* End of focus, defocus */
                                              #   NEAR         = 0x3, /* Focusing ended, object too close */
                                              #   FAR          = 0x4, /* Focusing ended, object too far */
                                              #   ROICHANGED   = 0x5, /* Focusing ends, roi changes */
                                              #   SCENECHANGED = 0x6, /* Focusing ends, scene changes */
                                              #   MODECHANGED  = 0x7, /* The end of focusing and the change in focusing mode is usually determined by the user moderator */
                                              #   UNFINISH     = 0x8, /* The focus is not complete. At the beginning of focusing, it will be set as incomplete */
MIINAM_PARA_AFPOSITION_ABSOLUTE    = 0x1d    ## absolute auto focus sensor board positon
MIINAM_PARA_STATUS                 = 0x1e    ## status
MIINAM_PARA_EVENT                  = 0x1f    ## event
MIINAM_PARA_WBROILEFT              = 0x20    ## white balance roi left
MIINAM_PARA_WBROITOP               = 0x21    ## white balance roi top
MIINAM_PARA_WBROIWIDTH             = 0x22    # white balance roi width
MIINAM_PARA_WBROIHEIGHT            = 0x23    # white balance roi height
MIINAM_PARA_VFLIP                  = 0x24    # vertical flip
MIINAM_PARA_HFLIP                  = 0x25    # horizontal flip
MIINAM_PARA_CHROME                 = 0x26    # monochromatic mode
MIINAM_PARA_SIZE                   = 0x27    # video width & height
MIINAM_PARA_LIGHTADJUSTMENT        = 0x28    # light source brightness adjustment
MIINAM_PARA_ZOOM                   = 0x29
MIINAM_PARA_EF_MODE                = 0x2a
MIINAM_PARA_EF_FL                  = 0x2b
MIINAM_PARA_EF_APERTURE            = 0x2c    # 24~16bit:Cur, 15~8bit:Min, 7~0bit:Max
MIINAM_PARA_EF_FOCUS_MAX           = 0x2d
MIINAM_PARA_EF_LENS_ID             = 0x2e
MIINAM_PARA_EF_AFMF                = 0x2f
MIINAM_PARA_EF_WD_ENABLE           = 0x30
MIINAM_PARA_EF_WD_NEAR             = 0x31
MIINAM_PARA_EF_WD_FAR              = 0x32

MIINAM_PARA_CHROME_LOCAL           = 0x80    # local monochromatic mode
MIINAM_PARA_VFLIP_LOCAL            = 0x81    # local vertical flip
MIINAM_PARA_HFLIP_LOCAL            = 0x82    # local horizontal flip
MIINAM_PARA_NEGATIVE_LOCAL         = 0x83    # local negative film
MIINAM_PARA_FORMAT_LOCAL           = 0x84    # output format: 0 => BGR888, 1 => BGRA8888, 2 => RGB888, 3 => RGBA8888, 4 => RAW; default: 0
                                              # MUST be set BEFORE StartXXXX

MIINAM_PARA_STATUS_RECORDING       = 0x00000001      # recording
MIINAM_PARA_STATUS_SD              = 0x00000002      # sd card available
MIINAM_PARA_STATUS_SD_FULL         = 0x00000004      # sd card full

MIINAM_PARA_EVENT_FAT4G            = 0x00000001      # file size limit 4g in FAT32

MIINAM_STATE_INITING               = 0x00    # initialization
MIINAM_STATE_NORMAL                = 0x01    # normal
MIINAM_STATE_UNREACHABLE           = 0x02    # network not reachable

MIINAM_FLAG_WIFI_AP                = 0x00000001
MIINAM_FLAG_WIFI_STA               = 0x00000002
MIINAM_FLAG_ETHERNET               = 0x00000004
MIINAM_FLAG_CAPTURE                = 0x00000008  # support the ability of capture image from camera
MIINAM_FLAG_AWBCHECKMODE           = 0x00000010  # auto white balance: check mode vs 'once' mode
MIINAM_FLAG_UVC                    = 0x00000020  # uvc camera */
MIINAM_FLAG_WBGAIN                 = 0x00000040  # white balance gain mode or temp tint mode
MIINAM_FLAG_MULTICAST              = 0x00000080  # RTSP/RTP multicast
MIINAM_FLAG_AF                     = 0x00000100  # support auto focus
MIINAM_FLAG_SD_LIST                = 0x00000200  # support to list sd card
MIINAM_FLAG_SD                     = 0x00000400  # support sd card
MIINAM_FLAG_WBROI                  = 0x00000800  # white balance: 0:manual;1:global auto;2:roi
MIINAM_FLAG_STA_SUPPORT            = 0x00001000  # wifi camera has sta mode, app should have sta ssid & password function
MIINAM_FLAG_RTP_OVER_RTSP          = 0x00002000  # rtp over rtsp
MIINAM_FLAG_HZ_AUTOEXPO            = 0x00004000  # enable auto exposure when 50/60 hz
MIINAM_FLAG_AFDM                   = 0x00008000
MIINAM_FLAG_EFL                    = 0x00010000
MIINAM_FLAG_CAPTURERAW             = 0x00020000  # capture raw image

MIINAM_EVENT_ENUM                  = 0x01    # enum
MIINAM_EVENT_WIFI                  = 0x02    # wifi
MIINAM_EVENT_PARA                  = 0x03    # parameter change MIINAM_PARA_xxxx
MIINAM_EVENT_IMAGE                 = 0x04    # image
MIINAM_EVENT_LISTWIFI              = 0x05    # list wifi finished
MIINAM_EVENT_LISTDIR               = 0x07    # list dir
MIINAM_EVENT_THUMBNAIL             = 0x08    # thumbnail
MIINAM_EVENT_DIRCHANGE             = 0x09    # dir change notify
MIINAM_EVENT_RECORDSTART           = 0x0a    # record start
MIINAM_EVENT_RECORDSTOP            = 0x0b    # record stop
MIINAM_EVENT_DATETIME              = 0x0c    # date time
MIINAM_EVENT_ERROR                 = 0x80    # error
MIINAM_EVENT_EOF                   = 0x81    # end of file

def TDIBWIDTHBYTES(bits):
    return ((bits + 31) // 32 * 4)
    
class miinam_range:
    def __init__(self, val):
        self.idisable = val.idisable                # 0 = "support this feature", 1 = "not support"
        self.imin = val.imin                        # minimum value
        self.imax = val.imax                        # maximum value
        self.idef = val.idef                        # default value

class miinam_device:
    def __init__(self, val):
        self.id = val.id                            # unique camera id, used for Miinam_Open
        self.sn = val.sn.decode('ascii')            # serial number
        self.name = val.name.decode('ascii')
        self.model = val.model.decode('ascii')
        self.version = val.version.decode('ascii')
        self.addr = val.addr.decode('ascii')        # ip
        self.url = val.url.decode('ascii')          # playback url, such as rtsp://xxxx/yyyy
        self.state = val.state                      # MIINAM_STATE_xxx
        self.flag = val.flag                        # MIINAM_FLAG_xxx
        self.range = []
        for i in range(0, len(val.range)):
            self.range.append(miinam_range(val.range[i]))

class miinam_wifi:
    def __init__(self, ssid, password):
        self.ssid = ssid
        self.password = val.password

class miinam_dirchange:
    def __init__(self, type, name, newname):
        self.type = type                            # 0 => add, 1 => del, 2 => rename
        self.name = name
        self.newname = newname

if sys.platform == 'win32':
    class HRESULTException(OSError):
        def __init__(self, hr):
            OSError.__init__(self, None, ctypes.FormatError(hr).strip(), None, hr)
            self.hr = hr
else:
    class HRESULTException(Exception):
        def __init__(self, hr):
            self.hr = hr

class CLib:
    class _device(ctypes.Structure):
        pass

    class _range(ctypes.Structure):
        _fields_ = [("idisable", ctypes.c_int),
                    ("imin", ctypes.c_int),
                    ("imax", ctypes.c_int),
                    ("idef", ctypes.c_int)]

    class _wifi(ctypes.Structure):
        _fields_ = [("ssid", ctypes.c_char * 64),
                    ("password", ctypes.c_char * 64)]

    class _eventextra(ctypes.Structure):
        _fields_ = [("result", ctypes.c_int),
                    ("ctx", ctypes.c_void_p),
                    ("ptr", ctypes.c_void_p),
                    ("length", ctypes.c_uint)]

    class _dirchange(ctypes.Structure):
        _fields_ = [("type", ctypes.c_uint),
                    ("name", ctypes.c_char * 256),
                    ("newname", ctypes.c_char * 256)]

    class _BITMAPINFOHEADER(ctypes.Structure):
        _fields_ = [("biSize", ctypes.c_uint),
                    ("biWidth", ctypes.c_int),
                    ("biHeight", ctypes.c_int),
                    ("biPlanes", ctypes.c_ushort),
                    ("biBitCount", ctypes.c_ushort),
                    ("biCompression", ctypes.c_uint),
                    ("biSizeImage", ctypes.c_uint),
                    ("biXPelsPerMeter", ctypes.c_int),
                    ("biYPelsPerMeter", ctypes.c_int),
                    ("biClrUsed", ctypes.c_uint),
                    ("biClrImportant", ctypes.c_uint)]

    if sys.platform == 'win32':
        _EVENT_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, ctypes.POINTER(_eventextra))
        _CAPTURE_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, ctypes.POINTER(_BITMAPINFOHEADER), ctypes.c_void_p)
    else:
        _EVENT_CALLBACK = ctypes.CFUNCTYPE(None, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, ctypes.POINTER(_eventextra))
        _CAPTURE_CALLBACK = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, ctypes.POINTER(_BITMAPINFOHEADER), ctypes.c_void_p)
    _lib = None

    @staticmethod
    def __errcheck(result, fun, args):
        if result < 0:
            raise HRESULTException(result)
        return args

    @classmethod
    def initlib(cls):
        if cls._lib is None:
            try: # Firstly try to load the library in the directory where this file is located
                dir = os.path.dirname(os.path.realpath(__file__))
                if sys.platform == 'win32':
                    cls._lib = ctypes.windll.LoadLibrary(os.path.join(dir, 'miinam.dll'))
                elif sys.platform.startswith('linux'):
                    cls._lib = ctypes.cdll.LoadLibrary(os.path.join(dir, 'libmiinam.so'))
                else:
                    cls._lib = ctypes.cdll.LoadLibrary(os.path.join(dir, 'libmiinam.dylib'))
            except OSError:
                pass

            if cls._lib is None:
                if sys.platform == 'win32':
                    cls._lib = ctypes.windll.LoadLibrary('miinam.dll')
                elif sys.platform.startswith('linux'):
                    cls._lib = ctypes.cdll.LoadLibrary('libmiinam.so')
                else:
                    cls._lib = ctypes.cdll.LoadLibrary('libmiinam.dylib')

            cls._device._fields_ = [
                    ("id", ctypes.c_char * 64),
                    ("sn", ctypes.c_char * 64),
                    ("name", ctypes.c_char * 64),
                    ("model", ctypes.c_char * 64),
                    ("version", ctypes.c_char * 64),
                    ("addr", ctypes.c_char * 64),
                    ("url", ctypes.c_char * 256),
                    ("state", ctypes.c_uint),
                    ("flag", ctypes.c_uint),
                    ("range", cls._range * 64)]

            cls._lib.Miinam_Version.restype = ctypes.c_char_p
            cls._lib.Miinam_Init.restype = None
            cls._lib.Miinam_Fini.restype = None
            cls._lib.Miinam_Enum.restype = ctypes.c_uint
            cls._lib.Miinam_Open.restype = ctypes.c_void_p
            cls._lib.Miinam_Open_ByIndex.restype = ctypes.c_void_p
            cls._lib.Miinam_Close.restype = None
            cls._lib.Miinam_PullImage.restype = ctypes.c_int
            cls._lib.Miinam_StartPullModeWithCallback.restype = ctypes.c_int
            cls._lib.Miinam_Stop.restype = ctypes.c_int
            cls._lib.Miinam_Pause.restype = ctypes.c_int
            cls._lib.Miinam_Capture.restype = ctypes.c_int
            cls._lib.Miinam_Capture_ById.restype = ctypes.c_int
            cls._lib.Miinam_get_Device.restype = cls._device
            cls._lib.Miinam_get_Size.restype = ctypes.c_int
            cls._lib.Miinam_get_CapSize.restype = ctypes.c_int
            cls._lib.Miinam_get_FourCC.restype = ctypes.c_int
            cls._lib.Miinam_Record.restype = ctypes.c_int
            cls._lib.Miinam_put_Para.restype = ctypes.c_int
            cls._lib.Miinam_get_Para.restype = ctypes.c_int
            cls._lib.Miinam_put_Wifi.restype = ctypes.c_int
            cls._lib.Miinam_get_Wifi.restype = ctypes.c_int
            cls._lib.Miinam_list_Wifi.restype = ctypes.c_int
            cls._lib.Miinam_list_Dir.restype = ctypes.c_int
            cls._lib.Miinam_get_Thumbnail.restype = ctypes.c_int
            cls._lib.Miinam_change_Dir.restype = ctypes.c_int
            cls._lib.Miinam_RecordStart.restype = ctypes.c_int
            cls._lib.Miinam_RecordStop.restype = ctypes.c_int
            cls._lib.Miinam_get_DateTime.restype = ctypes.c_int
            cls._lib.Miinam_put_DateTime.restype = ctypes.c_int
            cls._lib.Miinam_put_Para_ById.restype = ctypes.c_int
            cls._lib.Miinam_get_Para_ById.restype = ctypes.c_int
            cls._lib.Miinam_get_Size_ById.restype = ctypes.c_int
            cls._lib.Miinam_PriFlag.restype = None
            cls._lib.Miinam_add_Ip.restype = None
            cls._lib.Miinam_del_Ip.restype = None
            cls._lib.Miinam_OpenVideo.restype = ctypes.c_void_p
            cls._lib.Miinam_CloseVideo.restype = None
            cls._lib.Miinam_WriteVideo.restype = ctypes.c_int
            cls._lib.Miinam_EncodeVideo.restype = ctypes.c_int

            cls._lib.Miinam_Enum.errcheck = cls.__errcheck
            cls._lib.Miinam_PullImage.errcheck = cls.__errcheck
            cls._lib.Miinam_StartPullModeWithCallback.errcheck = cls.__errcheck
            cls._lib.Miinam_Stop.errcheck = cls.__errcheck
            cls._lib.Miinam_Pause.errcheck = cls.__errcheck
            cls._lib.Miinam_Capture.errcheck = cls.__errcheck
            cls._lib.Miinam_Capture_ById.errcheck = cls.__errcheck
            cls._lib.Miinam_get_Device.errcheck = cls.__errcheck
            cls._lib.Miinam_get_Size.errcheck = cls.__errcheck
            cls._lib.Miinam_get_CapSize.errcheck = cls.__errcheck
            cls._lib.Miinam_get_FourCC.errcheck = cls.__errcheck
            cls._lib.Miinam_Record.errcheck = cls.__errcheck
            cls._lib.Miinam_put_Para.errcheck = cls.__errcheck
            cls._lib.Miinam_get_Para.errcheck = cls.__errcheck
            cls._lib.Miinam_put_Wifi.errcheck = cls.__errcheck
            cls._lib.Miinam_get_Wifi.errcheck = cls.__errcheck
            cls._lib.Miinam_list_Wifi.errcheck = cls.__errcheck
            cls._lib.Miinam_list_Dir.errcheck = cls.__errcheck
            cls._lib.Miinam_get_Thumbnail.errcheck = cls.__errcheck
            cls._lib.Miinam_change_Dir.errcheck = cls.__errcheck
            cls._lib.Miinam_RecordStart.errcheck = cls.__errcheck
            cls._lib.Miinam_RecordStop.errcheck = cls.__errcheck
            cls._lib.Miinam_get_DateTime.errcheck = cls.__errcheck
            cls._lib.Miinam_put_DateTime.errcheck = cls.__errcheck
            cls._lib.Miinam_put_Para_ById.errcheck = cls.__errcheck
            cls._lib.Miinam_get_Para_ById.errcheck = cls.__errcheck
            cls._lib.Miinam_get_Size_ById.errcheck = cls.__errcheck
            cls._lib.Miinam_WriteVideo.errcheck = cls.__errcheck
            cls._lib.Miinam_EncodeVideo.errcheck = cls.__errcheck

            cls._lib.Miinam_Version.argtypes = None
            cls._lib.Miinam_Init.argtypes = [cls._EVENT_CALLBACK, ctypes.c_void_p]
            cls._lib.Miinam_Fini.argtypes = None
            cls._lib.Miinam_Enum.argtypes = [ctypes.POINTER(cls._device), ctypes.c_int]
            cls._lib.Miinam_Open.argtypes = [ctypes.c_char_p]
            cls._lib.Miinam_Open_ByIndex.argtypes = [ctypes.c_uint]
            cls._lib.Miinam_Close.argtypes = [ctypes.c_void_p]

            cls._lib.Miinam_PullImage.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint)]
            cls._lib.Miinam_StartPullModeWithCallback.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
            cls._lib.Miinam_Stop.argtypes = [ctypes.c_void_p]
            cls._lib.Miinam_Pause.argtypes = [ctypes.c_void_p, ctypes.c_int]
            cls._lib.Miinam_Capture.argtypes = [ctypes.c_void_p, ctypes.c_char_p, cls._CAPTURE_CALLBACK, ctypes.c_void_p]
            cls._lib.Miinam_Capture_ById.argtypes = [ctypes.c_char_p, ctypes.c_char_p, cls._CAPTURE_CALLBACK, ctypes.c_void_p]
            cls._lib.Miinam_get_Device.argtypes = [ctypes.c_void_p]
            cls._lib.Miinam_get_Size.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)]
            cls._lib.Miinam_get_CapSize.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)]
            cls._lib.Miinam_get_FourCC.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)]
            cls._lib.Miinam_Record.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
            cls._lib.Miinam_put_Para.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_int]
            cls._lib.Miinam_get_Para.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_int)]
            cls._lib.Miinam_put_Wifi.argtypes = [ctypes.c_char_p, cls._wifi]
            cls._lib.Miinam_get_Wifi.argtypes = [ctypes.c_char_p]
            cls._lib.Miinam_list_Wifi.argtypes = [ctypes.c_char_p, ctypes.c_void_p]
            cls._lib.Miinam_list_Dir.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p]
            cls._lib.Miinam_get_Thumbnail.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p]
            cls._lib.Miinam_change_Dir.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(cls._dirchange), ctypes.c_uint]
            cls._lib.Miinam_RecordStart.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_void_p]
            cls._lib.Miinam_RecordStop.argtypes = [ctypes.c_char_p, ctypes.c_void_p]
            cls._lib.Miinam_get_DateTime.argtypes = [ctypes.c_char_p, ctypes.c_void_p]
            cls._lib.Miinam_put_DateTime.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.c_void_p]
            cls._lib.Miinam_put_Para_ById.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.c_int]
            cls._lib.Miinam_get_Para_ById.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_int)]
            cls._lib.Miinam_get_Size_ById.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)]
            cls._lib.Miinam_PriFlag.argtypes = [ctypes.c_uint, ctypes.c_uint]
            cls._lib.Miinam_add_Ip.argtypes = [ctypes.c_void_p]
            cls._lib.Miinam_del_Ip.argtypes = [ctypes.c_void_p]
            
            cls._lib.Miinam_OpenVideo.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p]
            cls._lib.Miinam_CloseVideo.argtypes = [ctypes.c_void_p]
            cls._lib.Miinam_WriteVideo.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint]
            cls._lib.Miinam_EncodeVideo.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]

class EncVideo(CLib):
    def __init__(self, h):
        """the object of EncVideo must be obtained by classmethod Open, it cannot be obtained by obj = miinam.EncVideo()"""
        self.__h = h

    def __del__(self):
        self.Close()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.Close()

    def __nonzero__(self):
        return self.__h is not None

    def __bool__(self):
        return self.__h is not None

    @classmethod
    def Open(cls, width, height, fps, bitrate, quality, outputFile, codec):
        """the object of miinam must be obtained by classmethod Open or OpenByIndex, it cannot be obtained by obj = miinam.miinam()"""
        """
        outputFile: file format is based on the file extension, .mp4 used for H264/H265, while .avi used for MJPEG or uncompressed video
            If outputFile is null, the compressed video data will not be written to a file.
            Instead, the encoded output will be written to the memory buffer pointed to by outputData through the Miinam_EncodeVideo function.
        codec:
            h264_nvenc, hevc_nvenc: Nvidia GPU
            h264_qsv, hevc_qsv: Intel GPU (x64 only)
            h264_amf, hevc_amf: AMD GPU (x64 only)
            h264_v4l2m2m, hevc_v4l2m2m: V4L2 memory-to-memory device (linux arm64 only)
            libx264: software
            libx265: software
            mjpeg: motion jpeg
            rawvideo: uncompressed
        use : to split extra parameters, such as:
            input format: rgb24, bgr24, rgba, bgra, gray8, such as: "h264_nvenc:rgb24"; default = bgr24
            mono: used for uncompressed avi, "rawvideo:mono", reduce the size of video file by 2/3
            timestamp: fps(use fps for timestamp), tick(use os tick for timestamp), param(use the input value of the function parameter), such as: "timestamp=fps"; default = tick
            stride: 0(padded to a 4-byte boundary, see TDIBWIDTHBYTES), -1(no padding), positive integer(use this specific value); default = 0

        always use Constant Quality Mode
            quality = [1, 100]
            bitrate = 0
        """
        CLib.initlib()
        h = cls._lib.Miinam_OpenVideo(width, height, fps, bitrate, quality, outputFile.encode('utf-8') if outputFile is not None else None, codec.encode("ascii"))
        if h is None:
            return None
        return __class__(h)

    def Close(self):
        if self.__h:
            self._lib.Miinam_CloseVideo(self.__h)
            self.__h = None

    def Write(self, inputData, unTimeStamp):
        """
        unTimeStamp
            avi: ignored, timestamp is always set to fps
            mp4: ignored when timestamp is set to fps or tick
        """
        self._lib.Miinam_WriteVideo(self.__h, inputData, unTimeStamp)

    def Encode(self, inputData, outputData):
        """
        Return value:
            <0 indicates an HRESULT error code;
            =0 means no data is returned;
            >0 indicates the length of the returned data.
        outputData: please ensure that the output buffer is large enough to accommodate the compressed video data.
        """
        self._lib.Miinam_EncodeVideo(self.__h, inputData, outputData)

class Miinam(CLib):
    class cbobj:
        def __init__(self, f, c, i):
            self.fun = f
            self.ctx = c
            self.id = i

    @staticmethod
    def __eventCallbackFun(nEvent, nPara, pCallbackCtx, pExtra):
        o = __class__.__dic.get(pCallbackCtx)
        if o is not None:
            o.fun(nEvent, nPara, o.ctx)

    @staticmethod
    def __captureCallbackFun(result, pData, nLength, pHeader, pCallbackCtx):
        o = __class__.__dic.get(pCallbackCtx)
        if o is not None:
            if pData == 0 or nLength == 0:
                o.fun(result, None, 0, 0, o.ctx)
            else:
                nWidth = nHeight = 0
                if pHeader != 0:
                    nWidth = pHeader.contents.biWidth
                    nHeight = pHeader.contents.biHeight
                bData = (ctypes.c_char * nLength).from_address(pData)
                o.fun(result, bData, nWidth, nHeight, o.ctx)
    __dic = {}
    __evt = None
    __cap = None
    __sid = 0

    @classmethod
    def Version(cls):
        """get the version of this dll/so/dylib, which is: 5.31026.20260322"""
        CLib.initlib()
        return cls._lib.Miinam_Version()

    @classmethod
    def Init(cls, fun, ctx):
        """
        call only once when application startup
        when miinam.dll/libmiinam.so discovery new camera, pCallback will be called.
        fun can be None if the application does not interest this.
        """
        CLib.initlib()
        if cls.__evt is None:
            cls.__evt = CLib._EVENT_CALLBACK(cls.__eventCallbackFun)
            cls.__cap = CLib._CAPTURE_CALLBACK(cls.__captureCallbackFun)
            cls.__dic[0] = cls.cbobj(fun, ctx, 0)
            cls._lib.Miinam_Init(cls.__evt, 0)

    @classmethod
    def Fini(cls):
        """call only once when application exit"""
        cls._lib.Miinam_Fini()

    @classmethod
    def Enum(cls):
        CLib.initlib()
        a = (CLib._device * MIINAM_MAX)()
        n = cls._lib.Miinam_Enum(a, MIINAM_MAX)
        if n < 0:
            raise HRESULTException(n)
        arr = []
        for i in range(0, n):
            arr.append(miinam_device(a[i]))
        return arr

    def __init__(self, h):
        """the object of Miinam must be obtained by classmethod Open or OpenByIndex, it cannot be obtained by obj = miinam.Miinam()"""
        self.__h = h
        self.__sid += 1
        self.__id = self.__sid

    def __del__(self):
        self.Close()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.Close()

    def __nonzero__(self):
        return self.__h is not None

    def __bool__(self):
        return self.__h is not None

    @classmethod
    def Open(cls, camId):
        """the object of miinam must be obtained by classmethod Open or OpenByIndex, it cannot be obtained by obj = miinam.miinam()"""
        CLib.initlib()
        h = cls._lib.Miinam_Open(camId)
        if h is None:
            return None
        return __class__(h)

    @classmethod
    def Open_ByIndex(cls, index):
        CLib.initlib()
        h = cls._lib.Miinam_Open_ByIndex(index)
        if h is None:
            return None
        return __class__(h)

    def Close(self):
        for k in list(self.__dic.keys()):
            if self.__dic[k].id == self.__id:
                self.__dic.pop(k)

        if self.__h:
            self._lib.Miinam_Close(self.__h)
            self.__h = None

    def PullImage(self, pImageData, bits):
        '''
        bits: 24 (RGB24), 32 (RGB32), or 8 (Grey), see: MIINAM_PARA_FORMAT_LOCAL
        if RAW format, pnWidth = data size, pnHeight = not used
        '''
        x = ctypes.c_uint(0)
        y = ctypes.c_uint(0)
        self._lib.Miinam_PullImage(self.__h, pImageData, bits, ctypes.byref(x), ctypes.byref(y))
        return (x.value, y.value)

    def Start(self, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, self.__id)
        ret = self._lib.Miinam_StartPullModeWithCallback(self.__h, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    def Stop(self):
        self._lib.Miinam_Stop(self.__h)

    def Pause(self, bPause):
        self._lib.Miinam_Pause(self.__h, 1 if bPause else 0)

    def Capture(self, outputFile, fun, ctx):
        """
        capture image, compare this to image extracted from video
        outputFile:
                None        -> capture image and then return by callback
                "raw"       -> capture raw image and then return by callback
                "abc.jpg"   -> capture image and then save it in the camera sd card with filename 'abc.jpg'
                "abc.raw"   -> capture raw image and then save it in the camera sd card with filename 'abc.raw'
                "thumbnail" -> capture the thumbnail image and then return by callback
                "*"         -> capture image and then save it in the camera sd card with auto generated file name
                "*.raw"     -> capture raw image and then save it in the camera sd card with auto generated file name
        """
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, self.__id)
        ret = self._lib.Miinam_Capture(self.__h, outputFile.encode('utf-8'), self.__cap, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def Capture_ById(cls, camId, outputFile, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls._lib.Miinam_Capture_ById(camId, outputFile.encode('utf-8'), cls.__cap, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    def get_Device(self):
        return self._lib.Miinam_get_Device(self.__h)

    def get_Size(self):
        x = ctypes.c_int(0)
        y = ctypes.c_int(0)
        self._lib.Miinam_get_Size(self.__h, ctypes.byref(x), ctypes.byref(y))
        return (x.value, y.value)

    def get_CapSize(self):
        x = ctypes.c_int(0)
        y = ctypes.c_int(0)
        self._lib.Miinam_get_CapSize(self.__h, ctypes.byref(x), ctypes.byref(y))
        return (x.value, y.value)

    def get_FourCC(self):
        """see http://en.wikipedia.org/wiki/FourCC, http://www.fourcc.org"""
        x = ctypes.c_int(0)
        self._lib.Miinam_get_FourCC(self.__h, ctypes.byref(x))
        return x.value

    def Record(self, outputFile):
        """
        (outputFile == None) means to stop record.
        support file extension: *.asf, *.mp4, *.mkv
        """
        if outputFile is None:
            self._lib.Miinam_Record(self.__h, None)
        else:
            self._lib.Miinam_Record(self.__h, outputFile.encode('utf-8'))

    def put_Para(self, para, value):
        """para is one of MIINAM_PARA_xxx"""
        self._lib.Miinam_put_Para(self.__h, para, value)

    def get_Para(self, para):
        x = ctypes.c_int(0)
        self._lib.Miinam_get_Para(self.__h, para, ctypes.byref(x))
        return x.value

    @classmethod
    def put_Wifi(cls, camId, wifi):
        cls._lib.Miinam_put_Wifi(camId, wifi)

    @classmethod
    def get_Wifi(cls, camId):
        cls._lib.Miinam_get_Wifi(camId)

    @classmethod
    def list_Wifi(cls, camId, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls._lib.Miinam_list_Wifi(camId, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def list_Dir(cls, camId, path, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls._lib.Miinam_list_Dir(camId, path.encode('utf-8'), sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def get_Thumbnail(cls, camId, path, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls._lib.Miinam_get_Thumbnail(camId, path.encode('utf-8'), sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def change_Dir(cls, camId, path, dc):
        """del file or directory, rename file or directory"""
        cls._lib.Miinam_change_Dir(camId, path.encode('utf-8'), dc, len(dc))

    @classmethod
    def RecordStart(cls, camId, outputFile, recordtime, fun, ctx):
        """record to the sd card"""
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls._lib.Miinam_RecordStart(camId, outputFile.encode('utf-8'), recordtime, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def RecordStop(cls, camId, fun, ctx):
        """stop record to the sd card"""
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls._lib.Miinam_RecordStop(camId, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def get_DateTime(cls, camId, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls._lib.Miinam_get_DateTime(camId, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def put_DateTime(cls, camId, t, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls._lib.Miinam_put_DateTime(camId, t, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def put_Para_ById(cls, camId, para, value):
        """para is one of MIINAM_PARA_XXX"""
        x = ctypes.c_int(0)
        cls._lib.Miinam_put_Para_ById(camId, para, value)

    @classmethod
    def get_Para_ById(cls, camId, para):
        x = ctypes.c_int(0)
        cls._lib.Miinam_get_Para_ById(camId, para, ctypes.byref(x))
        return x.value

    @classmethod
    def get_Size_ById(cls, camId, res):
        x = ctypes.c_int(0)
        y = ctypes.c_int(0)
        cls._lib.Miinam_get_Size_ById(camId, res, ctypes.byref(x), ctypes.byref(y))
        return (x.value, y.value)

    @classmethod
    def PriFlag(cls, nFlag, nMask):
        CLib.initlib()
        cls._lib.Miinam_PriFlag(nFlag, nMask)

    @classmethod
    def add_Ip(cls, arr):
        """arr = ("1.2.3.4", "1.2.3.5", ...)"""
        type = (ctypes.c_char_p * (len(arr) + 1))
        a = type()
        for i in range(0, len(arr)):
            a[i] = ctypes.c_char_p(arr[i])
        cls._lib.Miinam_add_Ip(a)

    @classmethod
    def del_Ip(cls, arr):
        type = (ctypes.c_char_p * (len(arr) + 1))
        a = type()
        for i in range(0, len(arr)):
            a[i] = ctypes.c_char_p(arr[i])
        cls._lib.Miinam_del_Ip(a)