package com.its.vms.api.service;

import com.its.vms.api.dto.*;
import com.its.vms.domain.NET;
import com.its.vms.dto.TbVmsCtlrDto;
import com.its.vms.service.AppRepositoryService;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;

@Slf4j
@RequiredArgsConstructor
@Service
public class VmsControlService {
    
    private final AppRepositoryService repoService;

    Map<String, ControlCmd> ptzControlMap = new HashMap<>();

    @PostConstruct
    void init() {
        this.ptzControlMap.put("zoom-in",    new ControlCmd((byte)0, (byte)0, (byte)1, (byte)0, (byte)0,    (byte)0,    (byte)0xFE, (byte)0,    "Zoom In"));
        this.ptzControlMap.put("zoom-out",   new ControlCmd((byte)0, (byte)0, (byte)2, (byte)0, (byte)0,    (byte)0,    (byte)0xFE, (byte)0,    "Zoom Out"));
        this.ptzControlMap.put("focus-in",   new ControlCmd((byte)0, (byte)0, (byte)0, (byte)1, (byte)0,    (byte)0,    (byte)0,    (byte)0xFE, "Focus Near"));
        this.ptzControlMap.put("focus-out",  new ControlCmd((byte)0, (byte)0, (byte)0, (byte)2, (byte)0,    (byte)0,    (byte)0,    (byte)0xFE, "Focus Far"));
        this.ptzControlMap.put("pan-left",   new ControlCmd((byte)1, (byte)0, (byte)0, (byte)0, (byte)0xFE, (byte)0,    (byte)0,    (byte)0,    "Pan Left"));
        this.ptzControlMap.put("pan-right",  new ControlCmd((byte)2, (byte)0, (byte)0, (byte)0, (byte)0xFE, (byte)0,    (byte)0,    (byte)0,    "Pan Right"));
        this.ptzControlMap.put("tilt-up",    new ControlCmd((byte)0, (byte)1, (byte)0, (byte)0, (byte)0,    (byte)0xFE, (byte)0,    (byte)0,    "Tilt Up"));
        this.ptzControlMap.put("tilt-down",  new ControlCmd((byte)0, (byte)2, (byte)0, (byte)0, (byte)0,    (byte)0xFE, (byte)0,    (byte)0,    "Tilt Down"));
        this.ptzControlMap.put("up-left",    new ControlCmd((byte)1, (byte)1, (byte)0, (byte)0, (byte)0xFE, (byte)0xFE, (byte)0,    (byte)0,    "Pan Tilt Left Up"));
        this.ptzControlMap.put("up-right",   new ControlCmd((byte)2, (byte)1, (byte)0, (byte)0, (byte)0xFE, (byte)0xFE, (byte)0,    (byte)0,    "Pan Tilt Right Up"));
        this.ptzControlMap.put("down-left",  new ControlCmd((byte)1, (byte)2, (byte)0, (byte)0, (byte)0xFE, (byte)0xFE, (byte)0,    (byte)0,    "Pan Tilt Left Down"));
        this.ptzControlMap.put("down-right", new ControlCmd((byte)2, (byte)2, (byte)0, (byte)0, (byte)0xFE, (byte)0xFE, (byte)0,    (byte)0,    "Pan Tilt Right Down"));
    }

    @Getter
    @Setter
    public static class ControlCmd {
        byte pan;
        byte tilt;
        byte zoom;
        byte focus;
        byte panSped;
        byte tiltSped;
        byte zoomSped;
        byte focusSped;
        String cmdDesc;
        public ControlCmd(byte pan, byte tilt, byte zoom, byte focus, byte panSped, byte tiltSped, byte zoomSped, byte focusSped, String cmdDesc) {
            this.pan = pan;
            this.tilt = tilt;
            this.zoom = zoom;
            this.focus = focus;
            this.panSped = panSped;
            this.tiltSped = tiltSped;
            this.zoomSped = zoomSped;
            this.focusSped = focusSped;
            this.cmdDesc = cmdDesc;
        }
    }

    // 데이터 1건 조회, 없으면 exception
    private TbVmsCtlrDto requireOne(Long id) throws NoSuchElementException {
        return this.repoService.getCtrlMap(id);
    }

