// Add CSS link to the document if not already present
if (!document.querySelector('link[href*="rendering-canvas.css"]')) {
    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = './disp/rendering-canvas.css';
    document.head.appendChild(link);
}

export default {
    name: 'RenderingCanvas',
    template: `
        <div class="canvas-container">
            <canvas 
                ref="renderCanvas" 
                :id="canvasId"
                tabindex="0"
                @contextmenu.prevent
                @mousedown="handleMouseDown"
                @mousemove="handleMouseMove"
                @mouseup="handleMouseUp"
                @wheel.prevent="handleWheel"
                @keydown="handleKeyDown"
                @keyup="handleKeyUp"
                @touchstart.prevent="handleTouchStart"
                @touchmove.prevent="handleTouchMove"
                @touchend.prevent="handleTouchEnd"
                @click="focusCanvas"
                @focus="onFocus"
                @blur="onBlur"
            ></canvas>
        </div>
    `,
    props: {
        hubUrl: {
            type: String,
            default: '/renderingHub'
        },
        autoConnect: {
            type: Boolean,
            default: true
        },

        width: {
            type: Number,
            default: 800
        },
        height: {
            type: Number,
            default: 600
        }
    },
    data() {
        return {
            canvasId: `rendering-canvas-${Math.random().toString(36).substr(2, 9)}`,
            connection: null,
            sessionId: null,
            isConnected: false,
            isInitialized: false,
            hasFocus: false,
            mouseState: {
                isDown: false,
                button: 0,
                lastX: 0,
                lastY: 0
            },
            compressionSupported: typeof DecompressionStream !== 'undefined',
            isFirefox: navigator.userAgent.toLowerCase().indexOf('firefox') > -1,
            resizeObserver: null
        }
    },
    async mounted() {
        await this.setupCanvas();
        this.setupResizeObserver();
        
        if (this.autoConnect) {
            await this.connect();
        }
        
        // Add visibility change listener
        this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
        document.addEventListener('visibilitychange', this.handleVisibilityChange);
    },
    async beforeUnmount() {
        // Remove visibility change listener
        document.removeEventListener('visibilitychange', this.handleVisibilityChange);
        
        if (this.resizeObserver) {
            this.resizeObserver.disconnect();
        }
        
        if (this.connection) {
            await this.disconnect();
        }
    },
    watch: {
        // No longer needed since we manage connection internally
    },
    methods: {
        async connect() {
            try {
                // Create SignalR connection
                this.connection = new signalR.HubConnectionBuilder()
                    .withUrl(this.hubUrl)
                    .configureLogging(signalR.LogLevel.Information)
                    .build();
                
                // Set up event handlers
                this.connection.on("ImageUpdate", (compressedData, originalLength, width, height) => {
                    this.handleImageUpdate(compressedData, originalLength, width, height);
                });
                
                this.connection.on("CanvasInitialized", (id) => {
                    this.sessionId = id;
                    this.$emit('serverInitialized', id);
                });
                
                // Start connection
                await this.connection.start();
                this.isConnected = true;
                this.$emit('connected');
                
                // Initialize server canvas
                const canvas = this.$refs.renderCanvas;
                const rect = canvas.getBoundingClientRect();
                const width = Math.floor(rect.width);
                const height = Math.floor(rect.height);
                
                await this.connection.invoke("InitializeCanvas", width, height);
                
            } catch (err) {
                this.$emit('error', `Connection failed: ${err}`);
                console.error('Connection failed:', err);
                
                // Retry after 5 seconds
                setTimeout(() => this.connect(), 5000);
            }
        },
        
        async disconnect() {
            if (this.connection) {
                try {
                    await this.connection.stop();
                    this.connection = null;
                    this.sessionId = null;
                    this.isConnected = false;
                    
                    // Clear canvas
                    const canvas = this.$refs.renderCanvas;
                    const ctx = canvas.getContext('2d');
                    ctx.clearRect(0, 0, canvas.width, canvas.height);
                    
                    this.$emit('disconnected');
                } catch (err) {
                    console.error('Disconnect failed:', err);
                }
            }
        },
        

        async setupCanvas() {
            const canvas = this.$refs.renderCanvas;
            canvas.width = this.width;
            canvas.height = this.height;
            
            this.isInitialized = true;
            this.$emit('initialized', this.canvasId);
        },
        
        setupResizeObserver() {
            this.resizeObserver = new ResizeObserver(entries => {
                for (let entry of entries) {
                    const { width, height } = entry.contentRect;
                    this.handleResize(width, height);
                }
            });
            this.resizeObserver.observe(this.$refs.renderCanvas.parentElement);
        },
        
        async handleResize(width, height) {
            const canvas = this.$refs.renderCanvas;
            canvas.width = Math.floor(width);
            canvas.height = Math.floor(height);
            
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleResize", canvas.width, canvas.height);
                } catch (err) {
                    this.$emit('error', `Resize failed: ${err}`);
                }
            }
        },
        
        async handleVisibilityChange() {
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleVisibilityChange", document.visibilityState);
                    this.$emit('visibilityChanged', document.visibilityState);
                } catch (err) {
                    this.$emit('error', `Visibility change failed: ${err}`);
                }
            }
        },
        
        focusCanvas() {
            this.$refs.renderCanvas.focus();
        },
        
        onFocus() {
            this.hasFocus = true;
            this.$emit('focus');
        },
        
        onBlur() {
            this.hasFocus = false;
            this.$emit('blur');
        },
        
        // Mouse event handlers
        async handleMouseDown(e) {
            if (!this.isInitialized || !this.connection) return;
            
            this.mouseState.isDown = true;
            this.mouseState.button = e.button;
            
            const rect = this.$refs.renderCanvas.getBoundingClientRect();
            this.mouseState.lastX = e.clientX - rect.left;
            this.mouseState.lastY = e.clientY - rect.top;
            
            if (this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleMouseDown", 
                        this.mouseState.lastX, 
                        this.mouseState.lastY, 
                        e.button
                    );
                } catch (err) {
                    this.$emit('error', `Mouse down failed: ${err}`);
                }
            }
        },
        
        async handleMouseMove(e) {
            if (!this.isInitialized) return;
            
            const rect = this.$refs.renderCanvas.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;
            
            const buttonMask = this.mouseState.isDown ? (1 << this.mouseState.button) : 0;
            
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleMouseMove", x, y, buttonMask);
                } catch (err) {
                    this.$emit('error', `Mouse move failed: ${err}`);
                }
            }
            
            this.mouseState.lastX = x;
            this.mouseState.lastY = y;
        },
        
        async handleMouseUp(e) {
            if (!this.isInitialized) return;
            
            this.mouseState.isDown = false;
            
            const rect = this.$refs.renderCanvas.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;
            
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleMouseUp", x, y, e.button);
                } catch (err) {
                    this.$emit('error', `Mouse up failed: ${err}`);
                }
            }
        },
        
        async handleWheel(e) {
            if (!this.isInitialized) return;
            
            const rect = this.$refs.renderCanvas.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;
            
            const browserBrand = this.isFirefox ? 'firefox' : 'chrome';
            
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleMouseWheel", 
                        x, y, e.deltaX, e.deltaY, browserBrand
                    );
                } catch (err) {
                    this.$emit('error', `Mouse wheel failed: ${err}`);
                }
            }
        },
        
        // Keyboard event handlers
        async handleKeyDown(e) {
            if (!this.isInitialized) return;
            
            e.preventDefault();
            
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleKeyDown", 
                        e.key, e.code, e.ctrlKey, e.shiftKey, e.altKey
                    );
                } catch (err) {
                    this.$emit('error', `Key down failed: ${err}`);
                }
            }
        },
        
        async handleKeyUp(e) {
            if (!this.isInitialized) return;
            
            e.preventDefault();
            
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleKeyUp", 
                        e.key, e.code, e.ctrlKey, e.shiftKey, e.altKey
                    );
                } catch (err) {
                    this.$emit('error', `Key up failed: ${err}`);
                }
            }
        },
        
        // Touch event handlers
        async handleTouchStart(e) {
            if (!this.isInitialized) return;
            
            const touch = e.touches[0];
            const rect = this.$refs.renderCanvas.getBoundingClientRect();
            const x = touch.clientX - rect.left;
            const y = touch.clientY - rect.top;
            
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleTouchDown", touch.identifier, x, y);
                } catch (err) {
                    this.$emit('error', `Touch start failed: ${err}`);
                }
            }
        },
        
        async handleTouchMove(e) {
            if (!this.isInitialized) return;
            
            const touch = e.touches[0];
            const rect = this.$refs.renderCanvas.getBoundingClientRect();
            const x = touch.clientX - rect.left;
            const y = touch.clientY - rect.top;
            
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleTouchMove", touch.identifier, x, y);
                } catch (err) {
                    this.$emit('error', `Touch move failed: ${err}`);
                }
            }
        },
        
        async handleTouchEnd(e) {
            if (!this.isInitialized) return;
            
            const touch = e.changedTouches[0];
            
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke("HandleTouchUp", touch.identifier);
                } catch (err) {
                    this.$emit('error', `Touch end failed: ${err}`);
                }
            }
        },
        
        // Image update handler
        async handleImageUpdate(compressedData, originalLength, width, height) {
            try {
                let imageData;
                
                if (this.compressionSupported) {
                    const decompressed = await this.decompressData(compressedData, originalLength);
                    imageData = new Uint8ClampedArray(decompressed);
                } else {
                    imageData = new Uint8ClampedArray(compressedData);
                }
                
                const canvas = this.$refs.renderCanvas;
                const ctx = canvas.getContext('2d');
                const imgData = new ImageData(imageData, width, height);
                ctx.putImageData(imgData, 0, 0);
                
                this.$emit('imageUpdated');
                
            } catch (err) {
                this.$emit('error', `Image update failed: ${err}`);
            }
        },
        
        async decompressData(compressedData, originalLength) {
            let bytes;
            
            if (compressedData instanceof Uint8Array) {
                bytes = compressedData;
            } else if (compressedData instanceof ArrayBuffer) {
                bytes = new Uint8Array(compressedData);
            } else if (typeof compressedData === 'string') {
                const binaryString = atob(compressedData);
                bytes = new Uint8Array(binaryString.length);
                for (let i = 0; i < binaryString.length; i++) {
                    bytes[i] = binaryString.charCodeAt(i);
                }
            } else {
                throw new Error(`Unknown compressed data type: ${typeof compressedData}`);
            }
            
            const stream = new ReadableStream({
                start(controller) {
                    controller.enqueue(bytes);
                    controller.close();
                }
            });
            
            const decompressedStream = stream.pipeThrough(new DecompressionStream('gzip'));
            const reader = decompressedStream.getReader();
            const chunks = [];
            
            while (true) {
                const { done, value } = await reader.read();
                if (done) break;
                chunks.push(value);
            }
            
            const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
            const result = new Uint8Array(totalLength);
            let offset = 0;
            for (const chunk of chunks) {
                result.set(chunk, offset);
                offset += chunk.length;
            }
            
            return result;
        },
        
        // Public methods that can be called from parent
        async setView(viewType) {
            if (this.connection && this.connection.state === 'Connected') {
                try {
                    await this.connection.invoke('SetView', viewType);
                    this.$emit('viewChanged', viewType);
                } catch (err) {
                    this.$emit('error', `Set view failed: ${err}`);
                }
            }
        },
        
        // Public method to manually connect if autoConnect is false
        async manualConnect() {
            if (!this.isConnected) {
                await this.connect();
            }
        },
        
        // Public method to manually disconnect
        async manualDisconnect() {
            await this.disconnect();
        },
        
        // Getters for component state
        getConnectionState() {
            return {
                isConnected: this.isConnected,
                sessionId: this.sessionId,
                connectionState: this.connection ? this.connection.state : 'Not initialized'
            };
        },
        
        // Get session ID
        getSessionId() {
            return this.sessionId;
        }
    }
} 