package Yoseen;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.LongByReference;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.nio.file.FileSystems;

public interface YoseenSDK extends Library {
    String FileSeperator = FileSystems.getDefault().getSeparator();
    String OSType = System.getProperty("os.name").toLowerCase().contains("win") ? "windows" : "linux";
    String UserDir = System.getProperty("user.dir");

    static String getDebugDir() {
        String DebugDir;
        if (OSType.equals("windows")) {
             DebugDir = UserDir + "\\..\\YoseenBuild\\build-Debug64";
        } else {
             DebugDir = UserDir + "/../YoseenBuild/build-x64";
        }
        return DebugDir;
    }

    static String getSDKFile() {
        String SDKFile;
        if (OSType.equals("windows")) {
            SDKFile = UserDir + "\\..\\SDK\\YoseenSDK\\x64\\YoseenSDK.dll";
        } else {
            SDKFile = UserDir + "/../SDK/YoseenSDK/x64/libYoseenSDK.so";
        }
        return SDKFile;
    }

    YoseenSDK Instance = Native.load(getSDKFile(), YoseenSDK.class);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    int Yoseen_InitSDK();

    void Yoseen_FreeSDK();

    void Yoseen_SetTimeout(int connectTimeout, int sendTimeout, int recvTimeout);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    int Yoseen_Login(YoseenData.YoseenLoginInfo loginInfo, YoseenData.CameraBasicInfo cameraBasicInfo);

    int Yoseen_Logout(int userHandle);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    int Yoseen_StartPreview(int userHandle, YoseenData.YoseenPreviewInfo previewInfo);

    int Yoseen_StartPreviewTempScale(int userHandle, YoseenData.YoseenPreviewInfo previewInfo, int tempScale);

    int Yoseen_StopPreview(int previewHandle);

    int Yoseen_PreviewSetImage(int previewHandle, YoseenData.strech_control strechControl, int paletteType);

    int Yoseen_BeginSave(int previewHandle, String fn, int fileType);

    int Yoseen_EndSave(int previewHandle, int fileType);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    int Yoseen_SendControl(int userHandle, YoseenData.Ctl ctl);

    int Yoseen_SendControlX(int userHandle, long ctlx);

    int Yoseen_SaveFrame(int userHandle, String fileName, int withTemp);

    int Yoseen_SaveFrameToMem(int userHandle, YoseenData.TempFrameFile frameFile);

    int Yoseen_WriteSerialPortData(int userHandle, YoseenData.SerialPortData spd);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    int Yoseen_GetCameraBasicInfo(int userHandle, YoseenData.CameraBasicInfo cameraBasicInfo);

    int Yoseen_SetCameraBasicInfo(int userHandle, YoseenData.CameraBasicInfo cameraBasicInfo);

    int Yoseen_GetCameraNetworkInfo(int userHandle, YoseenData.CameraNetworkInfo cameraNetworkInfo);

    int Yoseen_SetCameraNetworkInfo(int userHandle, YoseenData.CameraNetworkInfo cameraNetworkInfo);

    int Yoseen_GetTvoutInfo(int userHandle, YoseenData.TvoutInfo tvoutInfo);

    int Yoseen_SetTvoutInfo(int userHandle, YoseenData.TvoutInfo tvoutInfo);

    int Yoseen_GetFixInfo(int userHandle, YoseenData.FixInfo fixInfo);

    int Yoseen_SetFixInfo(int userHandle, YoseenData.FixInfo fixInfo);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    int Yoseen_DownloadMem(int userHandle, int memType, long memData, IntByReference memSize);

    int Yoseen_UploadMem(int userHandle, int memType, long memData, int memSize);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    long YoseenPlayback_Create();

    void YoseenPlayback_Free(LongByReference pp);

    void YoseenPlayback_GetImage(long ctx, YoseenData.strech_control sc, IntByReference paletteType);

    void YoseenPlayback_SetImage(long ctx, YoseenData.strech_control sc, IntByReference paletteType);

    int YoseenPlayback_OpenFile(long ctx, String fileName, int fileType);

    void YoseenPlayback_CloseFile(long ctx);

    int YoseenPlayback_SaveFile(long ctx, YoseenData.TempFrameFile tff, String fileName);

    int YoseenPlayback_ReadCover(long ctx, YoseenData.TempFrameFile tff);

    int YoseenPlayback_ReadFrame(long ctx, int frameIndex, YoseenData.DataFrame dataFrame);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    int mtGetResult(long dfh, long dfd, YoseenData.xxxmeasure measure, YoseenData.xxxresult result);

    int mtGetResult_GMO(long dfh, long dfd, YoseenData.xxxresult result);

    long mtCreate(int width, int height);

    void mtFree(LongByReference pp);

    int mtReload(long mtContext, YoseenData.MeasureTemplate measureTemplate);

