let org_console = $.extend({}, console || {}, true);
const convertArgsToString = (args) => {
    let txt = '';
    for (let item of args) {
        txt += `${(typeof item == 'object' ? JSON.stringify(item) : item)} `;
    }
    return txt;
}
console = $.extend(console, {
    log: (...args) => {
        try {
            if (window.device != undefined) {
                window.plugins.gntel.pushGLog("log", convertArgsToString(args));
            }
        } catch (ex) { }
        org_console.log(convertArgsToString(args));
    },
    info: (...args) => {
        try {
            if (window.device != undefined) {
                window.plugins.gntel.pushGLog("info", convertArgsToString(args));
            }
        } catch (ex) { }
        org_console.log(convertArgsToString(args));
    },
    warn: (...args) => {
        try {
            if (window.device != undefined) {
                window.plugins.gntel.pushGLog("warn", convertArgsToString(args));
            }
        } catch (ex) { }
        org_console.log(convertArgsToString(args));
    },
    error: (...args) => {
        try {
            if (window.device != undefined) {
                window.plugins.gntel.pushGLog("error", convertArgsToString(args));
            }
        } catch (ex) { }
        org_console.error(convertArgsToString(args));
    }
}, true);

const getMimeType = (fileName) => {
    const ext = fileName.split('.').pop().toLowerCase();
    switch (ext) {
        case 'pdf':  return 'application/pdf';
        case 'doc':  case 'docx': return 'application/msword';
        case 'ppt':  case 'pptx': return 'application/vnd.ms-powerpoint';
        case 'xls':  case 'xlsx': return 'application/vnd.ms-excel';
        case 'apk':  return 'application/vnd.android.package-archive';
        case 'zip':  return 'application/zip';
        case 'hwp':  return 'application/hwp';
        case 'txt':  return 'text/plain';
        case 'png':  return 'image/png';
        case 'jpg':  case 'jpeg': return 'image/jpeg';
        default:     return 'application/octet-stream';
    }
};

$(() => {
    $(".tab_btn_setting > li").click(function () {
        var li = $(this).index();
        $(".tab_btn_setting > li").removeClass("on");
        $(this).addClass("on");

        $(".tab_wrap_setting > li").removeClass("show");
        $(".tab_wrap_setting > li").eq(li).addClass("show");
    });

    $('.toggle').click(function () {
        $('.mcaller_menu,.main_cont,.sub_cont,.call_cont,.view_cont,.dial_cont,.foot_btn').toggleClass('active');
    });

    // 레이어팝업 닫기
    $(document).on('click', '.popup_close:not(.fade_action), .pop_btn_close', (e) => {
        $(e.currentTarget).closest('.dim_bg').addClass('none');
        $(e.currentTarget).closest('.dim_bg.detail_info').removeAttr('style');
    });

    applySettings();
});

Storage.prototype.setArray = function (key, obj) {
    return this.setItem(key, JSON.stringify(obj))
}
Storage.prototype.getArray = function (key, type = 'object') {
    return JSON.parse(this.getItem(key) || (type == 'object' ? '{}' : '[]'));
}

let menu_info = localStorage.getArray('menu_info'),
    serverData = localStorage.getArray('serverData'),
    readed_contents = localStorage.getArray('readed_contents'),
    userData = window.localStorage.getArray("userData", 'object'),
    user_map = [],
    group_map = localStorage.getArray('group_map', 'list'),
    call_history_list = {},
    history_list = {},
    current_history_tab = "",
    board_seqs = {
        "article": "new"
        , "personnel": "9"
        , "press": "43"
        , "trend": "52"
        , "occasion": "10"
    },
    app_init_pw = serverData.web_init_pw,
    G = {
        init : {}
    },
    fileTransfer = null,
    loader_wrap_count = 0
    ;

//mcid_tel_no_format, mcid_tel_name_format, mcid_mobile_no_format 이 define.js에 값이 설정되어있으면 그값을 사용함.
var mcid_tel_no_format = typeof mcid_tel_no_format == 'undefined'?'`82${parseInt(serverData.svcPrefixNo)}${user.cid_no}`': mcid_tel_no_format;
var mcid_tel_name_format = typeof mcid_tel_name_format == 'undefined'?'`[${user.dept_name}] ${user.name}`':mcid_tel_name_format;
var mcid_mobile_no_format = typeof mcid_mobile_no_format == 'undefined'?'`82${user.mobile_no.replace(/0/,"")}`':mcid_mobile_no_format;

//개인별 설정한 테마 & 폰트 적용
const applySettings = () => {
    if(localStorage.getItem('settings') == null && typeof default_theme != 'undefined' && default_theme != '' && default_theme.startsWith('theme')){
        localStorage.setArray('settings', { "theme": default_theme});
    }

    let settings = localStorage.getArray('settings');
    const fontClass = settings.font || 'font_basic';
    const themeClass = settings.theme || 'theme00';
    if($('.wrap')[0]) $('.wrap')[0].className = `wrap ${settings.theme || 'theme00'} ${settings.font || 'font_basic'}`;

    const $popup = $('.dim_bg.detail_info');
    if ($popup.length > 0) {
        const isHidden = $popup.hasClass('none');

        $popup[0].className = `dim_bg detail_info wrap ${fontClass}`;

        if (isHidden) {
            $popup.addClass('none');
        }
    }
}

const popAlert = (msg, callback) => {
    $('.dim_bg.alert_pop p.txt').html(msg);
    $('.dim_bg.alert_pop .btn_ok').off('click').on('click', function () {
        if (callback) callback();
        $('.dim_bg.alert_pop').addClass('none');
    });

    $('.dim_bg.alert_pop').removeClass('none');
}