    /**
     * VMS 상태정보 요청 처리
     * @param id
     * @return
     */
    public CctvStatusDto.CctvStatusDtoRes requestStatus(Long id) {
        log.info("requestStatus: {}", id);
        TbVmsCtlrDto obj = this.requireOne(id);
        if (obj == null) {
            return new CctvStatusDto.CctvStatusDtoRes(1, "알수없는 VMS 관리번호 입니다.", null);
        }
        if (obj.getNetState() != NET.LOGINED) {
            return new CctvStatusDto.CctvStatusDtoRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.", null);
        }

        return new CctvStatusDto.CctvStatusDtoRes(0, "success", null);//obj.getStts().toDto());
    }

    /**
     * VMS PTZ 컨트롤
     * @param id
     * @param req
     * @return
     */
    public CctvPtzControlDto.CctvPtzControlRes controlPtz(Long id, CctvPtzControlDto.CctvControlPtzReq req) {
        log.info("controlPtz: {}, {}", id, req);
        TbVmsCtlrDto obj = this.requireOne(id);
        if (obj == null) {
            return new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 1, "알수없는 VMS 관리번호 입니다.");
        }
        if (obj.getNetState() != NET.LOGINED) {
            return new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
        }

        ControlCmd command = this.ptzControlMap.get(req.getCommand());
        if (command == null) {
            return new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 3, "VMS PTZ 명령이 잘못 되었습니다.");
        }

        CctvPtzControlDto.CctvPtzControlRes result = new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 0, "success");
        String cmdDesc = command.getCmdDesc();
