package com.its.op.service.its.rse;

import com.its.op.config.RseServerConfig;
import com.its.op.dao.mapper.its.rse.RseMapper;
import com.its.op.dto.its.common.NewCtrlSeqDto;
import com.its.op.dto.its.rse.RseControlDto;
import com.its.op.entity.its.rse.TbRseCtrlHs;
import com.its.op.service.its.xprotocol.CenterProtocol;
import com.its.utils.ItsUtils;
import com.its.utils.SysUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@Slf4j
@RequiredArgsConstructor
@Service
public class RseControlService {

    private final RseServerConfig config;
    private final RseMapper mapper;

    public ByteBuffer getResetCommand(Long ctlrNmbr, Long ctrlSeq, Integer devcType, Integer cntlType) {
        int length = 8+8+4+4;
        byte opCode = CenterProtocol.INT_OP_DSRC_CONTROL_REQ;

        // Center packet
        byte[] center = CenterProtocol.getRequestHead(CenterProtocol.INT_ID_RSE_OPER, CenterProtocol.INT_ID_RSE_SERVER, opCode, length);

        // command message packet
        ByteBuffer msgBuffer = ByteBuffer.allocate(length);
        msgBuffer.order(ByteOrder.BIG_ENDIAN);
        msgBuffer.putLong(ctlrNmbr);    // 8 Byte
        msgBuffer.putLong(ctrlSeq);     // 8 Byte
        msgBuffer.putInt(devcType);     // 4 Byte
        msgBuffer.putInt(cntlType);     // 4 Byte

        ByteBuffer cmdBuffer = ByteBuffer.allocate(center.length + length);
        cmdBuffer.order(ByteOrder.BIG_ENDIAN);
        cmdBuffer.put(center);
        cmdBuffer.put(msgBuffer.array());
        log.info("RSE Request Buffer: {}", SysUtils.byteArrayToHex(cmdBuffer.array()));
        return cmdBuffer;
    }

    /**
     * RSE 제어기 리셋
     * @param req : request buffer
     * @return : reset result
     */
    public RseControlDto.RseControlRes controlReset(Long id, RseControlDto.RseControlResetReq req) {
        NewCtrlSeqDto dto = this.mapper.getNextCtrlSeq();
        TbRseCtrlHs hs = TbRseCtrlHs.builder()
                .ctrlSeq(dto.getCTRL_SEQ())
                .rseCtlrNmbr(id)
                .cntlDt(ItsUtils.getSysTime())
                .devcType(String.valueOf(req.getDevice()))
                .cntlType(String.valueOf(req.getControl()))
                .rspsType("")
                .userId(req.getUserId())
                .build();

        this.mapper.insertRseCtrlHs(hs);    // 결과는 통신서버에서 업데이트 한다.

        RseControlDto.RseControlRes result = new RseControlDto.RseControlRes(0, "success");
        ByteBuffer cmdBuffer = getResetCommand(id, dto.getCTRL_SEQ(), req.getDevice(), req.getControl());

        String ipAddress = this.config.getIpAddress();
        int port = this.config.getPort();
        int connTimeout = 5000; // milli-seconds
        int readTimeout = 5000; // milli-seconds

        Socket socket = null;
        try {
            socket = CenterProtocol.connectServer(ipAddress, port, connTimeout, readTimeout);
            if (CenterProtocol.sendData(socket, cmdBuffer.array())) {
                try {
                    while (true) {
                        byte[] head = CenterProtocol.receiveBytes(socket, 10);
                        log.info("RECV RESPONSE HEAD: 10 Bytes. {}", SysUtils.byteArrayToHex(head));
                        if (head[0] == (byte)CenterProtocol.INT_ID_RSE_SERVER &&
                            head[1] == (byte)CenterProtocol.INT_ID_RSE_OPER &&
                            head[5] == (byte)CenterProtocol.INT_OP_DSRC_CONTROL_REQ) {
                            if (head[4] == (byte) 0xFF) {
                                result.setResult(9, "알수 없는 RSE 제어기 입니다.");
                            } else if (head[4] == (byte) 0xFE) {
                                result.setResult(8, "RSE 제어기 통신 상태가 비정상 입니다.");
                            } else if (head[4] == (byte) 0xFD) {
                                result.setResult(7, "RSE 제어기 제어기 제어값 오류.");
                            } else if (head[4] == (byte) 0xFC) {
                                result.setResult(6, "RSE 제어기로 리셋 명령을 전송하지 못하였습니다.");
                            } else {
                                result.setResult(0, "RSE 제어기 리셋명령을 정상적으로 전송하였습니다.");
                            }
                        } else {
                            result.setResult(4, "RSE 서버로 부터 비정상 데이터를 수신하였습니다.");
                        }
                        break;
                    }
                } catch (IOException e) {
                    // 타임아웃
                    log.error("{}", e.getMessage());
                    result.setResult(1, "RSE 서버로 부터 응답수신시각이 초과되었습니다.(명령응답 타임아웃)");
                }
            }
            else {
                result.setResult(3, "RSE 서버에 제어기 리셋명령을 정상적으로 전송하지 못하였습니다.");
            }
        } catch (IOException e) {
            log.error("Reset Request. {}, {}, IOException", ipAddress, port);
            String errMsg = "[RSE 서버: " + ipAddress + "." + port + "]";
            result.setResult(2, "RSE 서버와 통신이 실패하였습니다.\r\n" + errMsg);
            return result;
        }
        finally {
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                log.error("RseControlService: requestCommandReset, socket close IOException");
            }
        }
        return result;
    }

}