    int mtCheck(long mtContext, long dfh, long dfd, YoseenData.MeasureTemplateResult result);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    class YoseenDevice {
        public static Unsafe __unsafe;

        public static int initSDK() {
            int ret;
            ret = YoseenSDK.Instance.Yoseen_InitSDK();
            if (0 > ret) ret = EError.EError_InternalError;

            //
            try {
                Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
                field.setAccessible(true);
                __unsafe = (sun.misc.Unsafe) field.get(null);
            } catch (Exception ex) {
                ret = EError.EError_InternalError;
            }
            return ret;
        }

        public static void freeSDK() {
            YoseenSDK.Instance.Yoseen_FreeSDK();
        }

        //
        int _userHandle;
        int _previewHandle;
        long _mem;

        public YoseenDevice() {
            _userHandle = -1;
            _previewHandle = -1;

            //
            _basicInfo = new YoseenData.CameraBasicInfo();
            _loginInfo = new YoseenData.YoseenLoginInfo();
            _previewInfo = new YoseenData.YoseenPreviewInfo();
            _ctlx = new YoseenData.CtlX();
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////
        YoseenData.CameraBasicInfo _basicInfo;

        public YoseenData.CameraBasicInfo getBasicInfo() {
            return _basicInfo;
        }

        YoseenData.YoseenLoginInfo _loginInfo;

        public int login(String addr) {
            if (_userHandle >= 0) return EError.EError_InvalidState;

            int ret;
            _loginInfo.setCameraAddr(addr);
            ret = YoseenSDK.Instance.Yoseen_Login(_loginInfo, _basicInfo);
            if (0 <= ret) {
                _userHandle = ret;
                _mem = __unsafe.allocateMemory(1024 * 1024);
            }
            return ret;
        }

        public void logout() {
            if (_previewHandle >= 0) {
                YoseenSDK.Instance.Yoseen_StopPreview(_previewHandle);
                _previewHandle = -1;
            }

            if (_userHandle >= 0) {
                YoseenSDK.Instance.Yoseen_Logout(_userHandle);
                _userHandle = -1;
                __unsafe.freeMemory(_mem);
                _mem = 0;
            }
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////
        public int saveFrame(String fn) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_SaveFrame(_userHandle, fn, 1);
            return ret;
        }

        public int saveFrame(YoseenData.TempFrameFile tff) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_SaveFrameToMem(_userHandle, tff);
            return ret;
        }

        public int sendControl(YoseenData.Ctl x) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_SendControl(_userHandle, x);
            return ret;
        }

        YoseenData.CtlX _ctlx;

