import { LayerInfo, LayerIndex, LayerType, FacilityComm, TrafficGrade, TrafficGradeDesc, EditLinkLineSize, EditLinkColor } from "./map-const.js";
import { TMapConfig } from "./map-config.js";
import { TMapLayer } from "./map-layer.js";
import { TTraffic, TLink, TIfsc, TRoad, _trafMap, _linkMap, _ifscMap, _roadMap } from "./obj-traffic.js";

// https://github.com/Viglino/ol-ext
let _mapMngr = null;
class TLayerMap {
    constructor(ALayerInfo, ALayer) {
        this.lyrInfo = ALayerInfo;
        this.layer = ALayer;
    }
}
function copyToClipboard(val) {
    const t = document.createElement("textarea");
    document.body.appendChild(t);
    t.value = val;
    t.select();
    document.execCommand("copy");
    document.body.removeChild(t);
}

export class TFclt {
    constructor(ANmbr, AName, AX, AY) {
        this.NMBR = ANmbr;
        this.NAME = AName;
        this.LAT = AX;
        this.LNG = AY;
        this.COMM = "0";
    }
}
export class TMapMngr {
    constructor(AMapId, ATooltipId) {
        _mapMngr = this;

        this.config = new TMapConfig();
        this.currZoom = this.config.mapDefZoom;
        this.lyrInfoMap = new Map(); // 레이어 이름으로 레이어 인덱스를 찾기위한 맵
        this.layerArr = new Array(LayerIndex.MAX); // 레이어정보를 저장하고 있는 배열

        this.showTrafficTip = true; // 소통정보 툴팁 표출여부 플래그
        this.showTrafficLyr = true; // 소통정보 레이어 표출여부 플래그
        this.showIncidentLyr = true; // 돌발 레이어 표출여부 플래그

        this.selectFeature = null; // 객체 또는 좌표 선택시 표출할 화살표 feature
        this.isMeasurement = false; // 거리재기, 명적재기 등 작업 여부 플래그
        this.selectMode = false; // 객체 선택 모드 플래그
        this.selLinkWidth = 4; // 선택 링크 굵기
        this.selLink = null; // 소통정보 폴리라인 선택 객체
        this.selLinkStyle = null; // 소통정보 폴리라인 선택 스타일

        this.mapApp = {};
        this.editMode = false; // 지도 편집 모드 플래그
        this.editLayerIdx = -1; // 편집 레이어 인덱스
        this.editObj = null;
        this.editFeature = null;
        this.editCoords = null;
        this.onFcltDragEndFunc = null;

        this.selectLayerIdx = -1; // 선택 레이어 인덱스

        this.timerSelectArrow = null; // 객체선택 타이머
        this.onMapMoveEndFunc = null;
        this.onMouseClickFunc = null;
        this.onContextMenuFunc = null;
        this.onSelectObjFunc = null;
        this.centerPos = null;
        this.resizeMapUpdateTimer = null;

        this.mapApp.Drag = function () {
            ol.interaction.Pointer.call(this, {
                handleDownEvent: _mapMngr.mapApp.Drag.prototype.handleDownEvent,
                handleDragEvent: _mapMngr.mapApp.Drag.prototype.handleDragEvent,
                handleUpEvent: _mapMngr.mapApp.Drag.prototype.handleUpEvent,
            });
            _mapMngr.initEditMode();
        };
        ol.inherits(this.mapApp.Drag, ol.interaction.Pointer);

        this.mapApp.Drag.prototype.handleDownEvent = function (evt) {
            if (_mapMngr.editMode == false) return false;
            //const map = evt.map;
            const feature = _mapMngr.map.forEachFeatureAtPixel(evt.pixel, function (feature) {
                return feature;
            });

            if (feature) {
                const obj = feature.get("obj");
                if (!obj) return false;

                if (_mapMngr.editLayerIdx != obj.lyrIdx) return false;
                if (obj.edit == false) return false;

                _mapMngr.editObj = obj;
                _mapMngr.editCoords = evt.coordinate;
                _mapMngr.editFeature = feature;
            }
            return !!feature;
        };

        this.mapApp.Drag.prototype.handleDragEvent = function (evt) {
            if (_mapMngr.editMode == false) return;
            if (_mapMngr.editFeature) {
                const deltaX = evt.coordinate[0] - _mapMngr.editCoords[0];
                const deltaY = evt.coordinate[1] - _mapMngr.editCoords[1];

                const geometry = _mapMngr.editFeature.getGeometry();
                geometry.translate(deltaX, deltaY);

                _mapMngr.editCoords[0] = evt.coordinate[0];
                _mapMngr.editCoords[1] = evt.coordinate[1];
            }
        };

        this.mapApp.Drag.prototype.handleUpEvent = function () {
            if (_mapMngr.editMode == false) return false;
            if (!_mapMngr.editObj) return false;
            if (_mapMngr.onFcltDragEndFunc) {
                try {
                    const obj = _mapMngr.editObj;
                    _mapMngr.onFcltDragEndFunc(obj.lyrInfo.lyrIdx, obj.lyrInfo.lyrName, obj.NMBR, _mapMngr.editCoords[0], _mapMngr.editCoords[1]);
                } catch (err) {
                    console.error(err);
                }
            }
            _mapMngr.initEditMode();
            return false;
        };

        const scaleLineControl = new ol.control.ScaleLine();
        this.map = new ol.Map({
            controls: ol.control
                .defaults({
                    attributionOptions: {
                        collapsible: true,
                    },
                })
                .extend([scaleLineControl]),
            interactions: ol.interaction.defaults({ doubleClickZoom: false }).extend([new this.mapApp.Drag()]),
            // interactions: ol.interaction.defaults({c: false}).extend([
            //     new ol.interaction.MouseWheelZoom({
            //     constrainResolution: true
            //     })
            // ]),
            layers: [
                //this.config.baseMapLyr.normal,
                //this.config.baseMapLyr.satellite,
                this.config.baseMapGroup,
                this.config.trafGroup,
                this.config.poiGroup,
                this.config.eventGroup,
            ],
            logo: false,
            target: document.getElementById(AMapId),
            renderer: "canvas",
            view: this.config.mapView,
        });

        this.initMapLayer();

        this.currZoom = Math.round(this.map.getView().getZoom());

        this.selectFeature = this.layerArr[LayerIndex.Select].makePoiLayer([127.109873, 37.283203]); /*맵을 만든다음에 Select Layer를 생성해야함*/

        this.tooltip = document.getElementById(ATooltipId);
        this.tooltipOverlay = new ol.Overlay({
            element: this.tooltip,
            offset: [10, 0],
            positioning: "bottom-left",
        });
        this.map.addOverlay(this.tooltipOverlay);

        let layerSwitcher = new ol.control.LayerSwitcher({
            tipLabel: "레이어",
            groupSelectStyle: "children",
        });
        //this.map.addControl(layerSwitcher);
        document.getElementsByClassName("ol-zoom")[0].style.display = "none";

        const zoomLevelText = document.getElementById("zoom_level");
        if (zoomLevelText) {
            zoomLevelText.value = this.currZoom;
        }
        // 줌변경 및 지도표출영역 변경 이벤트 체크
        this.map.on("moveend", function (e) {
            const currZoom = Math.round(_mapMngr.map.getView().getZoom());
            if (_mapMngr.currZoom != currZoom) {
                _mapMngr.currZoom = currZoom;
                if (zoomLevelText) {
                    zoomLevelText.value = _mapMngr.currZoom;
                }
                // 줌이 변경된 경우에만 지도를 다시 그린다
                _mapMngr.refreshMap();
            }
            _mapMngr.centerPos = _mapMngr.map.getView().getCenter();
            const bound = _mapMngr.map.getView().calculateExtent(_mapMngr.map.getSize());
            const swLat = bound[0]; //left
            const swLng = bound[1]; //bottom
            const neLat = bound[2]; //right
            const neLng = bound[3]; //top
            if (_mapMngr.onMapMoveEndFunc) {
                _mapMngr.onMapMoveEndFunc(_mapMngr.currZoom, _mapMngr.centerPos[0], _mapMngr.centerPos[1], swLat, swLng, neLat, neLng);
            }
        });

        // 컨텍스트 메뉴 핸들러
        this.map.getViewport().addEventListener("contextmenu", function (e) {
            e.preventDefault();
            const f = _mapMngr.map.forEachFeatureAtPixel(_mapMngr.map.getEventPixel(e), function (feature, layer) {
                return feature;
            });
            if (f) {
                const coord = f.getGeometry().getCoordinates();
                const obj = f.get("obj");
                if (!obj) {
                    return;
                }
                const lyrInfo = obj.lyrInfo;
                const lyrType = lyrInfo.lyrType;
                try {
                    if (_mapMngr.onContextMenuFunc != null) {
                        if (obj.lyrInfo.lyrType <= LayerType.VMSI) {
                            const xy = coord[0];
                            _mapMngr.onContextMenuFunc(obj.lyrInfo.lyrIdx, obj.lyrInfo.lyrName, obj.NMBR, xy[0], xy[1], e.x, e.y);
                        } else {
                            _mapMngr.onContextMenuFunc(obj.lyrInfo.lyrIdx, obj.lyrInfo.lyrName, obj.NMBR, coord[0], coord[1], e.x, e.y);
                        }
                    }
                } catch (err) {
                    console.error(err);
                }
            }
            else {
                if (_mapMngr.onMouseClickFunc != null) {
                    _mapMngr.onMouseClickFunc(-1, -1, -1, -1, -1,-1, -1);
                }
            }
        });

        // checking mouse click event
        this.map.on("click", function (e) {
            const f = _mapMngr.map.forEachFeatureAtPixel(e.pixel, function (feature) {
                return feature;
            });
            if (f) {
                const coord = f.getGeometry().getCoordinates();
                const obj = f.get("obj");
                if (!obj) {
                    return;
                }
                const lyrInfo = obj.lyrInfo;
                const lyrType = lyrInfo.lyrType;
                let xy = [];
                if (obj.lyrInfo.lyrType <= LayerType.VMSI) {
                    xy = e.coordinate; //coord[0];
                } else {
                    xy[0] = coord[0];
                    xy[1] = coord[1];
                }
                if (_mapMngr.selectMode) {
                    copyToClipboard(obj.NMBR);
                    try {
                        if (_mapMngr.onSelectObjFunc != null) {
                            _mapMngr.onSelectObjFunc(obj.lyrInfo.lyrIdx, obj.lyrInfo.lyrName, obj.NMBR, xy[0], xy[1], e.pixel[0], e.pixel[1]);
                        }
                    } catch (err) {
                        console.error(err);
                    }
                } else {
                    try {
                        if (_mapMngr.onMouseClickFunc != null) {
                            _mapMngr.onMouseClickFunc(obj.lyrInfo.lyrIdx, obj.lyrInfo.lyrName, obj.NMBR, xy[0], xy[1], e.pixel[0], e.pixel[1]);
                        }
                    } catch (err) {
                        console.error(err);
                    }
                }
            }
            else {
                if (_mapMngr.onMouseClickFunc != null) {
                    _mapMngr.onMouseClickFunc(-1, -1, -1, -1, -1, e.pixel[0], e.pixel[1]);
                }
            }
        });

        this.map.on("pointermove", this.onMouseMove);
    } // constructor