const popConfirm = (msg, ok_callback, cancel_callback) => {
    $('.dim_bg.confirm_pop p.txt').html(msg);
    $('.dim_bg.confirm_pop .btn_ok').off('click').on('click', function () {
        if (ok_callback) ok_callback();
        $('.dim_bg.confirm_pop').addClass('none');
    });

    $('.dim_bg.confirm_pop .btn_cancel').off('click').on('click', function () {
        if (cancel_callback) cancel_callback();
        $('.dim_bg.confirm_pop').addClass('none');
    });

    $('.dim_bg.confirm_pop').removeClass('none');
}

const requestAstro = async (url, params, method, excloude_token = false) => {
    if (!url.startsWith('http://') && !url.startsWith('https://')) {
        url = `${serverData.server_host}/astroapi/${url}`
    }
    userData = localStorage.getArray('userData');

    let fetchoptions = {
        method: method
        , headers: {
            "Content-Type": "application/json"
            , 'Authorization': `Bearer ${userData.astroToken}`
        }
        ,signal: AbortSignal.timeout(10000)
    }

    if (excloude_token) {
        delete fetchoptions.headers.Authorization;
    }
    
    if (method.toLowerCase() != 'get') {
        try {
            if(params instanceof FormData){
                delete fetchoptions.headers["Content-Type"];
                fetchoptions.body = params;
            }else{
                fetchoptions.body = JSON.stringify(params);
            }
        } catch (error) {
            console.warn('params is not JSON', params);
        }
    }

    try {
        checkLoaderWrap('plus');
        let response = await fetch(
            url
            , fetchoptions
        );
        checkLoaderWrap('minus');

        if (!response.ok) {
            throw new Error(`Response status: ${response.status}`);
        }

        let response_json = await response.json();

        try {
            if (response_json.code == 'E0001' || response_json.code == '100') {
                popAlert('인증정보가 유효하지 않습니다.', function(){
                    if(location.href.indexOf('server.html') || location.href.indexOf('login.html')){
                        return;
                    }
                    logout(true);
                });

                
            }
        } catch (e) {
            console.warn(e, url);
        }

        return response_json;

    } catch (err) {
        if (err.name === "TimeoutError" || err.name == 'AbortError') {
            console.error("TimeoutError");
            popAlert("네트워크 통신이 원활하지 않습니다.");
        } else {
            console.error(`Error: type: ${err.name}, message: ${err.message}`);
            console.error(err);
        }
        checkLoaderWrap('init');
        throw err;
    }

}

const checkLoaderWrap = (flag) => {
    if(typeof request_loader == 'undefined' || request_loader == false) {
        return;
    }
    if(flag == 'plus'){
        loader_wrap_count++;
        if(loader_wrap_count>0){
            $('.loaderWrap').removeClass('none');
        }
    }else if(flag == 'minus'){
        //minus
        loader_wrap_count--;
        if(loader_wrap_count<=0){
            loader_wrap_count = 0;
            $('.loaderWrap').addClass('none');
        }
    }else if(flag == 'init'){
        loader_wrap_count = 0;
        $('.loaderWrap').addClass('none');
    }
}

const logout = (force = false) => {
    let process = () =>{
        let data = {
            mobileNumber: userData.mobileNumber
        };

        let keys = ['device_uuid','fcmToken','call_history_exp','mobile_extension_num','mobile_extension_used'];
        for(let key of keys){
            if(userData[key] !== undefined){
                data[key] = userData[key];
            }
        }

        syncUserData(data, true)

        location.href = 'login.html';
    }
    if(force){
        process()
    }else{
        popConfirm('로그아웃 하시겠습니까?', () => {
            process();
        })
    }
}

function withdrawal() {
    localStorage.removeItem('userData');
    syncUserData({fcmToken : userData.fcmToken});
    localStorage.removeItem('serverData');
    location.href = "server.html";
}

let substituteVariables = (content, variables) => {
    // 정규 표현식을 사용하여 ${key} 패턴을 찾아서 해당 변수 값으로 치환합니다
    if (variables == null || variables == undefined) {
        return content;
    }
    return content.replace(/{{(.*?)}}/g, (match, key) => {
        return variables[key.trim()] || match;
    });
}