        int sendControlX_ExecutePtz(YoseenData.xxxptzcmd x) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            _ctlx.makeReq_ExecutePtz(_mem, x);
            ret = YoseenSDK.Instance.Yoseen_SendControlX(_userHandle, _mem);
            if (0 == ret) {
                _ctlx.makeResp_ExecutePtz(_mem, x);
            }
            return ret;
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////
        public int ptzControl(int act, int arg) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            YoseenData.xxxptzcmd x = new YoseenData.xxxptzcmd();
            x.act = (byte) act;
            x.arg = (byte) arg;
            ret = sendControlX_ExecutePtz(x);
            return ret;
        }

        public int ptzControl_Stop(int act, int arg) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            YoseenData.xxxptzcmd x = new YoseenData.xxxptzcmd();
            x.act = YoseenData.PtzAction_Stop;
            x.arg = (byte) arg;
            ret = sendControlX_ExecutePtz(x);
            return ret;
        }

        public int ptzControl_GetPtz(int[] ptz, int timeout) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            YoseenData.xxxptzcmd x = new YoseenData.xxxptzcmd();
            x.act = YoseenData.PtzAction_GetPTZ;
            ret = sendControlX_ExecutePtz(x);
            if (0 == ret) {
                ptz[0] = x.pan;
                ptz[1] = x.tilt;
                ptz[2] = x.zoom;
            }
            return ret;
        }

        public int ptzControl_SetPtz(int[] ptz, int speed) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            YoseenData.xxxptzcmd x = new YoseenData.xxxptzcmd();
            x.act = YoseenData.PtzAction_SetPTZ;
            x.arg = (byte) speed;
            x.pan = ptz[0];
            x.tilt = ptz[1];
            x.zoom = ptz[2];
            ret = sendControlX_ExecutePtz(x);
            return ret;
        }

        public int ptzControl_B160_W4R4(String reqStr, byte[] respBytes) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            int[] integers = new int[1];
            boolean b = YoseenUtil.hexStr2Integers(reqStr, integers);
            if (!b) return EError.EError_InvalidState;

            YoseenData.xxxptzcmd x = new YoseenData.xxxptzcmd();
            x.act = YoseenData.PtzAction_B160_W4R4;
            x.pan = integers[0];
            ret = sendControlX_ExecutePtz(x);
            if (0 == ret) {
                byte[] bytes = YoseenUtil.int2Bytes(x.tilt);
                respBytes[0] = bytes[0];
                respBytes[1] = bytes[1];
                respBytes[2] = bytes[2];
                respBytes[3] = bytes[3];
            }
            return ret;
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////
        public int get_CameraBasicInfo(YoseenData.CameraBasicInfo x) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_GetCameraBasicInfo(_userHandle, x);
            return ret;
        }

        public int set_CameraBasicInfo(YoseenData.CameraBasicInfo x) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_SetCameraBasicInfo(_userHandle, x);
            return ret;
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////
        YoseenData.YoseenPreviewInfo _previewInfo;

        public int startPreview(int dataType, YoseenData.YoseenPreviewCallback cb) {
            boolean b = _userHandle >= 0 && _previewHandle < 0;
            if (!b) return EError.EError_InvalidState;
            int ret;
            _previewInfo.DataType = dataType;
            _previewInfo.CustomCallback = cb;
            ret = YoseenSDK.Instance.Yoseen_StartPreview(_userHandle, _previewInfo);
            if (0 <= ret) {
                _previewHandle = ret;
            }
            return 0;
        }

        public void stopPreview() {
            if (_previewHandle >= 0) {
                YoseenSDK.Instance.Yoseen_StopPreview(_previewHandle);
                _previewHandle = -1;
            }
        }

        public int beginSave(String fn, int ft) {
            if (_previewHandle < 0) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_BeginSave(_previewHandle, fn, ft);
            return ret;
        }

        public void endSave(int ft) {
            if (_previewHandle < 0) return;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_EndSave(_previewHandle, ft);
        }

        public int downloadMem(long memData, IntByReference memSize) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_DownloadMem(_userHandle, YoseenUtil.xxxcameramem_measure, memData, memSize);
            return ret;
        }

        public int uploadMem(long memData, int memSize) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_UploadMem(_userHandle, YoseenUtil.xxxcameramem_measure, memData, memSize);
            return ret;
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////
        public int writeSerialPortData(YoseenData.SerialPortData spd) {
            if (_userHandle < 0) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.Yoseen_WriteSerialPortData(_userHandle, spd);
            return ret;
        }
    }


    //////////////////////////////////////////////////////////////////////////////////////////////////////
    class YoseenFile {
        long _ypc;

        public YoseenFile() {
        }

        public int init() {
            if (0 != _ypc) return 0;
            _ypc = YoseenSDK.Instance.YoseenPlayback_Create();
            return 0;
        }

        public void free() {
            if (0 == _ypc) return;
            LongByReference ref = new LongByReference(_ypc);
            YoseenSDK.Instance.YoseenPlayback_Free(ref);
            _ypc = 0;
        }

        boolean _isOpen;

        public int open(String fn, int ft, YoseenData.TempFrameFile tff) {
            if (0 == _ypc) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.YoseenPlayback_OpenFile(_ypc, fn, ft);
            if (0 <= ret) {
                ret = YoseenSDK.Instance.YoseenPlayback_ReadCover(_ypc, tff);
            }
            if (0 <= ret) {
                _isOpen = true;
            }
            return ret;
        }

        public void close() {
            if (!_isOpen) return;
            YoseenSDK.Instance.YoseenPlayback_CloseFile(_ypc);
            _isOpen = false;
        }

        public int read(int frameIndex, YoseenData.DataFrame dataFrame) {
            if (!_isOpen) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.YoseenPlayback_ReadFrame(_ypc, 0, dataFrame);
            return ret;
        }

        public int setImage(YoseenData.strech_control strechControl, int paletteType) {
            if (!_isOpen) return EError.EError_InvalidState;
            int ret;
            IntByReference ref = new IntByReference(paletteType);
            YoseenSDK.Instance.YoseenPlayback_SetImage(_ypc, strechControl, ref);
            return 0;
        }

        public int save(String fn, YoseenData.TempFrameFile tff) {
            if (!_isOpen) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.YoseenPlayback_SaveFile(_ypc, tff, fn);
            return ret;
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    class YoseenMeasure {
        long _context;

        public int init() {
            if (0 != _context) return 0;
            int ret = 0;
            _context = YoseenSDK.Instance.mtCreate(384, 288);
            if (0 == _context) {
                ret = EError.EError_InternalError;
            }
            return ret;
        }

        public void free() {
            if (0 == _context) return;
            LongByReference ref = new LongByReference(_context);
            YoseenSDK.Instance.mtFree(ref);
            _context = 0;
        }

        public int setConfig(YoseenData.MeasureTemplate measureTemplate) {
            if (0 == _context) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.mtReload(_context, measureTemplate);
            return ret;
        }

        //multi measure
        public int getResult(long dfh, long dfd, YoseenData.MeasureTemplateResult result) {
            if (0 == _context) return EError.EError_InvalidState;
            int ret;
            ret = YoseenSDK.Instance.mtCheck(_context, dfh, dfd, result);
            return ret;
        }

        //single measure
        public static int getResult(long dfh, long dfd, YoseenData.xxxmeasure measure, YoseenData.xxxresult result) {
            int ret;
            ret = YoseenSDK.Instance.mtGetResult(dfh, dfd, measure, result);
            return ret;
        }
    }
}



