import PhotoSwipeLightbox from 'https://dpangzi.com/library/photoswipe/photoswipe.esm.min.js';
import PhotoSwipe from 'https://dpangzi.com/library/photoswipe/photoswipe.esm.min.js';

export class Gallery {
    /**
     * @param {NodeListOf<Element>|HTMLImageElement[]} images 
     * @param {Object} [options]
     * @param {boolean} [options.preloadAll=false]
     * @param {number} [options.preloadNeighbors=1]
     * @param {string|number} [options.galleryId=1]
     */
    constructor(images, options = {}) {
        this.images = Array.from(images);
        this.items = [];
        this.options = Object.assign({
            preloadAll: false,
            preloadNeighbors: 1,
            galleryId: 1
        }, options);
        
        this.lightbox = null;
        this._isClosedByNavigation = false;
        this._isClosing = false;
        this._clickHandlers = new Map(); // 存储事件处理器,用于清理

        this.init();
        this.checkHash();
        window.addEventListener('hashchange', () => this.handleHashChange());
    }

    init() {
        // 清理旧的事件监听器(如果存在)
        this.cleanup();
        
        this.images.forEach((img, index) => {
            // Prevent duplicate binding
            if (img.dataset.pswpBound) {
                // 如果已经绑定过,先移除旧的标记和事件监听器
                delete img.dataset.pswpBound;
                // 移除可能存在的旧事件监听器
                const oldHandler = this._clickHandlers.get(img);
                if (oldHandler) {
                    img.removeEventListener('click', oldHandler);
                    this._clickHandlers.delete(img);
                }
            }
            img.dataset.pswpBound = "true";

            // Determine image sources
            // Priority: data-origin > data-src > src
            const originalSrc = this.getImageSrc(img);
            
            // Item configuration
            const item = {
                src: originalSrc,
                w: 0,
                h: 0,
                element: img,
                index: index,
                loading: false
            };
            
            this.items.push(item);

            // Bind click event and store handler for cleanup
            const clickHandler = (e) => this.handleClick(e, index);
            this._clickHandlers.set(img, clickHandler);
            img.addEventListener('click', clickHandler);
            img.style.cursor = 'zoom-in';

            if (this.options.preloadAll) {
                this.preloadImage(item);
            }
        });
    }
    
    /**
     * 清理事件监听器和相关资源
     */
    cleanup() {
        // 移除所有存储的事件监听器
        this._clickHandlers.forEach((handler, img) => {
            img.removeEventListener('click', handler);
            delete img.dataset.pswpBound;
        });
        this._clickHandlers.clear();
        
        // 关闭可能打开的 lightbox
        if (this.lightbox && this.lightbox.pswp) {
            this.lightbox.close();
        }
        
        // 重置状态
        this.items = [];
        this.lightbox = null;
        this._isClosedByNavigation = false;
        this._isClosing = false;
    }
    

    /**
     * Get the highest priority image source
     * @param {HTMLImageElement} img 
     * @returns {string}
     */
    getImageSrc(img) {
        if (img.dataset.origin && img.dataset.origin.trim() !== '') {
            return img.dataset.origin;
        }
        if (img.dataset.src && img.dataset.src.trim() !== '') {
            return img.dataset.src;
        }
        return img.src;
    }

    preloadImage(item) {
        if (item.w > 0 && item.h > 0) return;
        if (item.loading) return;

        item.loading = true;
        const preloadImg = new Image();
        preloadImg.src = item.src;
        preloadImg.onload = () => {
            item.w = preloadImg.naturalWidth;
            item.h = preloadImg.naturalHeight;
            item.loading = false;
        };
        preloadImg.onerror = () => {
            item.loading = false;
        };
    }

    preloadNeighbors(currentIndex) {
        const count = this.options.preloadNeighbors;
        
        // Always preload current
        this.preloadImage(this.items[currentIndex]);

        // Preload neighbors
        for (let i = 1; i <= count; i++) {
            const nextIndex = currentIndex + i;
            const prevIndex = currentIndex - i;

            if (nextIndex < this.items.length) {
                this.preloadImage(this.items[nextIndex]);
            }
            if (prevIndex >= 0) {
                this.preloadImage(this.items[prevIndex]);
            }
        }
    }

