import * as DrawShare from "./drawShare";
import {useAddSignal, useDispatchSignal, useRemoveSignal} from "@/hooks/useSignal";

/** ----------------- 离屏渲染中的事件处理--------------------------- **/
const mouseEventHandler = makeSendPropertiesHandler( [
    'ctrlKey',
    'metaKey',
    'shiftKey',
    'button',
    'pointerType',
    'clientX',
    'clientY',
    'pageX',
    'pageY',
] );
const wheelEventHandlerImpl = makeSendPropertiesHandler( [
    'deltaX',
    'deltaY',
] );
const keydownEventHandler = makeSendPropertiesHandler( [
    'ctrlKey',
    'metaKey',
    'shiftKey',
    'keyCode',
] );
function wheelEventHandler( event, sendFn ) {
    event.preventDefault();
    wheelEventHandlerImpl( event, sendFn );
}

function preventDefaultHandler( event ) {
    event.preventDefault();
}

function copyProperties(src, properties, dst) {
    for ( const name of properties ) {
        dst[ name ] = src[ name ];
    }
}

function makeSendPropertiesHandler( properties ) {
    return function sendProperties(event, sendFn) {
        const data = { type: event.type };
        copyProperties( event, properties, data );
        sendFn( data );
    };
}

function touchEventHandler(event, sendFn) {
    const touches: { pageX:number,pageY:number }[] = [];
    const data = { type: event.type, touches };
    for ( let i = 0; i < event.touches.length; ++ i ) {
        const touch = event.touches[ i ];
        touches.push({
            pageX: touch.pageX,
            pageY: touch.pageY,
        });
    }

    sendFn( data );
}

// 四个方向键
const orbitKeys = {
    '37': true, // left
    '38': true, // up
    '39': true, // right
    '40': true, // down
};
function filteredKeydownEventHandler(event, sendFn) {
    const { keyCode } = event;
    if (orbitKeys[keyCode]) {
        event.preventDefault();
        keydownEventHandler( event, sendFn );
    }
}
/** ----------------- 离屏渲染中的事件end --------------------------- **/

let nextProxyId = 0;
/**
 * 转发 DOM 事件给Worker中的 ElementProxyReceiver
 */
class ElementProxy {
    readonly id: number;
    private worker: Worker;
    private element: HTMLElement;
    
    constructor(element, worker, eventHandlers) {
        this.id = nextProxyId++;
        this.worker = worker;
        this.element = element;

        // 注册一个响应元素id
        worker.postMessage({
            type: 'makeProxy',
            data:{
                id: this.id,
            }
        });

        this.sendSize();

        // 添加相应事件的监听
        for (const [eventName, handler] of Object.entries(eventHandlers)) {
            element.addEventListener(eventName, (event) => {
                // @ts-ignore
                handler(event, this.sendEvent.bind(this));
            });
        }
    }

    sendEvent(data){
        this.worker.postMessage({
            type: 'event',
            data:{
                id: this.id,
                data,
            }
        });
    };

    sendSize() {
        const rect = this.element.getBoundingClientRect();
        this.sendEvent({
            type: 'size',
            left: rect.left,
            top: rect.top,
            width: this.element.clientWidth,
            height: this.element.clientHeight,
        });
    }
}

let cadDialogMoveFn;
/**
 * dxf对象的查看器类
 * @param {any} data - dxf对象
 * @param {HTMLCanvasElement} canvas - three 画布
 * @param {Number} width - 渲染画布的宽度，以像素为单位
 * @param {Number} height - 渲染画布的高度，以像素为单位
 * @constructor
 */
export class Viewer {
    private worker:Worker | undefined;
    private resizeObserver: ResizeObserver;
    private proxy: ElementProxy | undefined;
    private middleObject:any = new Proxy({},{
        set(target: {}, p: string | symbol, newValue: any): boolean {
            target[p] = newValue;

            switch (p) {
                case "markList":
                    break;
            }

            return true;
        }
    })