class CancelDownloadError extends Error {
	constructor(...params){
		super(...params);
	}
}
/**
    progress : 0% ~ 100% string
*/
const onDownloadProgress = (progress) => {
    $('.loading .progressbar').css('width', progress);
    $('.loading #progress_value').text(progress);
}
const fileDownloader = {

    downloadCore(fileUrl, fileName) {
    return new Promise((resolve, reject) => {
        const ft = new FileTransfer();
        let targetPath = "";

        if (cordova.platformId === 'android') {
            const deviceVersion = parseInt(device.version);
            targetPath = cordova.file.externalRootDirectory + 'Download/' + fileName;
        } else if (cordova.platformId === 'ios') {
            // iOS는 documentsDirectory를 사용해야 함
            targetPath = cordova.file.documentsDirectory + fileName;
        }

        console.log("최종 저장 경로: " + targetPath);

        const encodedUrl = encodeURI(`${serverData.server_host}/astroapi/${fileUrl}`);

        ft.download(
            encodedUrl,
            targetPath,
            (entry) => resolve(entry),
            (error) => reject(error),
            true,
            { headers: { "Authorization": `Bearer ${userData.astroToken}` } }
        );
    });
    },

    /**
     * 단일 파일 다운로드
     */
    async downloadAndOpenFile(fileUrl, fileName) {
        let loader_interval = null, seconds = 0;
        
        $('.loading').removeClass('none');
        onDownloadProgress('0%');

        loader_interval = setInterval(() => {
            if (seconds <= 98) {
                onDownloadProgress(`${++seconds}%`);
            }
        }, 1000);

        try {
            const entry = await this.downloadCore(fileUrl, fileName);

            seconds = 99;
            onDownloadProgress(`100%`);
            
            setTimeout(() => {
                clearInterval(loader_interval);
                $('.loading').addClass('none');
            }, 500);

            cordova.plugins.fileOpener2.open(
                entry.toURL(),
                getMimeType(fileName),
                {
                    error: (e) => console.error("Error opening file: ", e),
                    success: () => console.log("File opened successfully")
                }
            );
        } catch (error) {
            // 에러 시 정리
            clearInterval(loader_interval);
            $('.loading').addClass('none');
            alert("다운로드 중 오류가 발생했습니다.");
        }
    },

    /**
     * 여러 파일 병렬 다운로드 (오픈 안 함)
     * @param {Array} fileList - [{fileUrl: '', fileName: ''}, ...] 형태의 배열
     */
    async downloadMultipleParallel(fileList) {
        if (!fileList || fileList.length === 0) return;

        $('.loading').removeClass('none');
        onDownloadProgress(`파일 ${fileList.length}개 다운로드 중...`);

        // 모든 파일의 다운로드 Promise를 배열로 만듦 (병렬 준비)
        const downloadPromises = fileList.map(file => 
            this.downloadCore(file.fileUrl, file.fileName)
        );

        try {
            // Promise.all을 통해 모든 다운로드를 동시에 실행
            const results = await Promise.all(downloadPromises);
            
            console.log("병렬 다운로드 완료:", results);
            alert(`총 ${results.length}개의 파일 다운로드가 완료되었습니다.`);
        } catch (error) {
            console.error("병렬 다운로드 중 일부 실패:", error);
            alert("일부 파일 다운로드에 실패했습니다.");
        } finally {
            // 성공/실패 여부와 상관없이 로딩창 닫기
            $('.loading').addClass('none');
        }
    }
};

// const fileDownloader = {
//     downloadAndOpenFile(fileUrl, fileName) {
//         let loader_interval = null, seconds = 0;
//         onDownloadProgress('0%');

//         $('.loading').removeClass('none');

//         loader_interval = setInterval(() => {
//             if (seconds <= 98) {
//                 onDownloadProgress(`${++seconds}%`);
//             }
//         }, 1000);

//         fileTransfer = new FileTransfer();
//         const targetPath = cordova.file.dataDirectory + fileName;

//         fileTransfer.download(
//             `${serverData.server_host}/astroapi/${fileUrl}`,
//             targetPath,
//             function (entry) {
//                 seconds = 99;
//                 onDownloadProgress(`100%`);
//                 setTimeout(function () {
//                     clearInterval(loader_interval);
//                     $('.loading').addClass('none');
//                 }, 500);
//                 console.log("Download complete: " + entry.toURL());

//                 cordova.plugins.fileOpener2.open(
//                     entry.toURL(),
//                     fileDownloader.getMimeType(fileName),
//                     {
//                         error: function (e) {
//                             console.error("Error opening file: ", e);
//                         },
//                         success: function () {
//                             console.log("File opened successfully");
//                         }
//                     }
//                 );
//             },
//             function (error) {
//                 $('.loading').addClass('none');
//                 clearInterval(loader_interval);
//                 console.error("Download error: ", error);
//             },
//             true,
//             {
//                 headers: {
//                     "accept": "*/*",
//                     "Authorization": `Bearer ${userData.astroToken}`
//                 }
//             }
//         );
//     },
//     getMimeType: function (fileName) {
//         const ext = fileName.split('.').pop().toLowerCase();
//         switch (ext) {
//             case 'pdf': return 'application/pdf';
//             case 'doc': case 'docx': return 'application/msword';
//             case 'ppt': case 'pptx': return 'application/vnd.ms-powerpoint';
//             case 'xls': case 'xlsx': return 'application/vnd.ms-excel';
//             case 'apk': return 'application/vnd.android.package-archive';
//             case 'zip': return 'application/zip';
//             case 'hwp': return 'application/hwp';
//             default: return 'application/octet-stream';
//         }
//     }
// }