    handleClick(e, index) {
        e.preventDefault();

        const item = this.items[index];
        if (item.w > 0 && item.h > 0) {
            this.open(index);
            return;
        }

        const img = new Image();
        img.src = item.src;
        img.onload = () => {
            item.w = img.naturalWidth;
            item.h = img.naturalHeight;
            this.open(index);
        };
        img.onerror = () => {
            this.open(index);
        };
    }

    open(index, fromHash = false) {
        // If already open, just update index
        if (this.lightbox && this.lightbox.pswp) {
            this.lightbox.pswp.goTo(index);
            return;
        }

        this._isClosing = false;

        const lightbox = new PhotoSwipeLightbox({
            dataSource: this.items,
            pswpModule: PhotoSwipe,
            index: index,
            bgOpacity: 0.9,
            showHideAnimationType: 'zoom',
            errorMsg: '<div class="pswp__error-msg">图片加载失败</div>'
        });

        this.lightbox = lightbox;

        // Preload based on config
        if (!this.options.preloadAll) {
            this.preloadNeighbors(index);
            
            lightbox.on('change', () => {
                const newIndex = lightbox.currIndex;
                this.preloadNeighbors(newIndex);
                this.updateHash(newIndex);
            });
        } else {
             lightbox.on('change', () => {
                this.updateHash(lightbox.currIndex);
            });
        }

        // Handle dimensions if not yet preloaded
        lightbox.on('contentLoad', (e) => {
            const { content } = e;
            
            // If dimensions are already known (from preload), let it proceed
            if (content.data.w > 0 && content.data.h > 0) {
                return;
            }

            // Do NOT prevent default here. 
            // We want PhotoSwipe to proceed with creating the slide elements,
            // even if dimensions are temporarily 0.
            // e.preventDefault(); 
            
            const img = new Image();
            img.src = content.data.src;
            
            img.onload = () => {
                content.data.w = img.naturalWidth;
                content.data.h = img.naturalHeight;
                
                // Update item for future use
                const item = lightbox.options.dataSource[content.index];
                if (item) {
                    item.w = img.naturalWidth;
                    item.h = img.naturalHeight;
                    item.loading = false;
                }
            };
            
            img.onerror = () => {
                console.error('Failed to load image:', content.data.src);
            };
        });

        lightbox.on('close', () => {
            this._isClosing = true;
            if (!this._isClosedByNavigation && window.location.hash.includes(`gid=${this.options.galleryId}`)) {
                window.history.back();
            }
            this.lightbox = null;
            this._isClosedByNavigation = false;
        });

        // If not opened from hash, push the initial state
        if (!fromHash) {
            this.pushHash(index);
        }

        lightbox.init();
    }

    // Hash Helper Methods
    
    parseHash() {
        const hash = window.location.hash.substring(1);
        const params = {};
        if (hash.length < 5) return params;

        const vars = hash.split('&');
        for (let i = 0; i < vars.length; i++) {
            if (!vars[i]) continue;
            const pair = vars[i].split('=');
            if (pair.length < 2) continue;
            params[pair[0]] = pair[1];
        }

        if (params.gid) params.gid = parseInt(params.gid, 10);
        if (params.pid) params.pid = parseInt(params.pid, 10);
        return params;
    }

    checkHash() {
        const params = this.parseHash();
        if (params.gid === this.options.galleryId && params.pid > 0 && params.pid <= this.items.length) {
            this.open(params.pid - 1, true);
        }
    }

    handleHashChange() {
        const params = this.parseHash();
        
        // If hash matches this gallery
        if (params.gid === this.options.galleryId && params.pid > 0 && params.pid <= this.items.length) {
            this.open(params.pid - 1, true);
        } else {
            // Hash doesn't match or is empty
            if (this.lightbox && !this._isClosing) {
                this._isClosedByNavigation = true;
                this.lightbox.close();
            }
        }
    }

    pushHash(index) {
        const hash = `&gid=${this.options.galleryId}&pid=${index + 1}`;
        window.history.pushState(null, '', `#${hash}`);
    }

    updateHash(index) {
        const hash = `&gid=${this.options.galleryId}&pid=${index + 1}`;
        window.history.replaceState(null, '', `#${hash}`);
    }
}
评论加载中...