// ==UserScript==
// @name         虚拟地理位置
// @name:zh      虚拟地理位置
// @name:en      Virtual Geographic Location
// @namespace    https://github.com/LaLa-HaHa-Hei/
// @version      1.1.1
// @description  自定义浏览器中的地理位置,在菜单中点击即可启用
// @author       代码见三
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// ==/UserScript==

(function () {
    'use strict';
    class VirtualLocationManager {
        originalGetCurrentPosition = navigator.geolocation.getCurrentPosition
        windowElement = null;
        headerElement = null;
        accuracyInputField = null;
        latitudeInputField = null;
        longitudeInputField = null;
        confirmButtonElement = null;
        styleElement = null;
        isDragging = false;
        offsetX = 0;
        offsetY = 0;

        createPopupWindow() {
            if (this.windowElement !== null) return;

            // 创建主容器
            this.windowElement = document.createElement('div');
            this.windowElement.id = 'VGL-popup-window';

            // 创建头部
            this.headerElement = document.createElement('header');
            this.headerElement.id = 'VGL-popup-window-header';
            this.headerElement.textContent = '虚拟地理位置';

            // 创建主体内容
            const main = document.createElement('main');
            main.id = 'VGL-popup-window-main';

            // 创建精度输入组
            const accuracyGroup = document.createElement('div');
            const accuracyLabel = document.createElement('label');
            accuracyLabel.textContent = '精度';
            this.accuracyInputField = document.createElement('input');
            this.accuracyInputField.type = 'number';
            this.accuracyInputField.id = 'VGL-accuracy-input';
            accuracyGroup.appendChild(accuracyLabel);
            accuracyGroup.appendChild(this.accuracyInputField);

            // 创建纬度输入组
            const latitudeGroup = document.createElement('div');
            const latitudeLabel = document.createElement('label');
            latitudeLabel.textContent = '纬度';
            this.latitudeInputField = document.createElement('input');
            this.latitudeInputField.type = 'number';
            this.latitudeInputField.id = 'VGL-latitude-input';
            latitudeGroup.appendChild(latitudeLabel);
            latitudeGroup.appendChild(this.latitudeInputField);

            // 创建经度输入组
            const longitudeGroup = document.createElement('div');
            const longitudeLabel = document.createElement('label');
            longitudeLabel.textContent = '经度';
            this.longitudeInputField = document.createElement('input');
            this.longitudeInputField.type = 'number';
            this.longitudeInputField.id = 'VGL-longitude-input';
            longitudeGroup.appendChild(longitudeLabel);
            longitudeGroup.appendChild(this.longitudeInputField);

            // 创建按钮容器
            const buttonGroup = document.createElement('div');
            buttonGroup.style.display = 'flex';
            buttonGroup.style.justifyContent = 'center';
            this.confirmButtonElement = document.createElement('button');
            this.confirmButtonElement.id = 'VGL-confirm-button';
            this.confirmButtonElement.textContent = '确定';
            buttonGroup.appendChild(this.confirmButtonElement);

            // 组装所有元素
            main.appendChild(accuracyGroup);
            main.appendChild(latitudeGroup);
            main.appendChild(longitudeGroup);
            main.appendChild(buttonGroup);

            this.windowElement.appendChild(this.headerElement);
            this.windowElement.appendChild(main);

            // 添加到文档
            document.body.appendChild(this.windowElement);
            // 调整为根据top而不是bottom定位,因为拖动逻辑是基于top的
            // setTimeout(() => {
            //     this.windowElement.style.bottom = 'auto'
            //     this.windowElement.style.top = window.innerHeight - this.windowElement.clientHeight - 10 + "px"
            // }, 0);
        }

        injectPopupWindowCSS() {
            if (this.styleElement !== null) return;

            const injectedCSS = `
                #VGL-popup-window {
                    border: 1px solid #ccc;
                    position: fixed;
                    bottom: 10px;
                    left: 10px;
                    background-color: #fff;
                    z-index: 2147483647;
                    color: black;
                    border-radius: 10px;
                    overflow: hidden;
                }
    
                #VGL-popup-window-header {
                    height: 30px;
                    line-height: 30px;
                    background-color: #409EFF;
                    font-size: 18px;
                    font-weight: 500;
                    color: #fff;
                    cursor: move;
                    user-select: none;
                    text-align: center;
                }
    
                #VGL-popup-window-main {
                    padding: 20px;
                    display: flex;
                    flex-direction: column;
                    gap: 10px;
                }
    
                #VGL-popup-window-main label {
                    display: inline;
                }
    
                #VGL-popup-window-main input {
                    height: 25px;
                    width: 150px;
                    border: 1px solid #ccc;
                }
    
                #VGL-confirm-button {
                    width: 60px;
                    height: 30px;
                    background-color: #409EFF;
                    color: #fff;
                    font-size: 14px;
                    font-weight: 500;
                    border: none;
                    cursor: pointer;
                    border-radius: 10px;
                }
            `;
            this.styleElement = document.createElement('style');
            this.styleElement.textContent = injectedCSS;
            this.styleElement.setAttribute('vgl-style', '');
            document.head.appendChild(this.styleElement);
        }

        confirmButtonClick = (event) => {
            this.saveSettings();

            window.navigator.geolocation.getCurrentPosition = (successCallback, errorCallback, options) => {
                const fakePosition = {
                    coords: {
                        accuracy: parseFloat(this.accuracyInputField.value),
                        altitude: null,
                        altitudeAccuracy: null,
                        latitude: parseFloat(this.latitudeInputField.value),
                        longitude: parseFloat(this.longitudeInputField.value),
                        heading: null,
                        speed: null,
                    },
                    timestamp: Date.now(),
                }
                if (successCallback) {
                    successCallback(fakePosition);
                }
            }

            alert("已设定虚拟地理位置");
        };

        handleDragStart = (event) => {
            const clientX = event.clientX || event.touches[0].clientX;
            const clientY = event.clientY || event.touches[0].clientY;
            const rect = this.windowElement.getBoundingClientRect();
            this.offsetX = clientX - rect.left;
            this.offsetY = clientY - rect.top;
            this.isDragging = true;
            event.preventDefault();
        };

        handleDragMove = (event) => {
            if (!this.isDragging) return;

            const clientX = event.clientX || event.touches[0].clientX;
            const clientY = event.clientY || event.touches[0].clientY;
            const windowHeight = window.innerHeight;
            const elementHeight = this.windowElement.offsetHeight;

            const newX = clientX - this.offsetX;
            const newTop = clientY - this.offsetY;
            const newBottom = windowHeight - newTop - elementHeight;

            this.windowElement.style.left = `${newX}px`;
            this.windowElement.style.bottom = `${newBottom}px`;
            event.preventDefault();
        };

        handleDragEnd = (event) => {
            this.isDragging = false;
        };

        bindEvents() {
            this.confirmButtonElement.addEventListener("click", this.confirmButtonClick);
            // 电脑端拖动
            this.headerElement.addEventListener("mousedown", this.handleDragStart);
            document.addEventListener("mousemove", this.handleDragMove);
            document.addEventListener("mouseup", this.handleDragEnd);
            // 手机端拖动
            this.headerElement.addEventListener("touchstart", this.handleDragStart);
            document.addEventListener("touchmove", this.handleDragMove);
            document.addEventListener("touchend", this.handleDragEnd);
        }

        unbindEvents() {
            this.confirmButtonElement.removeEventListener("click", this.confirmButtonClick);
            this.headerElement.removeEventListener("mousedown", this.handleDragStart);
            document.removeEventListener("mousemove", this.handleDragMove);
            document.removeEventListener("mouseup", this.handleDragEnd);
            this.headerElement.removeEventListener("touchstart", this.handleDragStart);
            document.removeEventListener("touchmove", this.handleDragMove);
            document.removeEventListener("touchend", this.handleDragEnd);
        }

        initializeInputFields() {
            this.accuracyInputField.value = GM_getValue("VGL-accuracy", 20000)
            this.latitudeInputField.value = GM_getValue("VGL-latitude", 0)
            this.longitudeInputField.value = GM_getValue("VGL-longitude", 0)
        }

        saveSettings() {
            GM_setValue("VGL-accuracy", this.accuracyInputField.value);
            GM_setValue("VGL-latitude", this.latitudeInputField.value);
            GM_setValue("VGL-longitude", this.longitudeInputField.value);
        }

        removePopupWindow() {
            if (this.windowElement !== null) {
                this.windowElement.remove();
                this.windowElement = null;
            }
        }

        removePopupWindowCSS() {
            if (this.styleElement !== null) {
                this.styleElement.remove();
                this.styleElement = null;
            }
        }

        cleanup() {
            this.unbindEvents();
            this.removePopupWindow();
            this.removePopupWindowCSS();
            navigator.geolocation.getCurrentPosition = popupWindow.originalGetCurrentPosition
        }
    }

    let popupWindow = null;

    GM_registerMenuCommand("启动虚拟地理位置", () => {
        if (popupWindow === null) {
            popupWindow = new VirtualLocationManager();
        }
        // 防止重复注入
        else if (popupWindow.windowElement !== null) {
            alert("已启动虚拟地理位置,请勿重复启用");
            return;
        }
        popupWindow.createPopupWindow();
        popupWindow.injectPopupWindowCSS();
        popupWindow.initializeInputFields();
        popupWindow.bindEvents();
    }, "h");

    GM_registerMenuCommand("恢复原始状态", () => {
        if (popupWindow === null) {
            return;
        }
        popupWindow.cleanup();
    }, "r");
})();