// 이미지 다운로드
let mdmDownloader = {
    downloadCore(fileUrl, fileName) {
        return new Promise((resolve, reject) => {
            const ft = new FileTransfer();
            let targetPath = "";

            if (cordova.platformId === 'android') {
                const deviceVersion = parseInt(device.version);
                targetPath = cordova.file.externalRootDirectory + 'Download/' + fileName;
            } else if (cordova.platformId === 'ios') {
                // iOS는 documentsDirectory를 사용해야 함
                targetPath = cordova.file.documentsDirectory + fileName;
            }

            console.log("최종 저장 경로: " + targetPath);

            const encodedUrl = encodeURI(`${fileUrl}`);

            ft.download(
                encodedUrl,
                targetPath,
                (entry) => resolve(entry),
                (error) => reject(error),
                true,
                { headers: { "Authorization": `Bearer ${userData.astroToken}` } }
            );
        });
    },
    downloadAndOpenFile(fileurl, fileName) {
        let loader_interval = null, seconds = 0;
        onDownloadProgress('0%');

        $('.loading').removeClass('none');

        loader_interval = setInterval(() => {
            if (seconds <= 98) {
                onDownloadProgress(`${++seconds}%`);
            }
        }, 1000);

        fileTransfer = new FileTransfer();
        const targetPath = "";
        if (cordova.platformId === 'android') {
                const deviceVersion = parseInt(device.version);
                targetPath = cordova.file.externalRootDirectory + 'Download/' + fileName;
        } else if (cordova.platformId === 'ios') {
                // iOS는 documentsDirectory를 사용해야 함
                targetPath = cordova.file.documentsDirectory + fileName;
        }
        fileTransfer.download(
            `${fileurl}`,
            targetPath,
            function (entry) {
                seconds = 99;
                onDownloadProgress(`100%`);
                setTimeout(function () {
                    clearInterval(loader_interval);
                    $('.loading').addClass('none');
                }, 500);
                console.log("Download complete: " + entry.toURL());

                cordova.plugins.fileOpener2.open(
                    entry.toURL(),
                    getMimeType(fileName),
                    {
                        error: function (e) {
                            console.error("Error opening file: ", e);
                        },
                        success: function () {
                            console.log("File opened successfully");
                        }
                    }
                );
            },
            function (error) {
                $('.loading').addClass('none');
                clearInterval(loader_interval);
                console.error("Download error: ", error);
            },
            true,
            {
                headers: {
                    "accept": "*/*",
                    "Authorization": `Bearer ${userData.astroToken}`
                }
            }
        );
    },
        /**
     * 여러 파일 병렬 다운로드 (오픈 안 함)
     * @param {Array} fileList - [{fileUrl: '', fileName: ''}, ...] 형태의 배열
     */
    async downloadMultipleParallel(fileList) {
        if (!fileList || fileList.length === 0) return;

        $('.loading').removeClass('none');
        onDownloadProgress(`파일 ${fileList.length}개 다운로드 중...`);

        // 모든 파일의 다운로드 Promise를 배열로 만듦 (병렬 준비)
        const downloadPromises = fileList.map(file => 
            this.downloadCore(file.fileUrl, file.fileName)
        );

        try {
            // Promise.all을 통해 모든 다운로드를 동시에 실행
            const results = await Promise.all(downloadPromises);
            
            console.log("병렬 다운로드 완료:", results);
            alert(`총 ${results.length}개의 파일 다운로드가 완료되었습니다.`);
        } catch (error) {
            console.error("병렬 다운로드 중 일부 실패:", error);
            alert("일부 파일 다운로드에 실패했습니다.");
        } finally {
            // 성공/실패 여부와 상관없이 로딩창 닫기
            $('.loading').addClass('none');
        }
    },
    downloadAndOpenFile(fileurl, fileName) {
        let loader_interval = null, seconds = 0;
        onDownloadProgress('0%');

        $('.loading').removeClass('none');

        loader_interval = setInterval(() => {
            if (seconds <= 98) {
                onDownloadProgress(`${++seconds}%`);
            }
        }, 1000);

        fileTransfer = new FileTransfer();
        const targetPath = cordova.file.dataDirectory + fileName;

        fileTransfer.download(
            `https://chat.astro.lgntel.com/api/chats/files/${fileurl}`,
            targetPath,
            function (entry) {
                seconds = 99;
                onDownloadProgress(`100%`);
                setTimeout(function () {
                    clearInterval(loader_interval);
                    $('.loading').addClass('none');
                }, 500);
                console.log("Download complete: " + entry.toURL());

                cordova.plugins.fileOpener2.open(
                    entry.toURL(),
                    getMimeType(fileName),
                    {
                        error: function (e) {
                            console.error("Error opening file: ", e);
                        },
                        success: function () {
                            console.log("File opened successfully");
                        }
                    }
                );
            },
            function (error) {
                $('.loading').addClass('none');
                clearInterval(loader_interval);
                console.error("Download error: ", error);
            },
            true,
            {
                headers: {
                    "accept": "*/*",
                    "Authorization": `Bearer ${userData.astroToken}`
                }
            }
        );
    }
}

// 파일 다운로드
const mdmFileDownloader = {
    downloadAndOpenFile(fileurl, fileName) {
        let loader_interval = null, seconds = 0;
        onDownloadProgress('0%');

        $('.loading').removeClass('none');

        loader_interval = setInterval(() => {
            if (seconds <= 98) {
                onDownloadProgress(`${++seconds}%`);
            }
        }, 1000);

        fileTransfer = new FileTransfer();
        const targetPath = cordova.file.dataDirectory + fileName;

        fileTransfer.download(
            `https://chat.astro.lgntel.com/api/chats/files/${fileurl}`,
            targetPath,
            function (entry) {
                seconds = 99;
                onDownloadProgress(`100%`);
                setTimeout(function () {
                    clearInterval(loader_interval);
                    $('.loading').addClass('none');
                }, 500);
                console.log("Download complete: " + entry.toURL());

                cordova.plugins.fileOpener2.open(
                    entry.toURL(),
                    getMimeType(fileName),
                    {
                        error: function (e) {
                            console.error("Error opening file: ", e);
                        },
                        success: function () {
                            console.log("File opened successfully");
                        }
                    }
                );
            },
            function (error) {
                $('.loading').addClass('none');
                clearInterval(loader_interval);
                console.error("Download error: ", error);
            },
            true,
            {
                headers: {
                    "accept": "*/*",
                    "Authorization": `Bearer ${userData.astroToken}`
        }
            }
        );
    }
}