    onMouseMove(e) {
        _mapMngr.tooltip.style.display = "none";
        if (e.dragging) {
            return;
        }

        if (_mapMngr.isMeasurement) {
            return;
        }
        _mapMngr.map.getTarget().style.cursor = "";

        if (_mapMngr.selLink) {
            _mapMngr.selLink.setStyle(_mapMngr.selLinkStyle);
            _mapMngr.selLink = null;
            _mapMngr.selLinkStyle = null;
        }

        const f = _mapMngr.map.forEachFeatureAtPixel(e.pixel, function (feature) {
            return feature;
        });

        if (f) {
            _mapMngr.map.getTarget().style.cursor = "pointer";

            const obj = f.get("obj");
            if (!obj) {
                return;
            }
            const lyrInfo = obj.lyrInfo;
            const lyrType = lyrInfo.lyrType;
            if (lyrType === LayerType.TRAF || lyrType === LayerType.LINK || lyrType === LayerType.IFSC || lyrType === LayerType.ROAD) {
                _mapMngr.selLink = f;
                _mapMngr.selLinkStyle = f.getStyle();
                const style = new ol.style.Style({
                    stroke: new ol.style.Stroke({
                        color: "red",
                        width: _mapMngr.selLinkWidth + 4,
                    }),
                });
                f.setStyle(style);
            }

            let iwContent = "";
            if (_mapMngr.editMode) {
                let NAME;
                if (lyrType === LayerType.TRAF || lyrType === LayerType.LINK || lyrType === LayerType.IFSC || lyrType === LayerType.ROAD) {
                    NAME = `${obj.obj.STRT_NM} -> ${obj.obj.END_NM}`;
                } else {
                    NAME = obj.NAME;
                }
                iwContent = '<div id="edit-tooltip">' + NAME + "</div>";
            } else {
                if (lyrType === LayerType.TRAF || lyrType === LayerType.LINK || lyrType === LayerType.IFSC || lyrType === LayerType.ROAD) {
                    if (_mapMngr.showTrafficTip) {
                        const refObj = obj.obj;
                        const grade = Number(refObj.cmtrGradCd);
                        const info = TrafficGradeDesc[grade];
                        const road = refObj.NAME;
                        const path = `${refObj.STRT_NM} -> ${refObj.END_NM}`;
                        const cnt = path ? path.length : 250;
                        const widSize = cnt > 20 ? 380 : 250;
                        //iwContent = '<div export class="mapPop traffic_' + grade + '" style="width:' + widSize + 'px !important;">';
                        iwContent = '<div export class="mapPop traffic_' + grade + '" >';
                        iwContent += '<div export class="head"><span export class="title">' + road + '</span><span export class="traffic">' + info + "</span></div>";
                        iwContent += '<div export class="cont">';
                        iwContent += "<dl><dt>" + path + "</dt>";
                        iwContent += "<dd>구간거리 : " + refObj.SECT_LNGT + " m<br/>평균속도 : " + refObj.sped + " km/h</dd>";
                        iwContent += "</dl></div>";
                    }
                } else if (lyrType === LayerType.FCLT) {
                    if (lyrInfo) {
                        const lyrName = "[" + lyrInfo.lyrName + "]";
                        const COMM = obj.COMM;
                        const path = obj.NAME;
                        let grade = 1;
                        let info = "";
                        const commStts = Number(obj.COMM);
                        switch (commStts) {
                            case FacilityComm.Normal.state:
                                info = FacilityComm.Normal.desc;
                                grade = TrafficGrade.Light.grade;
                                break;
                            case FacilityComm.Error.state:
                                info = FacilityComm.Error.desc;
                                grade = TrafficGrade.Heavy.grade;
                                break;
                            case FacilityComm.Module.state:
                                info = FacilityComm.Module.desc;
                                grade = TrafficGrade.Moderate.grade;
                                break;
                            default:
                                info = FacilityComm.Unknown.desc;
                                grade = TrafficGrade.NoData.grade;
                                break;
                        }
                        const cnt = path ? path.length : 250;
                        const widSize = cnt > 20 ? 380 : 250;
                        iwContent = '<div export class="mapPop traffic_' + grade + '" style="width:' + widSize + 'px !important;">';
                        iwContent += '<div export class="head"><span export class="title">' + lyrName + '</span><span export class="traffic">' + info + "</span></div>";
                        iwContent += '<div export class="cont">';
                        iwContent += "<dl><dt>" + path + "</dt>";
                        iwContent += "<dd>" + obj.NMBR + "<br/>" + obj.ID + "</dd>";
                        iwContent += "</dl></div>";
                    }
                } else if (lyrType === LayerType.BIS) {
                    if (lyrInfo) {
                        const lyrName = "[" + lyrInfo.lyrName + "]";
                        const path = obj.NAME;
                        let grade = 1;
                        let info = "";
                        const commStts = Number(obj.COMM);
                        switch (commStts) {
                            case FacilityComm.Normal.state:
                                info = FacilityComm.Normal.desc;
                                grade = TrafficGrade.Light.grade;
                                break;
                            case FacilityComm.Error.state:
                                info = FacilityComm.Error.desc;
                                grade = TrafficGrade.Heavy.grade;
                                break;
                            case FacilityComm.Module.state:
                                info = FacilityComm.Module.desc;
                                grade = TrafficGrade.Moderate.grade;
                                break;
                            default:
                                info = FacilityComm.Unknown.desc;
                                grade = TrafficGrade.NoData.grade;
                                break;
                        }
                        const cnt = path ? path.length : 250;
                        const widSize = cnt > 20 ? 380 : 250;
                        iwContent = '<div export class="mapPop traffic_' + grade + '" style="width:' + widSize + 'px !important;">';
                        iwContent += '<div export class="head"><span export class="title">' + lyrName + "</span></div>";
                        iwContent += '<div export class="cont">';
                        iwContent += "<dl><dt>" + path + "</dt>";
                        iwContent += "<dd>" + obj.NMBR + "<br/></dd>";
                        iwContent += "</dl></div>";
                    }
                } else if (lyrType === LayerType.PARK) {
                    if (lyrInfo) {
                        const lyrName = "[" + lyrInfo.lyrName + "]";
                        const COMM = obj.COMM;
                        const path = obj.NAME;
                        let grade = 1;
                        let info = "";
                        const commStts = Number(obj.COMM);
                        switch (commStts) {
                            case FacilityComm.Normal.state:
                                info = FacilityComm.Normal.desc;
                                grade = TrafficGrade.Light.grade;
                                break;
                            case FacilityComm.Error.state:
                                info = FacilityComm.Error.desc;
                                grade = TrafficGrade.Heavy.grade;
                                break;
                            case FacilityComm.Module.state:
                                info = FacilityComm.Module.desc;
                                grade = TrafficGrade.Moderate.grade;
                                break;
                            default:
                                info = FacilityComm.Unknown.desc;
                                grade = TrafficGrade.NoData.grade;
                                break;
                        }

                        const cnt = path ? path.length : 250;
                        const widSize = cnt > 20 ? 380 : 250;
                        iwContent = '<div export class="mapPop traffic_' + grade + '" style="width:' + widSize + 'px !important;">';
                        iwContent += '<div export class="head"><span export class="title">' + lyrName + "</div>";
                        iwContent += '<div export class="cont">';
                        iwContent += "<dl><dt>" + path + "</dt>";
                        iwContent += "<dd>주차면수: " + obj.parkingCnt + "<br/>위치: " + obj.location + "</dd>";
                        iwContent += "</dl></div>";
                    }
                } else if (lyrType === LayerType.INCD) {
                    if (lyrInfo) {
                        const nmbr = obj.NMBR;
                        const REG_DT = obj.REG_DT;
                        const STR_DT = obj.STR_DT;
                        const END_DT = obj.END_DT;
                        const INCLOC = obj.INCLOC;
                        const path = obj.TITLE;
                        const INCTYPECD = obj.COMM;
                        let grade = 1;
                        let info = obj.INCTYPENM;
                        switch (Number(INCTYPECD)) {
                            case 1:
                                grade = TrafficGrade.Heavy.grade;
                                break; /*사고*/
                            case 2:
                                grade = TrafficGrade.Moderate.grade;
                                break; /*공사*/
                            case 3:
                                grade = TrafficGrade.Light.grade;
                                break; /*행사*/
                            default:
                                grade = TrafficGrade.NoData.grade;
                                break; /*기상*/
                        }
                        const cnt = path ? path.length : 250;
                        const widSize = cnt > 40 ? 450 : 250;
                        iwContent = '<div export class="mapPop traffic_' + grade + '" style="width:' + widSize + 'px !important;">';
                        iwContent += '<div export class="head"><span export class="title">' + nmbr + '</span><span export class="traffic">' + info + "</span></div>";
                        iwContent += '<div export class="cont">';
                        iwContent += "<dl><dt>" + path + "</dt>";
                        iwContent += "<dd>등록시각: " + getParseDateTime(REG_DT) + "<br/>시작시각: " + getParseDateTime(STR_DT) + "<br/>종료시각: " + getParseDateTime(END_DT) + "<br/>발생장소: " + INCLOC + "</dd>";
                        iwContent += "</dl></div>";
                    }
                }
            }

            if (lyrInfo.showTooltip && iwContent != "") {
                _mapMngr.tooltip.style.display = "";
                _mapMngr.tooltipOverlay.setPosition(e.coordinate);
                _mapMngr.tooltip.innerHTML = iwContent;
            }
        }
    }
    setOverlay(overlay) {
        overlay.forEach((el) => {
            this.map.addOverlay(el);
        });
    }

