package com.its.op.xnetudp.service;

import com.its.op.config.VmsServerConfig;
import com.its.op.dto.its.vms.VmsControlDto;
import com.its.op.entity.its.unit.TbUnitSyst;
import com.its.op.global.UnitSystManager;
import com.its.op.xnetudp.codec.VmsServerRequestEncoder;
import com.its.op.xnetudp.protocol.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.nio.ByteBuffer;

@Slf4j
@RequiredArgsConstructor
@Service
public class VmsServerRequestSender {

    private final VmsServerConfig vmsServerConfig;

    @Async("centerCommExecutor")
    public void run(ByteBuffer sendBuffer) {

        TbUnitSyst vmsServer = UnitSystManager.get(this.vmsServerConfig.getProcessId());
        if (vmsServer != null) {
            this.vmsServerConfig.setIpAddress(vmsServer.getSystIp1().trim());
            this.vmsServerConfig.setPort(Integer.parseInt(vmsServer.getPrgmPort().trim()));
        }
        log.info("{}", this.vmsServerConfig);

        if (sendBuffer == null) {
            log.error("VmsServerRequestSender.run: Notify Data NULL");
            return;
        }

        EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.channel(NioDatagramChannel.class);
            bootstrap.group(nioEventLoopGroup);
            bootstrap.option(ChannelOption.SO_BROADCAST, false);
            bootstrap.handler(new ChannelInitializer<Channel>() {
                @Override
                protected void initChannel(Channel channel)
                {
                    channel.pipeline().addLast(new VmsServerRequestEncoder(vmsServerConfig.getIpAddress(), vmsServerConfig.getPort()));
                }
            });

            Channel channel = bootstrap.bind(0).sync().channel();
            ChannelFuture f = channel.writeAndFlush(sendBuffer).sync();
            if (!f.isDone() || !f.isSuccess()) {
                log.error("VmsServerRequestSender.run: Send Failed. isDone: {}, isSuccess: {}", f.isDone(), f.isSuccess());
            }
        }
        catch(InterruptedException ie) {
            log.error("VmsServerRequestSender.run: InterruptedException");
        }
        catch(Exception e) {
            log.error("VmsServerRequestSender.run: Exception");
        }
        finally {
            nioEventLoopGroup.shutdownGracefully();
            log.info("VmsServerRequestSender.run: {} Bytes, ip: {}, port: {}", sendBuffer.array().length, vmsServerConfig.getIpAddress(), vmsServerConfig.getPort());
        }
    }

    /**
     * VMS 서버로 리셋 명령 전송
     * @param id
     * @param req
     * @return
     */
    public boolean requestReset(Long id, VmsControlDto.VmsControlReq req) {
        log.info("requestReset: {}, {}", id, req);
        // 0: 소프트웨어리셋, 1: 하드웨어리셋
        CENTER_VMS_REQ_RESET commMsg = new CENTER_VMS_REQ_RESET(id, req.getUserId(), (byte)(req.getValue() == 0 ? 0x00 : 0x01));
        run(commMsg.getBuffer());
        return true;
    }

    /**
     * VMS 서버로 전광판 On/Off 명령 전송
     * @param id
     * @param req
     * @return
     */
    public boolean requestPowerControl(Long id, VmsControlDto.VmsControlReq req) {
        log.info("requestPowerControl: {}, {}", id, req);
        CENTER_VMS_REQ_POWER commMsg = new CENTER_VMS_REQ_POWER(id, req.getUserId(), (byte)(req.getValue() == 0 ? 0x00 : 0x01));
        run(commMsg.getBuffer());
        return true;
    }

    /**
     * VMS 서버로 휘도제어 명령 전송
     * @param id
     * @param req
     * @return
     */
    public boolean controlLuminance(Long id, VmsControlDto.VmsControlReq req) {
        log.info("controlLuminance: {}, {}", id, req);
        byte brghMode = req.getValue().byteValue();
        byte brghCurrStep = req.getCurrStep().byteValue();
        byte brghWeekStep = req.getWeekStep().byteValue();
        byte brghNghtStep = req.getNghtStep().byteValue();
        CENTER_VMS_REQ_LUMINANCE commMsg = new CENTER_VMS_REQ_LUMINANCE(id, req.getUserId(), brghMode, brghCurrStep, brghWeekStep, brghNghtStep);
        run(commMsg.getBuffer());
        return true;
    }

    /**
     * VMS 제어기 파라미터 설정
     * @param id
     * @param req
     * @return
     */
    public boolean controlParam(Long id, VmsControlDto.VmsControlParamReq req) {
        log.info("controlParam: {}, {}", id, req);
        CENTER_VMS_REQ_PARAM commMsg = new CENTER_VMS_REQ_PARAM(id, req);
        run(commMsg.getBuffer());
        return true;
    }

    /**
     * VMS 제어기 운영모드 변경
     * @param id
     * @param req
     * @return
     */
    public boolean controlOprMode(Long id, VmsControlDto.VmsControlOprModeReq req) {
        log.info("controlOprMode: {}, {}", id, req);
        CENTER_VMS_REQ_OPER_MODE commMsg = new CENTER_VMS_REQ_OPER_MODE(id, req);
        run(commMsg.getBuffer());
        return true;
    }

    /**
     * VMS 전광판 ON/OFF 시각 설정
     * @param id
     * @param req
     * @return
     */
    public boolean controlOnOffTime(Long id, VmsControlDto.VmsControlOnOffTimeReq req) {
        return true;
    }

    /**
     * VNS 시나리오 즉시 다운로드
     * @param ctlrNmbr
     * @return
     */
    public boolean controlImmScnrDnld(Long ctlrNmbr, VmsControlDto.VmsControlReq req) {
        log.info("controlImmScnrDnld: {}, {}", ctlrNmbr, req);
        CENTER_VMS_REQ_IMM_SCNR_DNLD commMsg = new CENTER_VMS_REQ_IMM_SCNR_DNLD(ctlrNmbr, req);
        run(commMsg.getBuffer());
        return true;
    }

    /**
     * VMS 서버 Notify 요청
     * @param opCode
     * @return
     */
    public boolean controlNotify(int opCode) {
        CENTER_VMS_REQ_NOTIFY commMsg = new CENTER_VMS_REQ_NOTIFY((byte)opCode);
        run(commMsg.getBuffer());
        return true;
    }
}