const gformatter = {
    /**
     * @param {*} date 
     *  Type Date 
     *  or String(yyyy-mm-dd hh:mm:ss ** hh:mm:ss부분은 optional)
     * @param pattern String 'y-M-d h:m:s'
     */
    date: function (date, pattern) {
        let y, M, d, h, m, s;

        if (date instanceof String) {
            try {
                date = date
                    .replace(/[^\d]/g, '');
                if (date.length == 8) {
                    date = date.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3');
                } else if (date.length == 12) {
                    date = date.replace(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})/, '$1-$2-$3 $4:$5');
                } else if (date.length == 14) {
                    date = date.replace(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, '$1-$2-$3 $4:$5:$6');
                } else {
                    console.log('INVALID DATE STRING');
                    return null;
                }

                date = new Date(date);

            } catch (error) {
                console.log('INVALID DATE STRING');
            }
        }

        y = date.getFullYear().toString();
        M = (date.getMonth() + 1).toString();
        if (M.length == 1) { M = '0' + M; }
        d = date.getDate().toString();
        if (d.length == 1) { d = '0' + d; }
        h = date.getHours().toString();
        if (h.length == 1) { h = '0' + h; }
        mm = date.getMinutes().toString();
        if (mm.length == 1) { mm = '0' + mm; }
        ss = date.getSeconds().toString();
        if (ss.length == 1) { ss = '0' + ss; }

        if (pattern != undefined) {
            return pattern
                .replace(/y/, y)
                .replace(/M/, M)
                .replace(/d/, d)
                .replace(/h/, h)
                .replace(/m/, mm)
                .replace(/s/, ss);
        }
        return y + '.' + M + '.' + d + ' ' + h + ':' + mm + ':' + ss;
    }
    , tel: function (telno) {
        let r_telno = telno.replace(/[^\d]/g, '');
        switch (r_telno.length) {
            case 7:
            case 8:
                return r_telno.replace(/(\d{3,4})(\d{4})/, '$1-$2');
            case 9:
                return r_telno.replace(/(\d{2})(\d{3})(\d{4})/, '$1-$2-$3');
            case 10:
                return r_telno.startsWith('02') ? r_telno.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3') : r_telno.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
            case 11:
                return r_telno.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
            default:
                return telno;
        }
    }
    , number: function (s) {
        return (s || '').toString().replace(/[^0-9]/g, '');
    }
    , string : function (v) {
        return v == null ? "" : String(v);
    }
}

/**
 * product_name : mcaller or callbanner or mbanner
 */
const authorizeCust = (product_name, cust_name, cust_pw, mobile_number) => {
    if (product_name == undefined) {
        try {
            product_name = (title != '' ? title.toLocaleLowerCase() : 'mcaller');
        } catch (error) {
            product_name = 'mcaller';
        }
    }

    requestAstro(`https://ent-update.lgntel.com/astroapi/verctrl/v1/appinfo/check/${product_name}/${cust_name}`, {}, 'get', true)
    .then(response => {

        switch (response.code) {
            case '0000':
                let json = {};
                try {
                    json = JSON.parse(response.data.info);
                } catch (error) {
                    console.log('appinfo/check parse response error', response);
                    json = {};
                }

                syncServerData({
                    server_host: json.server_host
                });

                let formdata = new FormData();
                formdata.append('cust_name' , cust_name);
                formdata.append('cust_pw' , cust_pw);

                requestAstro(
                    `mdial/v1/searchcust`
                    , formdata
                    , 'post'
                )
                .then(response => {
                    let code = response.code || response.errcode
                    switch (code) {
                        case "0000":
                        case "0":
                            // let data = {
                            //     companyId: cust_name,
                            //     companyPw: cust_pw,
                            //     autoLogin: "",
                            //     checkIdSave: "",
                            //     //mobileNumber: mobile_number
                            // };

                            // userData = {...userData, mobileNumber: mobile_number}
                            // localStorage.setArray('userData',userData);

                            syncUserData(
                                {
                                    mobileNumber: mobile_number
                                }
                            );

                            syncServerData(
                                {
                                    ...response,
                                    companyId: cust_name,
                                    companyPw: cust_pw
                                }
                            );

                            if (typeof device != 'undefined') {
                                let OS = device.platform === "Android" ? "ANDROID" : "IOS";

                                window.plugins.gntel.setUpdateContentsUrl(`https://ent-update.lgntel.com/astroapi/verctrl/v1/check/${json.update_name}/${OS}`, function () {
                                    $("#loading-overlay").fadeIn();
                                    window.plugins.gntel.updateContents(function () {
                                        $("#loading-overlay").fadeOut();
                                        window.plugins.gntel.loadLocalFile("index.html");
                                    }, function () {
                                        $("#loading-overlay").fadeOut();
                                        window.plugins.gntel.loadLocalFile("index.html");
                                    });
                                });
                            } else {
                                location.href = 'index.html';
                            }

                            break;

                        default:
                            if(response.message){
                                popAlert(response.message);
                            }
                            break;
                    }
                });
                break;
            case 'E1001':
                popAlert('인증정보 불일치');
                break;
            default:
                popAlert('알 수 없는 오류가 발생했습니다.<br/>관리자에게 문의하세요.');
                break;
        }
    })
    .catch(e => popAlert('회사인증을 진행할 수 없습니다.'));
    ;
}

const parsePortString = (str) => {
    if (str == undefined || str.trim() == '') return '';
    return str.replace(/:?(.*)/, ':$1');
}