    // 초기 레이어 정보 설정
    initMapLayer() {
        this.layerArr.forEach((obj) => {
            obj = null;
        });

        this.layerArr[LayerIndex.Traffic] = new TMapLayer(this.map, LayerType.TRAF, LayerInfo.Traffic, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Link1] = new TMapLayer(this.map, LayerType.LINK, LayerInfo.Link1, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Link2] = new TMapLayer(this.map, LayerType.LINK, LayerInfo.Link2, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Link3] = new TMapLayer(this.map, LayerType.LINK, LayerInfo.Link3, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Link4] = new TMapLayer(this.map, LayerType.LINK, LayerInfo.Link4, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Link5] = new TMapLayer(this.map, LayerType.LINK, LayerInfo.Link5, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Ifsc1] = new TMapLayer(this.map, LayerType.IFSC, LayerInfo.Ifsc1, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Ifsc2] = new TMapLayer(this.map, LayerType.IFSC, LayerInfo.Ifsc2, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Ifsc3] = new TMapLayer(this.map, LayerType.IFSC, LayerInfo.Ifsc3, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Ifsc4] = new TMapLayer(this.map, LayerType.IFSC, LayerInfo.Ifsc4, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Ifsc5] = new TMapLayer(this.map, LayerType.IFSC, LayerInfo.Ifsc5, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Road1] = new TMapLayer(this.map, LayerType.ROAD, LayerInfo.Road1, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Road2] = new TMapLayer(this.map, LayerType.ROAD, LayerInfo.Road2, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Road3] = new TMapLayer(this.map, LayerType.ROAD, LayerInfo.Road3, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Road4] = new TMapLayer(this.map, LayerType.ROAD, LayerInfo.Road4, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Road5] = new TMapLayer(this.map, LayerType.ROAD, LayerInfo.Road5, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.VmsIfsc] = new TMapLayer(this.map, LayerType.VMSI, LayerInfo.VmsIfsc, 16, 16, true, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Node] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Node, 16, 16, false, "0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5^0.5");
        this.layerArr[LayerIndex.Cctv] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Cctv, 48, 48, true, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Vms] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Vms, 48, 48, true, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Vds] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Vds, 48, 48, true, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.VdsDet] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.VdsDet, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.VdsR] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.VdsR, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.VdsRDet] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.VdsRDet, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.VdsC] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.VdsC, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.VdsCDet] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.VdsCDet, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Avi] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Avi, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Rse] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Rse, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Dsrc] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Dsrc, 48, 48, true, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Park] = new TMapLayer(this.map, LayerType.PARK, LayerInfo.Park, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.PVms] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.PVms, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Station] = new TMapLayer(this.map, LayerType.BIS, LayerInfo.Station, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Bit] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Bit, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Bit2] = new TMapLayer(this.map, LayerType.BIS, LayerInfo.Bit2, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Bus] = new TMapLayer(this.map, LayerType.BIS, LayerInfo.Bus, 48, 48, false, "1.5^1.5^1.5^1.5^1.5^1.5^1.5^1.5^1.5^1.5");
        this.layerArr[LayerIndex.Sig] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Sig, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.SigDet] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.SigDet, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Signal] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Signal, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.WCam] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.WCam, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.CCam] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.CCam, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.ICam] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.ICam, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.CCol] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.CCol, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.SPCam] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.SPCam, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Event] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Event, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Wthr] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Wthr, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Atmp] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Atmp, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Crs] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Crs, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.CrsCam] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.CrsCam, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Incd] = new TMapLayer(this.map, LayerType.INCD, LayerInfo.Incd, 48, 48, true, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Position] = new TMapLayer(this.map, LayerType.FCLT, LayerInfo.Position, 48, 48, false, "0.5^0.5^0.6^0.6^0.7^0.7^0.8^0.8^1.0^1.2");
        this.layerArr[LayerIndex.Select] = new TMapLayer(this.map, LayerType.Select, LayerInfo.Select, 19, 27, false, "0.7^0.7^0.8^1.0^1.0^1.0^1.2^1.4^1.6^1.8");

        this.layerArr.forEach((obj) => {
            if (obj != null) {
                this.lyrInfoMap.set(obj.lyrName, obj);
            }
        });

        this.layerArr[LayerIndex.Traffic].setLayerVisible([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
        this.layerArr[LayerIndex.Link1].setLayerVisible([16]);
        this.layerArr[LayerIndex.Link2].setLayerVisible([17, 18, 19]);
        this.layerArr[LayerIndex.Ifsc1].setLayerVisible([14]);
        this.layerArr[LayerIndex.Ifsc2].setLayerVisible([15]);
        this.layerArr[LayerIndex.Road1].setLayerVisible([10]);
        this.layerArr[LayerIndex.Road2].setLayerVisible([11, 12, 13]);
    }
    setLayerVisible(ALayerTypeIdx, ...AZoomLevels) {
        this.layerArr[ALayerTypeIdx].setLayerVisible(AZoomLevels);
    }
    // 배경맵 토글
    toggleBaseMap() {
        if (this.config.baseMapLyr.normal.getVisible()) {
            this.config.baseMapLyr.satellite.setVisible(true);
            this.config.baseMapLyr.normal.setVisible(false);
        } else {
            this.config.baseMapLyr.normal.setVisible(true);
            this.config.baseMapLyr.satellite.setVisible(false);
        }
        this.toggleTrafficPolyBackColor();
    }
    toggleTrafficPolyBackColor() {
        for (let ii = LayerIndex.Link1; ii < LayerIndex.Road5; ii++) {
            if (this.layerArr[ii].backPolyColor == "#000") {
                this.layerArr[ii].backPolyColor = "#FFFFFF";
            } else {
                this.layerArr[ii].backPolyColor = "#000";
            }
            this.layerArr[ii].refreshLayer(true);
        }
    }
    // zoom in
    zoomIn() {
        if (this.map !== null && this.currZoom < this.config.mapMaxZoom) {
            this.map.getView().animate({ zoom: this.map.getView().getZoom() + 1, duration: 400 });
        }
    }
    // zoom out
    zoomOut() {
        if (this.map !== null && this.currZoom > this.config.mapMinZoom) {
            this.map.getView().animate({ zoom: this.map.getView().getZoom() - 1, duration: 400 });
        }
    }
    // 표준영역 이동
    stdExtent() {
        if (this.map != null) {
            this.map.getView().animate({ center: this.config.mapCenter, zoom: this.config.mapDefZoom, duration: 400 });
        }
    }
    showTrafficTooltip(AIsShow) {
        this.showTrafficTip = AIsShow;
    }
    showIncidentLayer(AIsShow) {
        this.showIncidentLyr = AIsShow;
        this.showLayer(LayerIndex.Incd, this.showIncidentLyr);
    }
    setLayerSelectMode(ALyrIdx, AIsSelect) {
        this.selectLayerIdx = AIsSelect ? ALyrIdx : -1;
        this.selectMode = AIsSelect;
    }
    setSelectMode(AIsSelect) {
        this.selectMode = AIsSelect;
    }
    setEditColor() {
        EditLinkColor[0] = "#888888";
        EditLinkColor[1] = "#15B337";
        EditLinkColor[2] = "#FFAA00";
        EditLinkColor[3] = "#FF0000";
    }

    setEditMode(ALyrIdx, AIsEdit) {
        this.editLayerIdx = AIsEdit ? ALyrIdx : -1;
        this.editMode = AIsEdit;
        const lyrInfo = this.layerArr[Number(ALyrIdx)];
        if (lyrInfo) {
            lyrInfo.setEditMode(AIsEdit);
        }
        this.initEditMode();
    }
    initEditMode() {
        this.editObj = null;
        this.editFeature = null;
        this.editCoords = null;
    }
    getCenter() {
        return _mapMngr.map.getView().getCenter();
    }
    checkFcltEditMode(ALyrIdx) {
        if (!this.editMode) {
            console.error(`checkFcltEditMode: Is not edit mode`);
            return null;
        }
        const lyrIdx = Number(ALyrIdx);
        const lyrInfo = this.layerArr[lyrIdx];
        if (!lyrInfo) {
            console.error(`checkFcltEditMode: Unknown Layer Index, ${ALyrIdx}`);
            return null;
        }
        if (lyrInfo.lyrType < LayerType.NODE || lyrInfo.lyrType > LayerType.PARK) {
            console.error(`checkFcltEditMode: Not supported Layer Index, ${ALyrIdx}`);
            return null;
        }
        return lyrInfo;
    }

    showFcltObjectText(ALyrIdx, AIsShow) {
        const lyrInfo = this.checkFcltEditMode(ALyrIdx);
        if (!lyrInfo) {
            return;
        }
        lyrInfo.setShowText(Boolean(AIsShow));
    }
    editFcltObject(ALyrIdx, ANmbr, AIsEdit, AComm) {
        const lyrInfo = this.checkFcltEditMode(ALyrIdx);
        if (!lyrInfo) {
            return;
        }
        const obj = lyrInfo.findObject(ANmbr);
        if (!obj) {
            return;
        }
        obj.setEditObject(Boolean(AIsEdit), AComm);
    }
    deleteFcltObject(ALyrIdx, ANmbr) {
        const lyrInfo = this.checkFcltEditMode(ALyrIdx);
        if (!lyrInfo) {
            return;
        }
        lyrInfo.deleteFcltObject(ANmbr);
    }
    createFcltObject(ALyrIdx, ANmbr, AId, AName, AComm, AXCrdn, AYCrdn) {
        const lyrInfo = this.checkFcltEditMode(ALyrIdx);
        if (!lyrInfo) {
            return;
        }
        let x_crdn = _mapMngr.centerPos[0];
        let y_crdn = _mapMngr.centerPos[1];
        if (AXCrdn && AYCrdn) {
            x_crdn = Number(AXCrdn);
            y_crdn = Number(AYCrdn);
        }
        lyrInfo.createFcltObject(ANmbr, AId, AName, AComm, x_crdn, y_crdn);
    }
    initFcltObject(ALyrIdx, AFcltObjs) {
        const lyrInfo = this.checkFcltEditMode(ALyrIdx);
        if (!lyrInfo) {
            return;
        }
        const poiLayer = lyrInfo.initFcltObject(AFcltObjs);
        if (poiLayer) {
            let layerArr = this.config.poiGroup.getLayers().getArray();
            if (layerArr.length == 0) {
                this.config.poiGroup.getLayers().push(poiLayer);
            }
            // else {
            //     layerArr.forEach((lyr) => {
            //         console.log(lyr);
            //     });
            // }
        }
    }

    initEditLink(ALyrIdx) {
        if (!this.editMode) {
            console.error(`initEditLink: Is not edit mode`);
            return;
        }
        const lyrIdx = Number(ALyrIdx);
        const lyrInfo = this.layerArr[lyrIdx];
        if (!lyrInfo) {
            console.error(`initEditLink: Unknown Layer Index, ${ALyrIdx}`);
            return;
        }
        if (lyrInfo.lyrType > LayerType.Road5) {
            console.error(`initEditLink: Not supported Layer Index, ${ALyrIdx}`);
            return;
        }
        const objs = this.getLayerObject(ALyrIdx);
        objs.forEach((obj) => {
            if (obj.color !== 0) {
                obj.color = 0;
                obj.setEditMarkerStyle(this.currZoom);
            }
        });
    }
    updateEditLink(ALyrIdx, ANmbr, ASeqColor, AIsClick) {
        if (!this.editMode) {
            console.error(`updateEditLink: Is not edit mode`);
            return;
        }
        const lyrIdx = Number(ALyrIdx);
        const lyrInfo = this.layerArr[lyrIdx];
        if (!lyrInfo) {
            console.error(`updateEditLink: Unknown Layer Index, ${ALyrIdx}`);
            return;
        }
        if (lyrInfo.lyrType > LayerType.Road5) {
            console.error(`updateEditLink: Not supported Layer Index, ${ALyrIdx}`);
            return;
        }
        const obj = lyrInfo.findObject(ANmbr);
        if (!obj) {
            console.error(`updateEditLink, Not found object: ${ALyrIdx}, ${ANmbr}`);
            return;
        }
        if (!obj.marker) {
            console.error(`updateEditLink, Not found feature object: ${ALyrIdx}, ${ANmbr}`);
            return;
        }
        obj.color = Number(ASeqColor);
        obj.setEditMarkerStyle(this.currZoom);
        if (AIsClick) {
            _mapMngr.selLinkStyle = obj.marker.getStyle();
        }
    }

    initTraf(ALyrIdx, ARefresh) {
        const objs = this.getLayerObject(ALyrIdx);
        objs.forEach((obj) => {
            obj.cmtrGradCd = "0";
            obj.sped = 0;
        });
        if (ARefresh) {
            this.refreshLayer(ALyrIdx);
        }
    }
    updateTraf(ALyrIdx, AObjs) {
        this.initTraf(ALyrIdx, false);
        const objs = this.getLayerObject(ALyrIdx);
        //console.log(objs);
        if (objs) {
            // 모든 데이터 초기화
            AObjs.forEach((obj) => {
                const traf = objs.get(obj.id);
                if (traf) {
                    traf.cmtrGradCd = obj.cmtr_grad_cd.slice(-1);
                    //traf.color = Number(traf.cmtrGradCd);
                    traf.sped = obj.sped;
                    //console.log(`${obj.id}, ${traf.cmtrGradCd},  ${traf.sped}`);
                    //console.log(traf);
                }
            });
        }
        this.refreshLayer(ALyrIdx);
    }

    addFeature(ALyrIdx, AFeature) {
        const lyrIdx = Number(ALyrIdx);
        const lyrInfo = this.layerArr[lyrIdx];
        if (lyrInfo) {
            lyrInfo.addFeature(AFeature);
        }
    }

    getInfo() {
        return {
            zoom : _mapMngr.currZoom,
            center : _mapMngr.centerPos,
        };
    }
    // 레이어 객체 선택
    getLayer(ALyrIdx) {
        const lyrIdx = Number(ALyrIdx);
        if (lyrIdx >= LayerIndex.Traffic && lyrIdx <= LayerIndex.Incd) {
            return this.layerArr[lyrIdx];
        }
        return null;
    }

    // 소통정보 레이어 보이기/숨기기(소통정보 레이어만 해당)
    showTrafficLayer(AIsShow) {
        this.showTrafficLyr = AIsShow;
        if (this.config.trafGroup.getVisible() != this.showTrafficLyr) {
            this.config.trafGroup.setVisible(this.showTrafficLyr);
        }
    }
    showFacilityGroupLayer(AIsShow) {
        if (this.config.poiGroup.getVisible() != AIsShow) {
            this.config.poiGroup.setVisible(AIsShow);
        }
    }
    showEventGroupLayer(AIsShow) {
        if (this.config.eventGroup.getVisible() != AIsShow) {
            this.config.eventGroup.setVisible(AIsShow);
        }
    }

    // 레이어 화면 보이기/숨기기(POI 레이어 만 해당)
    showFacilityLayer(ALyrName, AShow) {
        const lyrInfo = this.lyrInfoMap.get(ALyrName);
        if (!lyrInfo) {
            console.error("showFacilityLayer, 레이어를 찾을 수 없습니다: " + ALyrName);
            //alert("[showFacilityLayer] 레이어를 찾을 수 없습니다.: " + ALyrName);
            return;
        }
        if (lyrInfo.lyrIdx >= LayerIndex.Traffic && lyrInfo.lyrIdx <= LayerIndex.VmsIfsc) {
            return;
        }
        this.showLayer(lyrInfo.lyrIdx, AShow);
    }

    // 레이어 화면 보이기/숨기기(POI 레이어 만 해당)
    showLayer(ALyrIdx, AIsShow) {
        const lyrInfo = this.layerArr[ALyrIdx];
        if (!lyrInfo) {
            console.error("showLayer, Unknown LayerIndex, " + ALyrIdx);
            alert("showLayer: Unknown LayerIndex, " + ALyrIdx);
            return;
        }

        if (ALyrIdx >= LayerIndex.Traffic && ALyrIdx <= LayerIndex.VmsIfsc) {
            this.showTrafficLyr = AIsShow;
            for (let ii = LayerIndex.Traffic; ii <= LayerIndex.VmsIfsc; ii++) {
                if (this.layerArr[ii] != null) {
                    this.layerArr[ii].lyrVisible = ii == ALyrIdx;
                    this.layerArr[ii].refreshLayer();
                }
            }
        }

        lyrInfo.lyrVisible = AIsShow;
        lyrInfo.refreshLayer();
    }
    // 레이어 이름설정
    setLayerName(ALyrIdx, ALyrName) {
        const lyrInfo = this.layerArr[ALyrIdx];
        if (!lyrInfo) {
            console.error("setLayerName, Unknown LayerIndex, " + ALyrIdx);
            alert("setLayerName: Unknown LayerIndex, " + ALyrIdx);
            return;
        }
        lyrInfo.lyrName = ALyrName;
    }
    // 레이어 툴팁설정
    setLayerTooltip(ALyrIdx, AIsShow) {
        const lyrInfo = this.layerArr[ALyrIdx];
        if (!lyrInfo) {
            console.error("setLayerTooltip, Unknown LayerIndex, " + ALyrIdx);
            alert("setLayerTooltip: Unknown LayerIndex, " + ALyrIdx);
            return;
        }
        lyrInfo.showTooltip = AIsShow;
    }
    // 레이어 이미지 설정
    setLayerImage(ALyrIdx, AImage) {
        const lyrInfo = this.layerArr[ALyrIdx];
        if (!lyrInfo) {
            console.error("setLayerImage, Unknown LayerIndex, " + ALyrIdx);
            alert("setLayerImage: Unknown LayerIndex, " + ALyrIdx);
            return;
        }
        //lyrInfo.image = "/images" + AImage;
        lyrInfo.image = AImage;
    }

    // 벡터 레이어 생성
    makeLayer(ALyrIdx, AJsonData) {
        const lyrInfo = this.layerArr[ALyrIdx];
        if (!lyrInfo) {
            console.error("makeLayer, Not found layer: " + ALyrIdx);
            return;
        }
        if (ALyrIdx >= LayerIndex.Traffic && ALyrIdx <= LayerIndex.VmsIfsc) {
            let trafLayer = null;
            if (ALyrIdx == LayerIndex.Traffic) {
                trafLayer = lyrInfo.makeTrafficLayer(_trafMap, AJsonData);
            } else if (ALyrIdx >= LayerIndex.Link1 && ALyrIdx <= LayerIndex.Link5) {
                trafLayer = lyrInfo.makeTrafficLayer(_linkMap, AJsonData);
            } else if (ALyrIdx >= LayerIndex.Ifsc1 && ALyrIdx <= LayerIndex.Ifsc5) {
                trafLayer = lyrInfo.makeTrafficLayer(_ifscMap, AJsonData);
            } else if (ALyrIdx >= LayerIndex.Road1 && ALyrIdx <= LayerIndex.Road5) {
                trafLayer = lyrInfo.makeTrafficLayer(_roadMap, AJsonData);
            }
            if (trafLayer != null) {
                try {
                    this.config.trafGroup.getLayers().push(trafLayer);
                } catch (error) {}
            }
        } else {
            const poiLayer = lyrInfo.makePoiLayer(AJsonData);
            if (poiLayer != null) {
                if (ALyrIdx == LayerIndex.Incd) {
                    this.map.removeLayer(poiLayer);
                    this.map.addLayer(poiLayer);
                } else {
                    try {
                        this.config.poiGroup.getLayers().push(poiLayer);
                    } catch (error) {}
                }
            }
        }
    }
    // 벡터 레이어 생성
    makeEventLayer(ALyrIdx, AJsonData) {
        const lyrInfo = this.layerArr[ALyrIdx];
        if (!lyrInfo) {
            console.error("makeEventLayer, Not found layer: " + ALyrIdx);
            return;
        }

        if (ALyrIdx >= LayerIndex.Traffic && ALyrIdx <= LayerIndex.VmsIfsc) {
            let trafLayer = null;
            if (ALyrIdx == LayerIndex.Traffic) {
                trafLayer = lyrInfo.makeTrafficLayer(_trafMap, AJsonData);
            } else if (ALyrIdx >= LayerIndex.Link1 && ALyrIdx <= LayerIndex.Link5) {
                trafLayer = lyrInfo.makeTrafficLayer(_linkMap, AJsonData);
            } else if (ALyrIdx >= LayerIndex.Ifsc1 && ALyrIdx <= LayerIndex.Ifsc5) {
                trafLayer = lyrInfo.makeTrafficLayer(_ifscMap, AJsonData);
            } else if (ALyrIdx >= LayerIndex.Road1 && ALyrIdx <= LayerIndex.Road5) {
                trafLayer = lyrInfo.makeTrafficLayer(_roadMap, AJsonData);
            }
            if (trafLayer != null) {
                try {
                    this.config.eventGroup.getLayers().push(trafLayer);
                } catch (error) {}
            }
        } else {
            const poiLayer = lyrInfo.makePoiLayer(AJsonData);
            if (poiLayer != null) {
                if (ALyrIdx == LayerIndex.Incd) {
                    this.map.removeLayer(poiLayer);
                    this.map.addLayer(poiLayer);
                } else {
                    try {
                        this.config.eventGroup.getLayers().push(poiLayer);
                    } catch (error) {}
                }
            }
        }
    }
    refreshLayer(ALyrIdx) {
        const lyrInfo = this.layerArr[ALyrIdx];
        if (!lyrInfo) {
            console.error("makeEventLayer, Not found layer: " + ALyrIdx);
            return;
        }
        lyrInfo.refreshLayer();
    }
    // 레이어 객체 오브젝트 생성(소통정보 레이어만 해당)
    makeTrafficObject(ALyrType, AJsonData) {
        switch (ALyrType) {
            //     case LayerType.TRAF:
            //         _trafMap.clear();
            //         AJsonData.forEach((el) => {
            //             _linkMap.set(el.link_id, new TLink(this, el));
            //         });
            //         break;
            case LayerType.LINK:
                _linkMap.clear();
                AJsonData.forEach((el) => {
                    _linkMap.set(el.link_id, new TLink(this, el));
                });
                break;
            case LayerType.IFSC:
                _ifscMap.clear();
                AJsonData.forEach((el) => {
                    _ifscMap.set(el.ifsc_id, new TIfsc(this, el));
                });
                break;
            case LayerType.ROAD:
                _roadMap.clear();
                AJsonData.forEach((el) => {
                    _roadMap.set(el.road_id, new TRoad(this, el));
                });
                break;
            default:
                console.error("makeTrafficObject: Unknown LayerType, ", ALyrType);
        }
    }
    // 시설물 상태정보 업데이트
    updateFacilityObject(ALyrIdx, AJsonData) {
        const lyrInfo = this.layerArr[ALyrIdx];
        if (!lyrInfo) {
            console.error("updateFacilityObject, Not found layer: " + ALyrIdx);
            return;
        }
        //시설물 아이디가 시설물별로 다르게 되어 있어서 하나의 함수로 처리하기 곤란함.
        //switch 문으로 구분해서처리해야함.
        //이전에서 처리하면 전체 통합 정보도 생성할수 있기 때문에 이전에서 처리하도록 하는게 좋음.
        //AJsonData.forEach((el, idx) => {
        //    const obj = lyrInfo.findObject(el.cctv_ctlr_nmbr);
        //    if (obj) {
        //        obj.updateStts(el.cmnc_stts_cd, el.updt_dt);
        //    }
        //});
    }

    // 소통정보 업데이트
    updateTrafficObject(ALyrType, AJsonData) {
        let fromIdx = 0;
        let toIdx = 0;
        switch (ALyrType) {
            case LayerType.LINK:
                AJsonData.forEach((el, idx) => {
                    const obj = _linkMap.get(el.id);
                    if (obj) {
                        obj.updateTraf(el);
                    }
                });
                fromIdx = LayerIndex.Link1;
                toIdx = LayerIndex.Link5;
                break;
            case LayerType.IFSC:
                AJsonData.forEach((el, idx) => {
                    const obj = _ifscMap.get(el.id);
                    if (obj) {
                        obj.updateTraf(el);
                    }
                });
                fromIdx = LayerIndex.Ifsc1;
                toIdx = LayerIndex.Ifsc5;
                break;
            case LayerType.ROAD:
                AJsonData.forEach((el, idx) => {
                    const obj = _roadMap.get(el.id);
                    if (obj) {
                        obj.updateTraf(el);
                    }
                });
                fromIdx = LayerIndex.Road1;
                toIdx = LayerIndex.Road5;
                break;
            default:
                console.error("makeTrafficObject: Unknown LayerType, ", ALyrType);
                return;
        }
        for (let ii = fromIdx; ii <= toIdx; ii++) {
            const lyrInfo = this.layerArr[ii];
            if (lyrInfo) {
                lyrInfo.refreshLayer(false);
            }
        }
    }

   // 지도영역 다시 그리기(화면이 리사이즈 될 경우 등)
   updateSize() {
    if (this.resizeMapUpdateTimer) {
        clearTimeout(this.resizeMapUpdateTimer);
    }
    this.resizeMapUpdateTimer = setTimeout(() => {
        this.map.updateSize();
        // let layerArr = this.config.baseMapLyr.satellite.getLayers().getArray();
        // layerArr.forEach((layer) => layer.redraw());
        // this.config.baseMapLyr.satellite.layers.forEach((lyr) => lyr.redraw());
        // this.config.baseMapLyr.normal.layers.forEach((lyr) => lyr.redraw());
        // this.config.baseMapLyr.satellite.redraw();
        // this.config.baseMapLyr.normal.redraw();
    }, 450);
}
// 지도화면 refresh
    refreshMap() {
        if (this.editMode) {
            // 편집 모드
            this.selLinkWidth = EditLinkLineSize[this.currZoom];
            for (let ii = LayerIndex.Traffic; ii <= LayerIndex.Road5; ii++) {
                this.layerArr[ii].refreshEditLayer();
            }
        } else {
            this.layerArr.forEach((lyr) => {
                if (lyr != null) {
                    lyr.refreshLayer(true);
                }
            });
        }
    }
    setZoom(AZoom) {
        if (AZoom) {
            // 줌이 설정된경우
            const reqZoom = Number(AZoom);
            if (reqZoom != 0) {
                if (this.currZoom != reqZoom) {
                    this.map.getView().setZoom(reqZoom);
                }
            }
        }
    }

    movePos(ALat, ALng, AZoom) {
        if (AZoom) {
            // 줌이 설정된경우
            const reqZoom = Number(AZoom);
            if (reqZoom != 0) {
                if (this.currZoom != reqZoom) {
                    this.map.getView().setZoom(reqZoom);
                }
            }
        }
        const pos = [Number(ALat), Number(ALng)];
        this.map.getView().setCenter(pos);
    }
    // 좌표로 이동하고 화살표를 보여준다.
    // selectPos(ALat, ALng, AZoom) {
    //     const reqZoom = Number(AZoom);
    //     if (reqZoom != 0) {
    //         if (this.currZoom != reqZoom) {
    //             this.map.getView().setZoom(reqZoom);
    //         }
    //     }
    //     this.showSelectArrow(ALat, ALng);
    // }
    // 객체선택 화살표 표출
    selectPos(ALat, ALng, AZoom) {
        this.movePos(ALat, ALng, AZoom);
        const pos = [Number(ALat), Number(ALng)];
        const lyrInfo = this.layerArr[LayerIndex.Select];
        if (this.timerSelectArrow) {
            window.clearTimeout(this.timerSelectArrow);
            lyrInfo.lyrVisible = false;
            lyrInfo.refreshLayer();
        }
        this.selectFeature.setStyle(
            new ol.style.Style({
                image: new ol.style.Icon({
                    anchor: [0.5, lyrInfo.imgH] /*아이콘 중앙*/,
                    anchorXUnits: "fraction",
                    anchorYUnits: "pixels",
                    opacity: 1,
                    size: [lyrInfo.imgW, lyrInfo.imgH],
                    scale: lyrInfo.scales[this.currZoom],
                    src: lyrInfo.image + ".png",
                }),
            })
        );
        this.selectFeature.getGeometry().setCoordinates(pos);
        lyrInfo.lyrVisible = true;
        lyrInfo.refreshLayer();

        this.timerSelectArrow = window.setTimeout(function () {
            this.timerSelectArrow = null;
            lyrInfo.lyrVisible = false;
            lyrInfo.refreshLayer();
        }, 3000);
    }

    hideObjectPosition() {
        const lyrInfo = this.layerArr[LayerIndex.Select];
        lyrInfo.lyrVisible = false;
        lyrInfo.refreshLayer();
    }

    showObjectPosition(ALat, ALng, AImg, AZoom) {
        this.movePos(ALat, ALng, AZoom);
        const pos = [Number(ALat), Number(ALng)];
        const lyrInfo = this.layerArr[LayerIndex.Select];
        this.selectFeature.setStyle(
            new ol.style.Style({
                image: new ol.style.Icon({
                    anchor: [0.5, 0.5] /*아이콘 중앙*/,
                    anchorXUnits: "fraction",
                    anchorYUnits: "pixels",
                    opacity: 1,
                    //size: [lyrInfo.imgW, lyrInfo.imgH],
                    //scale: lyrInfo.scales[this.currZoom],
                    src: AImg,
                }),
            })
        );
        this.selectFeature.getGeometry().setCoordinates(pos);
        lyrInfo.lyrVisible = true;
        lyrInfo.refreshLayer();
    }

    // 레이어에 있는 모든 객체를 Map으로 반환
    getLayerObject(ALyrIdx) {
        const lyrIdx = Number(ALyrIdx);
        const lyrInfo = this.layerArr[lyrIdx];
        if (!lyrInfo) {
            console.error(`getLayerObject, Not found layer: ${ALyrIdx}, ${ANmbr}`);
            return;
        }
        return lyrInfo.getLayerObject();
    }
    // 레이어에 있는 모든 객체를 삭제한다.
    removeLayerObject(ALyrIdx) {
        const lyrIdx = Number(ALyrIdx);
        const lyrInfo = this.layerArr[lyrIdx];
        if (!lyrInfo) {
            console.error(`removeLayerObject, Not found layer: ${ALyrIdx}, ${ANmbr}`);
            return;
        }
        lyrInfo.removeLayerObject();
    }
    selectLayerObject(ALyrIdx, ANmbr, AZoom) {
        const lyrIdx = Number(ALyrIdx);
        const lyrInfo = this.layerArr[lyrIdx];
        if (!lyrInfo) {
            console.error(`selectLayerObject, Not found layer: ${ALyrIdx}, ${ANmbr}`);
            return;
        }
        const obj = lyrInfo.findObject(ANmbr);
        if (!obj) return;
        if (!obj.marker) {
            console.error(`selectLayerObject, Not found feature object: ${ALyrIdx}, ${ANmbr}`);
            return;
        }
        const center = obj.marker.getGeometry().getCoordinates();
        if (lyrIdx <= LayerIndex.VmsIfsc) {
            this.selectPos(center[0][0], center[0][1], AZoom);
        } else {
            this.selectPos(center[0], center[1], AZoom);
        }
    }

    // 레이어 객체 선택
    findLayerObject(ALyrIdx, AObjNmbr) {
        const lyrIdx = Number(ALyrIdx);
        if (lyrIdx >= LayerIndex.Traffic && lyrIdx <= LayerIndex.Incd) {
            const obj = this.layerArr[lyrIdx].findObject(AObjNmbr);
            //console.log(obj);
            if (lyrIdx >= LayerIndex.Link1 && lyrIdx <= LayerIndex.Road5) {
                return obj ? obj.refObj : null;
            }
            return obj;
        }
        return null;
    }
    findLinkObject(AObjNmbr) {
        return _linkMap.get(AObjNmbr);
    }
    findIfscObject(AObjNmbr) {
        return _ifscMap.get(AObjNmbr);
    }
    findRoadObject(AObjNmbr) {
        return _roadMap.get(AObjNmbr);
    }

    extentLayer(ALyrIdx) {
        const lyrIdx = Number(ALyrIdx);
        const lyrInfo = this.layerArr[lyrIdx];
        if (!lyrInfo) {
            console.error(`extentLayer: Unknown Layer Index, ${ALyrIdx}`);
            return;
        }
        lyrInfo.extentLayer();
    }
    // 레이어의 해당 객체가 중심이 되도록 영역을 맞춤
    // ANmbrs = Array
    extentLayerObject(ALyrIdx, ANmbrs) {
        const lyrInfo = this.layerArr[ALyrIdx];
        if (!lyrInfo) {
            console.error("extentLayerObject, Not found layer: " + ALyrIdx);
            return;
        }
        if (!ANmbrs || ANmbrs.length == 0) {
            return;
        }

        if (ALyrIdx >= LayerIndex.Traffic && ALyrIdx <= LayerIndex.VmsIfsc) {
            //if (this.editMode) {
            if (ANmbrs.length == 1) {
                const obj = lyrInfo.findObject(ANmbrs[0]);
                if (obj) {
                    const extent = obj.marker.getGeometry().getExtent();
                    this.map.getView().fit(extent, this.map.getSize());
                }
            } else {
                let extent = ol.extent.createEmpty();
                ANmbrs.forEach((el) => {
                    const obj = lyrInfo.findObject(el);
                    if (obj) {
                        ol.extent.extend(extent, obj.marker.getGeometry().getExtent());
                    }
                });
                this.map.getView().fit(extent, this.map.getSize());
            }
            //}
        } else {
            // POI Layer(객체 하나만 선택)
            const obj = lyrInfo.findObject(ANmbrs[0]);
            if (obj) {
                const extent = obj.marker.getGeometry().getExtent();
                this.map.getView().fit(extent, this.map.getSize());
            }
        }
    }

    saveImage(APageName, ADownloadElId) {
        const _map = this.map;
        _map.once("rendercomplete", () => {
            const mapCanvas = document.createElement("canvas");
            const size = _map.getSize();
            mapCanvas.width = size[0];
            mapCanvas.height = size[1];
            const mapContext = mapCanvas.getContext("2d");
            Array.prototype.forEach.call(_map.getViewport().querySelectorAll(".ol-layer canvas, canvas.ol-layer"), function (canvas) {
                if (canvas.width > 0) {
                    const opacity = canvas.parentNode.style.opacity || canvas.style.opacity;
                    mapContext.globalAlpha = opacity === "" ? 1 : Number(opacity);

                    const backgroundColor = canvas.parentNode.style.backgroundColor;
                    if (backgroundColor) {
                        mapContext.fillStyle = backgroundColor;
                        mapContext.fillRect(0, 0, canvas.width, canvas.height);
                    }

                    let matrix;
                    const transform = canvas.style.transform;
                    if (transform) {
                        // Get the transform parameters from the style's transform matrix
                        matrix = transform
                            .match(/^matrix\(([^\(]*)\)$/)[1]
                            .split(",")
                            .map(Number);
                    } else {
                        matrix = [parseFloat(canvas.style.width) / canvas.width, 0, 0, parseFloat(canvas.style.height) / canvas.height, 0, 0];
                    }
                    // Apply the transform to the export map context
                    CanvasRenderingContext2D.prototype.setTransform.apply(mapContext, matrix);
                    mapContext.drawImage(canvas, 0, 0);
                }
            });
            const downloadMapName = APageName + " 지도_" + this.currentTime() + ".png";
            const link = document.getElementById(ADownloadElId);
            document.getElementById(ADownloadElId).setAttribute("download", downloadMapName);
            if (navigator.msSaveBlob) {
                navigator.msSaveBlob(mapCanvas.msToBlob(), downloadMapName);
            } else {
                link.href = mapCanvas.toDataURL();
                link.click();
            }
        });
        _map.renderSync();
    }

    currentTime() {
        const d = new Date();
        return (
            d.getFullYear() +
            (d.getMonth() + 1 > 9 ? (d.getMonth() + 1).toString() : "0" + (d.getMonth() + 1)) +
            (d.getDate() > 9 ? d.getDate().toString() : "0" + d.getDate().toString()) +
            (d.getHours() > 9 ? d.getHours().toString() : "0" + d.getHours().toString()) +
            (d.getMinutes() > 9 ? d.getMinutes().toString() : "0" + d.getMinutes().toString()) +
            (d.getSeconds() > 9 ? d.getSeconds().toString() : "0" + d.getSeconds().toString())
        );
    }
}

function getParseDateTime(param){
    if (param.length <= 14) {
        let str = "";
        str += param.substring(0, 4);
        str += "-";
        str += param.substring(4, 6);
        str += "-";
        str += param.substring(6, 8);
        str += " ";
        str += param.substring(8, 10);
        str += ":";
        str += param.substring(10, 12);
        str += ":";
        str += param.substring(12, 14);
        return str;
    }
    else {
        return param;
    }
}
