package com.its.dsrc.xnettcp.dsrc.process.service;

import com.beanit.asn1bean.ber.ReverseByteArrayOutputStream;
import com.beanit.asn1bean.ber.types.*;
import com.beanit.asn1bean.ber.types.string.BerUTF8String;
import com.beanit.asn1bean.ber.types.string.BerVisibleString;
import com.beanit.asn1dsrc.dsrc.*;
import com.beanit.asn1dsrc.enums.eAuthInfo;
import com.beanit.asn1dsrc.util.DsrcAsn1Utils;
import com.its.app.AppUtils;
import com.its.app.utils.SysUtils;
import com.its.dsrc.dao.mapper.RseOffrSectMapper;
import com.its.dsrc.entity.TbRseCtlr;
import com.its.dsrc.entity.TbRseOffrSect;
import com.its.dsrc.entity.TbRseOffrSectTraf;
import com.its.dsrc.global.AppRepository;
import com.its.dsrc.process.DbmsData;
import com.its.dsrc.process.DbmsDataProcess;
import com.its.dsrc.process.DbmsDataType;
import com.its.dsrc.vo.NET;
import com.its.dsrc.vo.voDsrcOffrSectTraf;
import io.netty.channel.ChannelFuture;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class MultiMediaDataService {

    private RseOffrSectMapper rseOffrSectMapper;
    private ConcurrentHashMap<String, TbRseOffrSect> offrSectInfoMap = null;
    private ConcurrentHashMap<String, voDsrcOffrSectTraf> offrSectTrafMap = null;
    private List<TbRseOffrSectTraf> offrInfrHsList = null;

    private static MultiMediaDataService _instance = null;

    public static MultiMediaDataService getInstance() {
        if (_instance == null) {
            synchronized (MultiMediaDataService.class) {
                if (_instance == null)
                    _instance = new MultiMediaDataService();
            }
        }
        return _instance;
    }

    public MultiMediaDataService() {
        this.rseOffrSectMapper = (RseOffrSectMapper) AppUtils.getBean(RseOffrSectMapper.class);
        this.offrSectInfoMap = new ConcurrentHashMap<String, TbRseOffrSect>();
        this.offrSectTrafMap = new ConcurrentHashMap<String, voDsrcOffrSectTraf>();
        this.offrInfrHsList = Collections.synchronizedList(new ArrayList<>());
    }

    /*
    *  DSRC 제공구간 정보를 조회하고 DSRC 제어기에 설정한다.
    *  기존에 존재하는 설정정보를 삭제하고 다시 설정
    *  (데이터가 너무 많아 Delay 가 생길경우 최초에 한번만 실행하고 주기적으로 업데이트 하는 방법으로 수정가능)
     */
    private boolean loadOffrSectInfo() {
        // DSRC 제공구간 목록을 조회해서 DSRC 제어기에 설정한다.
        // 기존 제어기의 목록을 삭제한다.
        for (Map.Entry<String, TbRseCtlr> obj : AppRepository.getInstance().getCtlrMap().entrySet()) {
            obj.getValue().getOffrSectMap().clear();    // 제공구간 목록 초기화
            this.offrInfrHsList.clear();                // 제공이력 목록 삭제
        }
        try {
            List<TbRseOffrSect> list = this.rseOffrSectMapper.selectRseOffrSectAll();
            this.offrSectInfoMap.clear();
            for (TbRseOffrSect vo : list) {
                this.offrSectInfoMap.put(vo.getOFFR_SECT_ID(), vo);
                TbRseCtlr obj = AppRepository.getInstance().getCtlrMap().get(vo.getRSE_CTLR_NMBR());
                if (obj == null) {
                    log.error("MultiMediaDataService.loadOffrSectInfo: DSRC Not Found: {}", vo.getRSE_CTLR_NMBR());
                }
                else {
                    obj.getOffrSectMap().put(vo.getOFFR_SECT_ID(), vo);
                }
            }
        }
        catch(Exception e) {
            log.error("MultiMediaDataService.loadOffrSectInfo: Exception: {}", e.toString());
        }
        log.info("MultiMediaDataService.loadOffrSectInfo: {} EA.", this.offrSectInfoMap.size());
        return true;
    }

    /*
    *  DSRC 제공구간 소통정보를 조회한다.
     */
    private boolean loadOffrSectTraf() {
        try {
            List<voDsrcOffrSectTraf> list = this.rseOffrSectMapper.selectRseOffrSectTraf();
            this.offrSectTrafMap.clear();
            for (voDsrcOffrSectTraf vo : list) {
                this.offrSectTrafMap.put(vo.getOFFR_SECT_ID(), vo);
            }
        }
        catch(Exception e) {
            log.error("MultiMediaDataService.loadOffrSectTraf: Exception: {}", e.toString());
        }
        log.info("MultiMediaDataService.loadOffrSectTraf: {} EA.", this.offrSectTrafMap.size());
        return true;
    }

    public void makeMultimediaData() {

        // DSRC 정보제공구간 목록 조회
        loadOffrSectInfo();

        // DSRC 정보제공구간 소통정보 조회
        // DSRC 제공구간정보에 포함된 기본정보를 소통정보 조회하면서 함께 조회하기 때문에
        // DSRC 정보제공구간정보는 조회하지 않는다.
        loadOffrSectTraf();

        String logKey = "x";

        // 제어기별 Multimedia packet 생성
        for (Map.Entry<String, TbRseCtlr> entry : AppRepository.getInstance().getCtlrMap().entrySet()) {
            TbRseCtlr obj = entry.getValue();
            if (!logKey.equals("x")) {
                MDC.remove(logKey);
                MDC.clear();
            }
            logKey = obj.getLogKey();
            MDC.put("id", logKey);
            if (obj.getChannel() == null || obj.getNetState() != NET.LOGINED || !obj.getChannel().isActive()) {
                log.warn("MultiMediaDataService.makeMultimediaData: {}, {}, is not connected.", obj.getID(), obj.getRSE_ID());
                continue;
            }

            if (obj.getOffrSectMap().size() == 0) {
                log.warn("MultiMediaDataService.makeMultimediaData: {}, {}, multimedia data not found.", obj.getID(), obj.getRSE_ID());
                continue;
            }

            MultiMediaDataList multiMediaDataList = null;
            for (Map.Entry<String, TbRseOffrSect> sectEntry : obj.getOffrSectMap().entrySet()) {
                TbRseOffrSect sectInfo = sectEntry.getValue();
                voDsrcOffrSectTraf traf = this.offrSectTrafMap.get(sectInfo.getOFFR_SECT_ID());
                if (traf == null) {
                    continue;
                }

                if (multiMediaDataList == null) {
                    multiMediaDataList = new MultiMediaDataList();
                }

                String offrDt = SysUtils.getSysTime();
                String offrCont = traf.getOFFR_DRCT_NM() + " 방향 " + traf.getCMTR_GRAD_NM();
                byte[] infoTypeid = { 0x01 };

                //0x00 : 정의 없음
                //0x01 : 소통정보
                //0x02 : 돌발/통제/도로상태/유고 정보
                //0x03 : 공지사항
                //0x04 : 정지영상

                MultiMediaData multiMediaData = new MultiMediaData();
                MultiMediaData.LinkLinkIDNumber linkIDNumber = new MultiMediaData.LinkLinkIDNumber();
                linkIDNumber.getBerUTF8String().add(new BerUTF8String(traf.getOFFR_SECT_ID()));

                multiMediaData.setDirectionID(new BerOctetString(traf.getOBU_ENTR_DRCT_NMBR().getBytes())); // directionID
                multiMediaData.setTrntMessageCreativeGeneralizedTime(new BerVisibleString(offrDt));         // trnt-MessageCreativeGeneralizedTime\
                multiMediaData.setLinkLinkIDNumber(linkIDNumber);                                           // link-LinkIDNumber
                multiMediaData.setInfoTypeID(new BerOctetString(infoTypeid));                               // infoTypeID

                MultiMediaData.DataType dataType = new MultiMediaData.DataType();
                dataType.setMmdCharData(new CharObjectData(offrCont.getBytes()));
                multiMediaData.setDataType(dataType);                                                       // dataType

                multiMediaDataList.getMultiMediaData().add(multiMediaData);

                this.offrInfrHsList.add(new TbRseOffrSectTraf(traf.getOFFR_SECT_ID(), offrDt, offrCont));
            }

            if (multiMediaDataList == null) {
                log.warn("MultiMediaDataService.makeMultimediaData: {}, {}, multimedia traffic data not found.", obj.getID(), obj.getRSE_ID());
                continue;
            }

            EndApplicationMessage endMsg = makeEndApplicationMessage(obj, multiMediaDataList);
            if (endMsg == null) {
                continue;
            }

            byte[] auth = { (byte) eAuthInfo.AI_Multimedia.getValue() };
            int packetNmbr = obj.getSeq().nextValue();

            PublicationType publicationType = new PublicationType();
            publicationType.setDatexPublishData(endMsg);

            PublicationData publicationData = new PublicationData();
            publicationData.setDatexPublishSubscribeSerialNbr(new BerInteger(packetNmbr));          // 요청 serial nmbr
            publicationData.setDatexPublishSerialNbr(new BerInteger(packetNmbr));     // 생성 serial nmbr
            publicationData.setDatexPublishLatePublicationFlag(new BerBoolean(true));
            publicationData.setDatexPublishType(publicationType);

            PublishFormat.DatexPublishData publishData = new PublishFormat.DatexPublishData();
            publishData.getPublicationData().add(publicationData);

            PublishFormat publishFormat = new PublishFormat();
            publishFormat.setDatexPublishData(publishData);

            Publication publication = new Publication();
            publication.setDatexPublishFormat(publishFormat);
            publication.setDatexPublishGuaranteedBool(new BerBoolean(true));

            C2CAuthenticatedMessage c2c = new C2CAuthenticatedMessage();
            PDUs pdus = new PDUs();
            pdus.setPublication(publication);
            c2c.setDatexDataPacketNumber(new BerInteger(packetNmbr));
            c2c.setDatexDataPacketPriorityNumber(new BerInteger(1));
            c2c.setDatexAuthenticationInfoText(new BerOctetString(auth));
            c2c.setOptions(DsrcAsn1Utils.swapHeaderOptions(obj.getHeaderOptions()));
            c2c.setPdu(pdus);

            ChannelFuture f = obj.getChannel().writeAndFlush(c2c);
            f.awaitUninterruptibly();
            if (f.isDone() || f.isSuccess()) {
                log.info("MultiMediaDataService.makeMultimediaData: {}, {}, multimedia data: {} EA, send ok", obj.getID(), obj.getRSE_ID(), multiMediaDataList.getMultiMediaData().size());
                //obj.putRegisteredCommands(c2c);
            } else {
                log.error("MultiMediaDataService.makeMultimediaData: {}, {}, multimedia data: {} EA, send failed. isDone: {}, isSuccess: {}",
                        obj.getID(), obj.getRSE_ID(), multiMediaDataList.getMultiMediaData().size(), f.isDone(), f.isSuccess());
            }
        }
        if (!logKey.equals("x")) {
            MDC.remove(logKey);
            MDC.clear();
        }
        // DSRC 제공구간 제공이력을 이력테이블에 저장한다.
        DbmsDataProcess dbmsDataProcess = (DbmsDataProcess) AppUtils.getBean(DbmsDataProcess.class);
        dbmsDataProcess.add(new DbmsData(DbmsDataType.DBMS_DATA_MULTIMEDIA_HS, false, this.offrInfrHsList));
    }

    public EndApplicationMessage makeEndApplicationMessage(TbRseCtlr obj, MultiMediaDataList multiMediaDataList) {

        EndApplicationMessage endApplicationMessage = null;
        MDC.put("id", obj.getLogKey());
        try {
            if (multiMediaDataList != null) {
                int[] messageId = {1, 0, 15784, 5, 0, 1};
                ReverseByteArrayOutputStream msgBuff = new ReverseByteArrayOutputStream(2048);
                multiMediaDataList.encode(msgBuff);

                endApplicationMessage = new EndApplicationMessage();
                endApplicationMessage.setEndApplicationMessageId(new BerObjectIdentifier(messageId));
                endApplicationMessage.setEndApplicationMessageMsg(new BerAny(msgBuff.getArray()));
            }
        }
        catch(Exception e) {
            log.error("MultiMediaDataService.makeEndApplicationMessage: {}, MultiMediaDataList: {}, Exception: {}", obj.getID(), multiMediaDataList, e);
            return null;
        }
        log.debug("MultiMediaDataService.makeEndApplicationMessage: {}, MultiMediaDataList: {}, EndApplicationMessage: {}", obj.getID(), multiMediaDataList, endApplicationMessage);
        MDC.remove(obj.getLogKey());
        MDC.clear();
        return endApplicationMessage;
    }

}