//        if (StringUtils.equalsIgnoreCase("start", req.getAction())) {
//            byte speed = req.getSpeed().byteValue();
//            byte pan = command.getPan();
//            byte tilt = command.getTilt();
//            byte zoom = command.getZoom();
//            byte focus = command.getFocus();
//            byte panSped = (byte)Math.min(command.getPanSped(), speed);
//            byte tiltSped = (byte)Math.min(command.getTiltSped(), speed);
//            byte zoomSped = (byte)Math.min(command.getZoomSped(), speed);
//            byte focusSped = (byte)Math.min(command.getFocusSped(), speed);
//            CctvReqPtzCtrl pkt = new CctvReqPtzCtrl(obj.getAddress());
//            byte ptz = pkt.setPtzValue((byte) pan, (byte) tilt, (byte) zoom, (byte) focus, (byte) panSped, (byte) tiltSped, (byte) zoomSped, (byte) focusSped);
//            //log.info("[{}]. PTZ Control: " + ByteUtils.byteToBitString(ptz));
//            ByteBuffer sendBuffer = pkt.getByteBuffer();
//            if (obj.sendData(sendBuffer, 0, "cctv_PtzStart")) {
//                log.info("[{}]. START send success. {}", obj.getCCTV_CTLR_ID(), cmdDesc);
//            } else {
//                result.setResult(4, "CCTV PTZ 시작 명령 전송이 실패하였습니다.");
//                log.error("[{}]. START send failed. {}", obj.getCCTV_CTLR_ID(), cmdDesc);
//            }
//        } else {
//            // stop
//            CctvReqPtzCtrl pkt = new CctvReqPtzCtrl(obj.getAddress());
//            ByteBuffer sendBuffer = pkt.getByteBuffer();
//            if (obj.sendData(sendBuffer, 0, "cctv_PtzStop")) {
//                log.info("[{}]. STOP send success. {}", obj.getCCTV_CTLR_ID(), cmdDesc);
//            } else {
//                result.setResult(4, "CCTV PTZ 정지 명령 전송이 실패하였습니다.");
//                log.error("[{}]. STOP send failed. {}", obj.getCCTV_CTLR_ID(), cmdDesc);
//            }
//        }
        return result;
    }

    /**
     * VMS Preset 컨트롤
     * @param id
     * @param req
     * @return
     */
    public CctvPresetControlDto.CctvPresetControlRes controlPreset(Long id, CctvPresetControlDto.CctvPresetControlReq req) {
        log.info("controlPreset: {}, {}", id, req);
        TbVmsCtlrDto cctv = this.requireOne(id);
        if (cctv == null) {
            return new CctvPresetControlDto.CctvPresetControlRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
        }
        if (cctv.getNetState() != NET.LOGINED) {
            return new CctvPresetControlDto.CctvPresetControlRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
        }

        CctvPresetControlDto.CctvPresetControlRes result = new CctvPresetControlDto.CctvPresetControlRes(0, "success");
//        CctvReqPresetMove pkt = new CctvReqPresetMove(cctv.getAddress());
//        pkt.setValue(req.getPan().shortValue(), req.getTilt().shortValue(), req.getZoom().shortValue(), req.getFocus().shortValue());
//        ByteBuffer sendBuffer = pkt.getByteBuffer();
//        if (cctv.sendData(sendBuffer, 0, "cctv_PresetMove")) {
//            log.info("[{}]. PRESET send success.", cctv.getCCTV_CTLR_ID());
//        } else {
//            result.setResult(4, "CCTV PRESET 명령 전송이 실패하였습니다.");
//            log.error("[{}]. PRESET send failed.", cctv.getCCTV_CTLR_ID());
//        }
        return result;
    }

    /**
     * VMS 파라미터정보 설정
     * @param id
     * @param req
     * @return
     */
    public CctvParamControlDto.CctvParamControlRes controlParam(Long id, CctvParamControlDto.CctvParamControlReq req) {
        log.info("controlParam: {}, {}", id, req);
        TbVmsCtlrDto cctv = this.requireOne(id);
        if (cctv == null) {
            return new CctvParamControlDto.CctvParamControlRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
        }
        if (cctv.getNetState() != NET.LOGINED) {
            return new CctvParamControlDto.CctvParamControlRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
        }

        CctvParamControlDto.CctvParamControlRes result = new CctvParamControlDto.CctvParamControlRes(0, "success");
//        CctvReqParamSet pkt = new CctvReqParamSet(cctv.getAddress());
//        pkt.setValue(req.getFanOnTmpr().byteValue(), req.getFanOffTmpr().byteValue(), req.getHetrOnTmpr().byteValue(), req.getHetrOffTmpr().byteValue());
//        ByteBuffer sendBuffer = pkt.getByteBuffer();
//        if (cctv.sendData(sendBuffer, 0, "cctv_ParamSet")) {
//            log.info("[{}]. PARAMETER SET send success.", cctv.getCCTV_CTLR_ID());
//        } else {
//            result.setResult(4, "CCTV PARAMETER 명령 전송이 실패하였습니다.");
//            log.error("[{}]. PARAMETER SET send failed.", cctv.getCCTV_CTLR_ID());
//        }
        return result;
    }

    /**
     * VMS 파라미터 정보 조회
     * @param id
     * @return
     */
    public CctvParamControlDto.CctvParamValueRes requestParam(Long id) {
        log.info("requestParam: {}", id);
        TbVmsCtlrDto cctv = this.requireOne(id);
        if (cctv == null) {
            return new CctvParamControlDto.CctvParamValueRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
        }
        if (cctv.getNetState() != NET.LOGINED) {
            return new CctvParamControlDto.CctvParamValueRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
        }

        CctvParamControlDto.CctvParamValueRes result = new CctvParamControlDto.CctvParamValueRes(0, "success");

//        synchronized (cctv.getParamVal()) {
//            try {
//                cctv.getParamVal().setRequest(true);
//                cctv.getParamVal().setCompleted(false);
//                ByteBuffer sendBuffer = cctv.getReqParam().getByteBuffer();
//                if (cctv.sendData(sendBuffer, 0, "cctv_ParamReq")) {
//                    log.info("[{}]. PARAMETER QRY send success.", cctv.getCCTV_CTLR_ID());
//                    cctv.getParamVal().wait(5000);
//                    if (cctv.getParamVal().isCompleted()) {
//                        result.setValue(cctv.getParamVal().getFanOnTmpr(), cctv.getParamVal().getFanOffTmpr(), cctv.getParamVal().getHetrOnTmpr(), cctv.getParamVal().getHetrOffTmpr());
//                    } else {
//                        result.setResult(5, "CCTV PARAMETER 값조회 명령 중 타임아웃이 발생하였습니다.");
//                    }
//                } else {
//                    result.setResult(4, "CCTV PARAMETER 값조회 명령 전송이 실패하였습니다.");
//                    log.error("[{}]. PARAMETER QRY send failed.", cctv.getCCTV_CTLR_ID());
//                }
//            } catch (InterruptedException e) {
//                result.setResult(9, "CCTV PARAMETER 값조회 명령 전송 중 오류가 발생하였습니다.");
//                log.error("[{}]. PARAMETER QRY send failed. InterruptedException.", cctv.getCCTV_CTLR_ID());
//            }
//            cctv.getParamVal().setRequest(false);
//        }
        return result;
    }

    /**
     * VMS 프리셋 현재위치정보 요청
     * @param id
     * @return
     */
    public CctvPresetControlDto.CctvPresetValueRes requestPresetValue(Long id) {
        log.info("requestPresetValue: {}", id);
        TbVmsCtlrDto cctv = this.requireOne(id);
        if (cctv == null) {
            return new CctvPresetControlDto.CctvPresetValueRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
        }
        if (cctv.getNetState() != NET.LOGINED) {
            return new CctvPresetControlDto.CctvPresetValueRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
        }

        CctvPresetControlDto.CctvPresetValueRes result = new CctvPresetControlDto.CctvPresetValueRes(0, "success");
//        synchronized (cctv.getPsetVal()) {
//            try {
//                cctv.getPsetVal().setCompleted(false);
//                cctv.getPsetVal().setRequest(true);
////                CctvReqPresetQry pkt = new CctvReqPresetQry(cctv.getAddress());
////                pkt.setValue((byte)0x00);
////                ByteBuffer sendBuffer = pkt.getByteBuffer();
////                if (cctv.sendData(sendBuffer, 0, "cctv_PresetQry")) {
//                ByteBuffer sendBuffer = cctv.getReqState().getByteBuffer();
//                if (cctv.sendData(sendBuffer, 0, "cctv_PresetQry")) {
//                    log.info("[{}]. PRESET QRY send success.", cctv.getCCTV_CTLR_ID());
//                    cctv.getPsetVal().wait(5000);
//                    if (cctv.getPsetVal().isCompleted()) {
//                        result.setValue(cctv.getPsetVal().getPan(), cctv.getPsetVal().getTilt(), cctv.getPsetVal().getZoom(), cctv.getPsetVal().getFocus());
//                    } else {
//                        result.setResult(5, "CCTV PRESET 값조회 명령 중 타임아웃이 발생하였습니다.");
//                    }
//                } else {
//                    result.setResult(4, "CCTV PRESET 값조회 명령 전송이 실패하였습니다.");
//                    log.error("[{}]. PRESET QRY send failed.", cctv.getCCTV_CTLR_ID());
//                }
//            } catch (InterruptedException e) {
//                result.setResult(6, "CCTV PRESET 값조회 명령 전송 중 오류가 발생하였습니다.");
//                log.error("[{}]. PRESET QRY send failed. InterruptedException.", cctv.getCCTV_CTLR_ID());
//            }
//            cctv.getPsetVal().setRequest(false);
//        }
        return result;
    }

    /**
     * Object Notify TEST
     * @param id
     * @return
     */
    public CctvStatusDto.CctvStatusNotifyDtoRes notifyStatus(Long id) {
        log.info("notifyStatus: {}", id);
        TbVmsCtlrDto cctv = this.requireOne(id);
        if (cctv == null) {
            return new CctvStatusDto.CctvStatusNotifyDtoRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
        }
        if (cctv.getNetState() != NET.LOGINED) {
            return new CctvStatusDto.CctvStatusNotifyDtoRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
        }

        CctvStatusDto.CctvStatusNotifyDtoRes result = new CctvStatusDto.CctvStatusNotifyDtoRes(0, "success");
//        synchronized (cctv.getPsetVal()) {
//            try {
//                cctv.getPsetVal().notifyAll();
//            }
//            catch(Exception e) {
//                log.error("{}, {}", cctv.getPsetVal(), e.getMessage());
//            }
//        }
        return result;
    }

    /**
     * VMS 가변문자 설정
     * @param id
     * @param req
     * @return
     */
    public CctvVarCharControlDto.CctvVarCharControlRes controlVarCharSet(Long id, CctvVarCharControlDto.CctvVarCharControlSetReq req) {
        log.info("controlVarCharSet: {}, {}", id, req);
        TbVmsCtlrDto cctv = this.requireOne(id);
        if (cctv == null) {
            return new CctvVarCharControlDto.CctvVarCharControlRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
        }
        if (cctv.getNetState() != NET.LOGINED) {
            return new CctvVarCharControlDto.CctvVarCharControlRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
        }

        CctvVarCharControlDto.CctvVarCharControlRes result = new CctvVarCharControlDto.CctvVarCharControlRes(0, "success");
//        CctvReqCharVar pkt = new CctvReqCharVar(cctv.getAddress());
//        if (!pkt.setValue(
//                req.getSectorNo().byteValue(),
//                req.getStartPan(), req.getEndPan(),
//                req.getCh1CharSize().byteValue(), req.getCh1PosX(), req.getCh2PosY(),
//                req.getCh2CharSize().byteValue(), req.getCh2PosX(), req.getCh2PosY(),
//                req.getCh1Char(), req.getCh2Char()
//                )) {
//            return new CctvVarCharControlDto.CctvVarCharControlRes(3, "CCTV 가변문자 설정값에 오류가 있습니다.");
//        }
//
//        ByteBuffer sendBuffer = pkt.getByteBuffer();
//        if (cctv.sendData(sendBuffer, 0, "cctv_CharVarSet")) {
//            log.info("[{}]. CHAR-VAR-SET send success.", cctv.getCCTV_CTLR_ID());
//        } else {
//            result.setResult(4, "CCTV CHAR-VAR-SET 명령 전송이 실패하였습니다.");
//            log.error("[{}]. CHAR-VAR-SET send failed.", cctv.getCCTV_CTLR_ID());
//        }
        return result;
    }

    /**
     * VMS 가변문자 삭제
     * @param id
     * @param req
     * @return
     */
    public CctvVarCharControlDto.CctvVarCharControlRes controlVarCharDel(Long id, CctvVarCharControlDto.CctvVarCharControlDelReq req) {
        log.info("controlVarCharDel: {}, {}", id, req);
        TbVmsCtlrDto cctv = this.requireOne(id);
        if (cctv == null) {
            return new CctvVarCharControlDto.CctvVarCharControlRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
        }
        if (cctv.getNetState() != NET.LOGINED) {
            return new CctvVarCharControlDto.CctvVarCharControlRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
        }

        CctvVarCharControlDto.CctvVarCharControlRes result = new CctvVarCharControlDto.CctvVarCharControlRes(0, "success");
//        CctvReqCharDel pkt = new CctvReqCharDel(cctv.getAddress());
//        pkt.setValue(req.getSectorNo().byteValue());
//
//        ByteBuffer sendBuffer = pkt.getByteBuffer();
//        if (cctv.sendData(sendBuffer, 0, "cctv_CharVarDel")) {
//            log.info("[{}]. CHAR-VAR-DEL send success.", cctv.getCCTV_CTLR_ID());
//        } else {
//            result.setResult(4, "CCTV CHAR-VAR-DEL 명령 전송이 실패하였습니다.");
//            log.error("[{}]. CHAR-VAR-DEL send failed.", cctv.getCCTV_CTLR_ID());
//        }
        return result;
    }

    /**
     * VMS 제어기 리셋
     * @param id
     * @param req
     * @return
     */
    public CctvControlDto.CctvControlResetRes controlReset(Long id, CctvControlDto.CctvControlResetReq req) {
        log.info("controlReset: {}, {}", id, req);
        TbVmsCtlrDto cctv = this.requireOne(id);
        if (cctv == null) {
            return new CctvControlDto.CctvControlResetRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
        }
        if (cctv.getNetState() != NET.LOGINED) {
            return new CctvControlDto.CctvControlResetRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
        }

        CctvControlDto.CctvControlResetRes result = new CctvControlDto.CctvControlResetRes(0, "success");
//        if (0 == req.getResetType()) {
//            // SW Reset
//            if (!cctv.resetSw()) {
//                result.setResult(4, "CCTV 제어기 SW 리셋 명령 전송에 실패하였습니다.");
//            } else {
//                log.info("[{}]. SW Reset send success.", cctv.getCCTV_CTLR_ID());
//            }
//        }
//        else if (1 == req.getResetType()) {
//            // HW Reset
//            if (!cctv.resetHw()) {
//                result.setResult(5, "CCTV 제어기 HW 리셋 명령 전송에 실패하였습니다.");
//            } else {
//                log.info("[{}]. HW Reset send success.", cctv.getCCTV_CTLR_ID());
//            }
//        }
//        else {
//            result.setResult(3, "CCTV 리셋 정보가 잘못 되었습니다.");
//        }
        return result;
    }

}