let syncServerData = (data, force = false) => {

    serverData = localStorage.getArray('serverData');

    let server_data_keys = [
        //회사관련
        { key: 'corp_code', jsonkey: 'corp_code' },
        { key: 'pbxType', jsonkey: 'cust_pbx_type' },
        { key: 'svcPrefixNo', jsonkey: 'cust_svc_prefix_no' },
        { key: 'cidLength', jsonkey: 'cust_cid_length' },
        { key: 'fsSvcNo', jsonkey: 'cust_fs_svc_no' },
        { key: 'cust_con_port', jsonkey: 'cust_con_port' },
        { key: 'cust_con_pbx_ip', jsonkey: 'cust_con_pbx_ip' },
        { key: 'cust_name', jsonkey: 'cust_name' },
        { key: 'cust_logo1', jsonkey: 'cust_logo1' },
        { key: 'cust_logo2', jsonkey: 'cust_logo2' },
        { key: 'cust_logo3', jsonkey: 'cust_logo3' },
        { key: 'accCodeUsed', jsonkey: 'cust_acc_code_used' },
        { key: 'accCode', jsonkey: 'cust_acc_code' },
        { key: 'digitCount', jsonkey: 'cust_digit_cnt' },
        { key: 'cust_func_code', jsonkey: 'cust_func_code' },
        { key: 'cust_func_code', jsonkey: 'funcCode' },
        { key: 'rootGroupcode', jsonkey: 'rootGroupcode' },
        { key: 'notice_used', jsonkey: 'notice_used', default: 'Y' },
        { key: 'notice_def_remains', jsonkey: 'notice_def_remains', default: '0' },
        { key: 'notice_manage_groups', jsonkey: 'notice_manage_groups'},
        { key: 'forward_call_mention', jsonkey: 'forward_call_mention' },
        { key: 'call_thru_prefix', jsonkey: 'call_thru_prefix' },
        { key: 'otp_used', jsonkey: 'otp_used' },
        { key: 'otp_type', jsonkey: 'otp_type' },
        { key: 'profile_used', jsonkey: 'profile_used' },
        { key: 'default_profile', jsonkey: 'default_profile' },
        { key: 'server_host', jsonkey: 'server_host' },
        { key: 'companyId', jsonkey: 'companyId' },
        { key: 'companyPw', jsonkey: 'companyPw' },
        { key: 'main_num', jsonkey: 'main_num'},
        { key: 'main_num_used', jsonkey: 'main_num_used' },
        { key: 'mobile_extension_num', jsonkey: 'mobile_extension_num' },
        { key: 'mobile_extension_used', jsonkey: 'mobile_extension_used' },
        { key: 'web_init_pw', jsonkey: 'web_init_pw' }
    ];

    if (force) {
        serverData = data;
    } else {
        server_data_keys.map(item => {
            if (data[item.jsonkey] !== undefined) {
                serverData[item.key] = data[item.jsonkey];
            } else if (item.default !== undefined) {
                serverData[item.key] = item.default;
            }
        });
    }

    localStorage.setArray('serverData', serverData);

}

let syncUserData = (data, force = false) => {
    userData = localStorage.getArray('userData');

    let user_data_keys = [
        //교환기인증 관련
        { key: 'dn', jsonkey: 'dn' },
        { key: 'id', jsonkey: 'id' },
        { key: 'node', jsonkey: 'node' },
        { key: 'systemType', jsonkey: 'systemType' },
        { key: 'token', jsonkey: 'token' },

        //astro checkstatus
        { key: 'etcNo', jsonkey: 'etc_no' },
        { key: 'etcNo', jsonkey: 'etcNo' },
        { key: 'userId', jsonkey: 'userId' },
        { key: 'userName', jsonkey: 'userName' },
        { key: 'noticeRegPermitted', jsonkey: 'noticeRegPermitted' },
        { key: 'deptCode', jsonkey: 'deptCode' },
        { key: 'astroToken', jsonkey: 'astroToken' },
        { key: 'allow_push', jsonkey: 'allow_push' },
        { key: 'push_start_time', jsonkey: 'push_start_time' },
        { key: 'push_end_time', jsonkey: 'push_end_time' },
        { key: 'mobileNumber', jsonkey: 'mobileNumber' },
        { key: 'prefixEx', jsonkey: 'prefixEx' },
        { key: 'mobile_extension_num', jsonkey: 'mobile_extension_num'},
        { key: 'mobile_extension_used', jsonkey: 'mobile_extension_used'},
         
        //firebase token
        { key: 'fcmToken', jsonkey: 'fcmToken' },

        { key: 'device_uuid', jsonkey: 'device_uuid' },

        { key: 'autoLogin', jsonkey: 'autoLogin' },

        { key: 'call_history_exp', jsonkey: 'call_history_exp' } 
    ];

    if (force) {
        userData = data;
    } else {
        user_data_keys.map(item => {
            if (data[item.jsonkey] !== undefined) {
                userData[item.key] = data[item.jsonkey];
            } else if (item.default !== undefined) {
                userData[item.key] = item.default;
            }
        });
    }

    localStorage.setArray('userData', userData);
}

const phonenumberValidation = (inputtxt) => {
    console.log(inputtxt);
    if ($.isEmptyObject(inputtxt)) {
        return false;
    } else if (inputtxt.length > 3) {
        return true;
    } else {
        return false;
    }
}

let emptyToSpace = (v) => { return (v == null || (v + '').trim() === '') ? '' : (v + ''); }

