import {
    _decorator, Camera, Component, EventMouse, EventTouch, Input, input, Vec2, view,
} from 'cc';
import { CAMERA_ORTHO_HALF, DESIGN_WIDTH } from '../core/GridConstants';
import { getEmbeddedOrthoHalf } from '../core/EmbeddedView';

const { ccclass } = _decorator;

/** 对齐 Unity ViewController：Orthographic 缩放与拖拽 */
@ccclass('ViewController')
export class ViewController extends Component {
    static instance: ViewController | null = null;

    /** Unity zoomSpeed=2 → 世界半高步进 200 */
    zoomSpeed = 200;
    /** Unity minZoom=3, maxZoom=10（×100 世界单位） */
    minOrtho = 300;
    maxOrtho = 1000;

    private camera: Camera | null = null;
    private dragOrigin = new Vec2();
    private dragging = false;

    onLoad() {
        if (ViewController.instance && ViewController.instance !== this) {
            this.destroy();
            return;
        }
        ViewController.instance = this;
        this.camera = this.getComponent(Camera);
        if (this.camera && this.camera.orthoHeight <= 0) {
            this.camera.orthoHeight = CAMERA_ORTHO_HALF;
        }
        input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
        input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
        input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
        input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
        input.on(Input.EventType.MOUSE_DOWN, this.onMouseDown, this);
        input.on(Input.EventType.MOUSE_MOVE, this.onMouseMove, this);
        input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);
    }

    onDestroy() {
        if (ViewController.instance === this) ViewController.instance = null;
        input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
        input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
        input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
        input.off(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
        input.off(Input.EventType.MOUSE_DOWN, this.onMouseDown, this);
        input.off(Input.EventType.MOUSE_MOVE, this.onMouseMove, this);
        input.off(Input.EventType.MOUSE_UP, this.onMouseUp, this);
    }

    zoomIn() {
        const cam = this.camera;
        if (!cam || cam.orthoHeight <= this.minOrtho) return;
        cam.orthoHeight = Math.max(this.minOrtho, cam.orthoHeight - this.zoomSpeed);
    }

    zoomOut() {
        const cam = this.camera;
        if (!cam || cam.orthoHeight >= this.maxOrtho) return;
        cam.orthoHeight = Math.min(this.maxOrtho, cam.orthoHeight + this.zoomSpeed);
    }

    resetZoom() {
        const cam = this.camera;
        if (!cam) return;
        cam.orthoHeight = getEmbeddedOrthoHalf();
        cam.node.setPosition(0, 0, cam.node.position.z);
    }

    private onTouchStart(e: EventTouch) {
        if (this.isPointerOnUI(e.getLocation())) return;
        this.dragging = true;
        e.getLocation(this.dragOrigin);
    }

    private onTouchEnd() {
        this.dragging = false;
    }

    private onTouchMove(e: EventTouch) {
        if (!this.dragging) return;
        const cur = new Vec2();
        e.getLocation(cur);
        this.applyDrag(cur);
        e.getLocation(this.dragOrigin);
    }

    private onMouseDown(e: EventMouse) {
        if (this.isPointerOnUI(new Vec2(e.getLocationX(), e.getLocationY()))) return;
        this.dragging = true;
        this.dragOrigin.set(e.getLocationX(), e.getLocationY());
    }

    private onMouseUp() {
        this.dragging = false;
    }

    private onMouseMove(e: EventMouse) {
        if (!this.dragging) return;
        this.applyDrag(new Vec2(e.getLocationX(), e.getLocationY()));
        this.dragOrigin.set(e.getLocationX(), e.getLocationY());
    }

    private applyDrag(cur: Vec2) {
        if (!this.camera) return;
        const delta = cur.subtract(this.dragOrigin);

        const ortho = this.camera.orthoHeight;
        const { width, height } = view.getVisibleSize();
        const worldPerPixelX = (2 * ortho * (width / height)) / width;
        const worldPerPixelY = (2 * ortho) / height;

        const pos = this.camera.node.position;
        let nx = pos.x - delta.x * worldPerPixelX;
        let ny = pos.y - delta.y * worldPerPixelY;

        const limit = ortho - 20;
        if (Math.abs(nx) > limit) nx = pos.x;
        if (Math.abs(ny) > limit) ny = pos.y;

        this.camera.node.setPosition(nx, ny, pos.z);
    }

    /** 右侧 UIMain 区域不拖拽镜头（与 UIMain 边距一致） */
    private isPointerOnUI(loc: Vec2): boolean {
        const vis = view.getVisibleSize();
        const margin = Math.max(96, (DESIGN_WIDTH * 0.5) * 0.14);
        const right = vis.width > DESIGN_WIDTH
            ? DESIGN_WIDTH * 0.5
            : vis.width * 0.5;
        return loc.x >= right - margin;
    }
}