    constructor(data: any, canvas: HTMLCanvasElement, width: number, height: number, onComplete?:()=>void) {
        //console.log('dxf data:', data);
        canvas.style.width = `${width}px`;
        canvas.style.height = `${height}px`;

        // 检查是否支持离屏canvas
        if (canvas.transferControlToOffscreen) {
            console.log('OffscreenCanvas supported');
            // 创建Worker进行离屏渲染
            this.worker = new Worker(new URL('./offScreenCanvas.worker.ts', import.meta.url), {type: 'module'});
            const offscreen = canvas.transferControlToOffscreen();
            offscreen.width = width;
            offscreen.height = height;

            // 添加鼠标事件的监听
            const eventHandlers = {
                contextmenu: preventDefaultHandler,
                mousedown: mouseEventHandler,
                mousemove: mouseEventHandler,
                mouseup: mouseEventHandler,
                pointerdown: mouseEventHandler,
                pointermove: mouseEventHandler,
                pointerup: mouseEventHandler,
                touchstart: touchEventHandler,
                //touchmove: touchEventHandler,
                touchend: touchEventHandler,
                wheel: wheelEventHandler,
                keydown: filteredKeydownEventHandler,
            };
            this.proxy = new ElementProxy(canvas, this.worker, eventHandlers);

            this.worker.postMessage({
                type: 'start', data: {
                    canvas: offscreen,
                    canvasId: this.proxy.id,
                    data:data,
                    options: {
                        bgColor:0x000000,
                        contrastColor:0xffffff
                    }
                }
            }, [offscreen]);

            this.worker.onmessage = (event) => {
                const data = event.data;
                switch (data.type) {
                    case 'complete':
                        onComplete && onComplete();
                        break;
                    case "signal":
                        this.handleSignal(data.data)
                        break;
                    case "style":
                        canvas.style[data.data.key] = data.data.value;
                        break;
                    case "middle":
                        this.middleObject[data.data.key] = data.data.value;
                        break;
                }
            }

            cadDialogMoveFn = this.proxy.sendSize.bind(this.proxy);
            useAddSignal("cadViewerResize",cadDialogMoveFn)
        }else{
            canvas.width = width;
            canvas.height = height;

            //不支持离屏渲染
            DrawShare.main({
                canvas,
                inputElement:canvas,
                data:data,
                onComplete:onComplete,
                signal:this.handleSignal,
                options: {
                    bgColor:0x000000,
                    contrastColor:0xffffff
                },
                middleObject:this.middleObject
            });
        }

        // 监听窗口变化
        this.resizeObserver = new ResizeObserver(entries => {
            canvas.style.width = `${entries[0].contentRect.width}px`;
            canvas.style.height = `${entries[0].contentRect.height}px`;

            const data = {
                width:entries[0].contentRect.width,
                height:entries[0].contentRect.height
            }

            if(this.worker){
                this.worker.postMessage({
                    type:"resize",
                    data
                })

                this.proxy?.sendSize();
            }else{
                DrawShare.resize(data)
            }
        });
        this.resizeObserver.observe(canvas.parentElement as HTMLDivElement);
    }

    // 触发signal
    handleSignal(args){
        console.log("handleSignal",args)
        const {type,name,data} = args;
        switch (type) {
            case "dispatch":
                useDispatchSignal(name,...data)
                break;
        }
    }

    // 调用drawShare中的方法,data为传入的参数，对象展示
    callMethod(methodName:string, data:any = {}){
        if(this.worker){
            this.worker.postMessage({
                type:methodName,
                data,
            })
        }else{
            DrawShare[methodName](data);
        }
    }

    dispose() {
        if(this.worker){
            this.worker.terminate();
            useRemoveSignal("cadViewerResize",cadDialogMoveFn)
        }else{
            DrawShare.dispose();
        }

        this.resizeObserver.disconnect();
    }

    /* ------------------- 需要共同实现的标记相关方法 ------------------------- */
    // 获取选中的标记
    get selectRectIndex(){
        return this.middleObject.selectRectIndex;
    }

    // 删除选中的标记
    deleteRect(){
        const id = this.middleObject.selectRect?.id;
        if(!id) return;

        const elementId = this.middleObject.selectRect?.elementId;
        this.callMethod('callModuleMethod',{
            moduleName: "drawRect", methodName: "deleteRect",elementId:elementId
        })
    }

    // 根据模型选中对应标记
    selectRect(uuid:string){
        this.callMethod('callModuleMethod',{
            moduleName: "drawRect", methodName: "setSelect",elementId:uuid
        })
    }
}