let isAndroid = () => { return (window.device?.platform || '').toLowerCase() === 'android'; }

let isIOS = () => { return (window.device?.platform || '').toLowerCase() === 'ios'; }

let newContact = [];
let favoriteContact;
let historyContact;
let imsitime = "";
let searchImsitime = "";

// 날짜 포맷 (연-월-일 그룹 헤더용)
function getYear(el) {
    var getYear = el.split("일");
    return getYear[0] + "일";
}

// 썸네일
function getThumbImage(el) {
    return !$.isEmptyObject(el) ? el : "images/thumb.png";
}
function setThumbImage() {
    return "images/thumb.png";
}

// 문자 분류
function isAlphabet(ch) {
    var c = ch.charCodeAt(0);
    return (65 <= c && c <= 90) || (97 <= c && c <= 122);
}
function isKorean(ch) {
    var c = ch.charCodeAt(0);
    return (44032 <= c && c <= 55203);
}

function isNumber(ch) {
    var c = ch.charCodeAt(0);
    return (48 <= c && c <= 57);
}

// 초성 추출
function getCho(str) {
    var rCho = ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"];
    var result = [];
    for (var i in str) {
        var ch = str.substr(i, 1);
        var n = ch.charCodeAt(0) - 0xAC00;
        var jong = n % 28;
        var jung = ((n - jong) / 28) % 21;
        var cho = ((((n - jong) / 28) - jung) / 21);
        result.push(rCho[cho]);
    }
    return result.join('');
}

// 번호 정규화
function cleanPhoneNumber(num) {
    if (!num) return "";
    // 특수문자 제거 (숫자만 남김)
    let cleaned = String(num).replace(/[^\d+]/g, "");
    // +82 → 0으로 변환
    cleaned = cleaned.replace(/^\+82/, "0");
    return cleaned;
}

// 한국형 번호 display 포맷 (cleanPhoneNumber 이후 사용)
function getKoreanNumber(phoneNumber) {
    if (!phoneNumber) return "";
    if (phoneNumber.startsWith('+')) return phoneNumber;
    if (phoneNumber.length <= 4) return phoneNumber;
    if (phoneNumber.length <= 8) return phoneNumber.replace(/([0-9]+)([0-9]{4})/, "$1-$2");
    return phoneNumber.replace(/(^02.{0}|^01.{1}|[0-9]{3})([0-9]+)([0-9]{4})/, "$1-$2-$3");
}

// 문자열 Bytes로 몇자인지 확인
const getByteLengthOfString = function (s, b, i, c) {
    for (b = i = 0; (c = s.charCodeAt(i++)); b += c >> 11 ? 3 : c >> 7 ? 2 : 1);
    return b;
};

function nomalizeNumber(number) {
    return $.trim(number).replace('+82', '0').replace(/[^0-9]/g, '');
}

function getUserNumber() {
    //var serverData = window.localStorage.getArray("serverData");
    //alert("serverData"+JSON.stringify(serverData));
    var accCodeUsed = serverData.accCodeUsed;
    var accCode = serverData.accCode;
    var digitCount = serverData.digitCount;
    var mobileNumber = userData.mobileNumber;
    digitCount = parseInt(digitCount);
    if (!$.isEmptyObject(mobileNumber)) {
        if (mobileNumber.length > digitCount) {
            if (accCodeUsed == "Y") {
                mobileNumber = accCode + mobileNumber;
            }
        }

        mobileNumber = encodeURIComponent(mobileNumber);
        return mobileNumber;
    } else {
        popAlert("HP번호가 없습니다.", function () {
            goToSetting()
        });
        return "";
    }
}

/* ===== File API with silent LocalStorage fallback (browser-friendly) ===== */


let FS_AVAILABLE = typeof window.requestFileSystem !== "undefined";
const LS_KEY = (name) => `__fs__:${name}`;

// 내부 공통: LS 쓰기(문자열/Blob 모두 지원)
function writeToLocalStorage(filename, data, callback) {
    const key = LS_KEY(filename);

    // 문자열이면 바로 저장
    if (typeof data === "string") {
        try {
            localStorage.setItem(key, data);
            if (callback) callback();
        } catch (e) {
            if (callback) callback(e);
        }
        return;
    }

    // Blob이면 텍스트로 변환해 저장
    if (data instanceof Blob) {
        try {
            const reader = new FileReader();
            reader.onload = function () {
                try {
                    localStorage.setItem(key, reader.result || "");
                    if (callback) callback();
                } catch (e) {
                    if (callback) callback(e);
                }
            };
            reader.onerror = function () {
                if (callback) callback(reader.error || new Error("Blob read failed"));
            };
            reader.readAsText(data);
        } catch (e) {
            if (callback) callback(e);
        }
        return;
    }

    // 그 외 타입은 문자열로 강제 캐스팅
    try {
        localStorage.setItem(key, String(data ?? ""));
        if (callback) callback();
    } catch (e) {
        if (callback) callback(e);
    }
}

// 내부 공통: LS 읽기
function readFromLocalStorage(filename, callback) {
    try {
        const key = LS_KEY(filename);
        const val = localStorage.getItem(key);
        if (callback) callback(val != null ? val : "{}");
    } catch {
        if (callback) callback("{}");
    }
}

const toast = (msg) => {
    $("<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h3>" + msg + "</h3></div>")
        .css({
            "display": "block",
            "position": "fixed",
            "padding": "12px",
            "text-align": "center",
            "color": "#FFF",
            "width": "290px",
            "height": "40px",
            "background": "#00000080",
            "font-weight": "normal",
            "font-size": "15px",
            "border-radius": "25px",
            "left": ($(window).width() - 290) / 2,
            "top": $(window).height() - 140,
            "z-index": 999
        })
        .appendTo(".wrap").delay(1800)
        .fadeOut(100, function () {
            $(this).remove();
        });
}


window.writeFile = function writeFile(filename, data, callback) {
    FS_AVAILABLE = typeof window.requestFileSystem !== "undefined";
    if (FS_AVAILABLE) {
        // Cordova/지원 브라우저: 기존 FileSystem 로직
        let type = window.PERSISTENT;
        let size = 5 * 1024 * 1024;

        function write() {
            window.requestFileSystem(type, size, function (fs) {
                fs.root.getFile(filename, { create: true, exclusive: false }, function (fileEntry) {
                    fileEntry.createWriter(function (fileWriter) {
                        fileWriter.onwriteend = function () { if (callback) callback(); };
                        fileWriter.onerror = function (e) { if (callback) callback(e); };
                        fileWriter.write(data);
                    }, function (error) { if (callback) callback(error); });
                }, function (error) { if (callback) callback(error); });
            }, function (error) { if (callback) callback(error); });
        }

        // 기존 파일이 있으면 삭제 후 쓰기
        window.requestFileSystem(type, size, function (fs) {
            fs.root.getFile(filename, { create: false }, function (fileEntry) {
                fileEntry.remove(function () { write(); }, function () { write(); });
            }, function () { write(); });
        }, function () { write(); });
    } else {
        writeToLocalStorage(filename, data, callback);
    }
};

// ---------- writeFileX ----------
window.writeFileX = function writeFileX(filename, data, callback) {
    FS_AVAILABLE = typeof window.requestFileSystem !== "undefined";
    if (FS_AVAILABLE) {
        let type = window.PERSISTENT;
        let size = 10 * 1024 * 1024;
        window.requestFileSystem(type, size, function (fs) {
            fs.root.getFile(filename, { create: true, exclusive: false }, function (fileEntry) {
                fileEntry.createWriter(function (fileWriter) {
                    fileWriter.onwriteend = function () { if (callback) callback(); };
                    fileWriter.onerror = function (e) { if (callback) callback(e); };
                    fileWriter.write(data);
                }, function (error) { if (callback) callback(error); });
            }, function (error) { if (callback) callback(error); });
        }, function (error) { if (callback) callback(error); });
    } else {
        writeToLocalStorage(filename, data, callback);
    }
};

// ---------- readFile ----------
window.readFile = function readFile(filename, callback) {
    FS_AVAILABLE = typeof window.requestFileSystem !== "undefined";
    if (FS_AVAILABLE) {
        let type = window.PERSISTENT;
        let size = 50 * 1024 * 1024;
        window.requestFileSystem(type, size, function (fs) {
            fs.root.getFile(filename, { create: false, exclusive: false }, function (fileEntry) {
                fileEntry.file(function (file) {
                    const reader = new FileReader();
                    reader.onloadend = function (e) {
                        try {
                            const arrayBuffer = new Uint8Array(e.target.result || new ArrayBuffer(0));
                            const enc = new TextDecoder("utf-8");
                            if (callback) callback(enc.decode(arrayBuffer));
                        } catch {
                            if (callback) callback("{}");
                        }
                    };
                    reader.onerror = function () { if (callback) callback("{}"); };
                    reader.readAsArrayBuffer(file);
                }, function () { if (callback) callback("{}"); });
            }, function () { if (callback) callback("{}"); });
        }, function () { if (callback) callback("{}"); });
    } else {
        readFromLocalStorage(filename, callback);
    }
};

const bindPinchZoom = ($zoomTarget) => {

    if($zoomTarget == undefined) return;
    if($zoomTarget[0].style['font-size'] == ''){
        $zoomTarget[0].style['font-size'] = $zoomTarget.css('font-size');
    }
    let initialDistance = null;
    const minTouchEventDiff = 10;

    const getDistancePointer = (touch1, touch2) => {
        const dx = touch2.clientX - touch1.clientX;
        const dy = touch2.clientY - touch1.clientY;
        return Math.sqrt(dx * dx + dy * dy);
    };

    $zoomTarget.on("touchstart", (event) => {
        const touches = event.originalEvent.touches;
        if (touches.length === 2) {
            initialDistance = getDistancePointer(touches[0], touches[1]);
        }
    });
    
    $zoomTarget.on("touchmove", (event) => {
        const touches = event.originalEvent.touches;
        if (touches.length === 2 && initialDistance !== null) {
            event.preventDefault(); // 필요시 추가
            const currentDistance = getDistancePointer(touches[0], touches[1]);
			let fontsize = $zoomTarget[0].style['font-size'].replace('/^\d/','')
            if (Math.abs(currentDistance - initialDistance) >= minTouchEventDiff) {
                if (currentDistance > initialDistance) {
					$zoomTarget[0].style['font-size'] = $zoomTarget[0].style['font-size'].replace(/[\d.]+/, n => (parseFloat(n) +1 ));
                } else if (currentDistance < initialDistance) {
					$zoomTarget[0].style['font-size'] = $zoomTarget[0].style['font-size'].replace(/[\d.]+/, n => Math.max((parseFloat(n) -1 ), 10));
                }
    
                initialDistance = currentDistance;
            }
        }
    });
    
    $zoomTarget.on("touchend", () => {
        initialDistance = null;
    });
}
