/**
 * CardSection — collapsible section with name filtering and incremental
 * card reconciliation for card grids.
 *
 * Usage:
 *   const section = new CardSection('led-devices', {
 *       titleKey: 'targets.section.devices',
 *       gridClass: 'devices-grid',
 *       addCardOnclick: "showAddDevice()",
 *       keyAttr: 'data-device-id',
 *   });
 *
 *   const items = devices.map(d => ({ key: d.id, html: createDeviceCard(d) }));
 *
 *   if (section.isMounted()) {
 *       section.reconcile(items);          // incremental DOM diff
 *   } else {
 *       html += section.render(items);     // initial HTML string
 *       // after container.innerHTML = html:
 *       section.bind();
 *   }
 */

import { t } from './i18n.js';

const STORAGE_KEY = 'sections_collapsed';
const ORDER_PREFIX = 'card_order_';
const DRAG_THRESHOLD = 5;
const SCROLL_EDGE = 60;
const SCROLL_SPEED = 12;
const _reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');

function _getCollapsedMap() {
    try { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || {}; }
    catch { return {}; }
}

export class CardSection {

    /**
     * @param {string} sectionKey  Unique key for localStorage persistence
     * @param {object} opts
     * @param {string} opts.titleKey       i18n key for the section title
     * @param {string} opts.gridClass      CSS class for the card grid: 'devices-grid' | 'templates-grid'
     * @param {string} [opts.addCardOnclick] onclick handler string for the "+" add card
     * @param {string} [opts.keyAttr]      data attribute that uniquely identifies cards (e.g. 'data-device-id')
     * @param {string} [opts.headerExtra]  Extra HTML injected between count badge and filter (e.g. action buttons)
     * @param {string} [opts.emptyKey]     i18n key for the empty-state message shown when there are no items
     */
    constructor(sectionKey, { titleKey, gridClass, addCardOnclick, keyAttr, headerExtra, collapsible, emptyKey }) {
        this.sectionKey = sectionKey;
        this.titleKey = titleKey;
        this.gridClass = gridClass;
        this.addCardOnclick = addCardOnclick || '';
        this.keyAttr = keyAttr || '';
        this.headerExtra = headerExtra || '';
        this.collapsible = !!collapsible;
        this.emptyKey = emptyKey || '';
        this._filterValue = '';
        this._lastItems = null;
        this._dragState = null;
        this._dragBound = false;
        this._cachedCardRects = null;
    }

    /** True if this section's DOM element exists (i.e. not the first render). */
    isMounted() {
        return !!document.querySelector(`[data-card-section="${this.sectionKey}"]`);
    }

    /**
     * Returns section HTML string for initial innerHTML building.
     * @param {Array<{key: string, html: string}>} items
     */
    render(items) {
        this._lastItems = items;
        this._dragBound = false; // DOM will be recreated → need to re-init drag
        const count = items.length;
        const cardsHtml = items.map(i => i.html).join('');

        const isCollapsed = this.collapsible && !!_getCollapsedMap()[this.sectionKey];
        const chevronStyle = isCollapsed ? '' : ' style="transform:rotate(90deg)"';
        const contentDisplay = isCollapsed ? ' style="display:none"' : '';
        const collapsedClass = isCollapsed ? ' cs-collapsed' : '';

        const addCard = this.addCardOnclick
            ? `<div class="template-card add-template-card cs-add-card" data-cs-add="${this.sectionKey}" onclick="${this.addCardOnclick}"><div class="add-template-icon">+</div></div>`
            : '';

        const emptyState = (count === 0 && this.emptyKey)
            ? `<div class="cs-empty-state" data-cs-empty="${this.sectionKey}"><span class="cs-empty-text text-muted">${t(this.emptyKey)}</span></div>`
            : '';

        return `
        <div class="subtab-section${collapsedClass}" data-card-section="${this.sectionKey}">
            <div class="subtab-section-header cs-header" data-cs-toggle="${this.sectionKey}">
                ${this.collapsible ? `<span class="cs-chevron"${chevronStyle}>&#9654;</span>` : ''}
                <span class="cs-title" data-i18n="${this.titleKey}">${t(this.titleKey)}</span>
                <span class="cs-count">${count}</span>
                ${this.headerExtra ? `<span class="cs-header-extra">${this.headerExtra}</span>` : ''}
                <div class="cs-filter-wrap">
                    <input type="text" class="cs-filter" data-cs-filter="${this.sectionKey}"
                           data-i18n-placeholder="section.filter.placeholder" placeholder="${t('section.filter.placeholder')}" autocomplete="off">
                    <button type="button" class="cs-filter-reset" data-cs-filter-reset="${this.sectionKey}" data-i18n-title="section.filter.reset" title="${t('section.filter.reset')}">×</button>
                </div>
            </div>
            <div class="cs-content ${this.gridClass}" data-cs-content="${this.sectionKey}"${contentDisplay}>
                ${emptyState}${cardsHtml}
                ${addCard}
            </div>
        </div>`;
    }

    /** Attach event listeners after innerHTML is set. */
    bind() {
        const header = document.querySelector(`[data-cs-toggle="${this.sectionKey}"]`);
        const content = document.querySelector(`[data-cs-content="${this.sectionKey}"]`);
        const filterInput = document.querySelector(`[data-cs-filter="${this.sectionKey}"]`);
        if (!header || !content) return;

        if (this.collapsible) {
            header.addEventListener('mousedown', (e) => {
                if (e.target.closest('.cs-filter-wrap') || e.target.closest('.cs-header-extra')) return;
                this._toggleCollapse(header, content);
            });
        }

        if (filterInput) {
            const resetBtn = document.querySelector(`[data-cs-filter-reset="${this.sectionKey}"]`);
            const updateResetVisibility = () => {
                if (resetBtn) resetBtn.style.display = filterInput.value ? '' : 'none';
            };

            filterInput.addEventListener('mousedown', (e) => e.stopPropagation());
            if (resetBtn) resetBtn.addEventListener('mousedown', (e) => e.stopPropagation());

            let timer = null;
            filterInput.addEventListener('input', () => {
                clearTimeout(timer);
                updateResetVisibility();
                timer = setTimeout(() => {
                    this._filterValue = filterInput.value.trim();
                    this._applyFilter(content, this._filterValue);
                }, 150);
            });
            filterInput.addEventListener('keydown', (e) => {
                if (e.key === 'Escape') {
                    if (filterInput.value) {
                        e.stopPropagation();
                        filterInput.value = '';
                        this._filterValue = '';
                        this._applyFilter(content, '');
                        updateResetVisibility();
                    }
                }
            });

            if (resetBtn) {
                resetBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    filterInput.value = '';
                    this._filterValue = '';
                    this._applyFilter(content, '');
                    updateResetVisibility();
                    filterInput.focus();
                });
            }

            // Restore filter from before re-render
            if (this._filterValue) {
                filterInput.value = this._filterValue;
                this._applyFilter(content, this._filterValue);
            }
            updateResetVisibility();
        }

        // Tag card elements with their source HTML for future reconciliation
        this._tagCards(content);

        // Inject drag handles and initialize drag-and-drop
        if (this.keyAttr) {
            this._injectDragHandles(content);
            this._initDrag(content);
        }

        // Stagger card entrance animation
        this._animateEntrance(content);

        // Cache searchable text on cards for faster filtering
        this._cacheSearchText(content);
    }

    /**
     * Incremental DOM diff — update cards in-place without rebuilding the section.
     * @param {Array<{key: string, html: string}>} items
     * @returns {{added: Set<string>, replaced: Set<string>, removed: Set<string>}}
     */
    reconcile(items) {
        // Skip DOM mutations while a drag is in progress — would destroy drag state
        if (this._dragState) {
            this._pendingReconcile = items;
            return { added: new Set(), replaced: new Set(), removed: new Set() };
        }

        const content = document.querySelector(`[data-cs-content="${this.sectionKey}"]`);
        if (!content) return { added: new Set(), replaced: new Set(), removed: new Set() };

        this._lastItems = items;

        // Update count badge (will be refined by _applyFilter if a filter is active)
        const countEl = document.querySelector(`[data-cs-toggle="${this.sectionKey}"] .cs-count`);
        if (countEl && !this._filterValue) countEl.textContent = items.length;

        // Show/hide empty state
        if (this.emptyKey) {
            let emptyEl = content.querySelector(`[data-cs-empty="${this.sectionKey}"]`);
            if (items.length === 0) {
                if (!emptyEl) {
                    emptyEl = document.createElement('div');
                    emptyEl.className = 'cs-empty-state';
                    emptyEl.setAttribute('data-cs-empty', this.sectionKey);
                    emptyEl.innerHTML = `<span class="cs-empty-text text-muted">${t(this.emptyKey)}</span>`;
                    const addCard = content.querySelector('.cs-add-card');
                    if (addCard) content.insertBefore(emptyEl, addCard);
                    else content.appendChild(emptyEl);
                }
                emptyEl.style.display = '';
            } else if (emptyEl) {
                emptyEl.style.display = 'none';
            }
        }

        const newMap = new Map(items.map(i => [i.key, i.html]));
        const addCard = content.querySelector('.cs-add-card');
        const added = new Set();
        const replaced = new Set();
        const removed = new Set();

        // Process existing cards: remove or update
        if (this.keyAttr) {
            const existing = [...content.querySelectorAll(`[${this.keyAttr}]`)];
            for (const card of existing) {
                const key = card.getAttribute(this.keyAttr);
                if (!newMap.has(key)) {
                    card.remove();
                    removed.add(key);
                } else {
                    const newHtml = newMap.get(key);
                    if (card._csHtml !== newHtml) {
                        const tmp = document.createElement('div');
                        tmp.innerHTML = newHtml;
                        const newEl = tmp.firstElementChild;
                        newEl._csHtml = newHtml;
                        card.replaceWith(newEl);
                        replaced.add(key);
                    }
                    // else: unchanged — skip
                }
            }

            // Insert new cards (keys not already in DOM)
            const existingKeys = new Set([...content.querySelectorAll(`[${this.keyAttr}]`)].map(
                el => el.getAttribute(this.keyAttr)
            ));
            for (const { key, html } of items) {
                if (!existingKeys.has(key)) {
                    const tmp = document.createElement('div');
                    tmp.innerHTML = html;
                    const newEl = tmp.firstElementChild;
                    newEl._csHtml = html;
                    if (addCard) content.insertBefore(newEl, addCard);
                    else content.appendChild(newEl);
                    added.add(key);
                }
            }
        }

        // Animate newly added cards
        if (added.size > 0 && this.keyAttr && !_reducedMotion.matches) {
            let delay = 0;
            for (const key of added) {
                const card = content.querySelector(`[${this.keyAttr}="${key}"]`);
                if (card) {
                    card.style.animationDelay = `${delay}ms`;
                    card.classList.add('card-enter');
                    card.addEventListener('animationend', () => card.classList.remove('card-enter'), { once: true });
                    delay += 30;
                }
            }
        }

        // Re-inject drag handles on new/replaced cards
        if (this.keyAttr && (added.size > 0 || replaced.size > 0)) {
            this._injectDragHandles(content);
        }

        // Re-cache searchable text for new/replaced cards
        if (added.size > 0 || replaced.size > 0) {
            this._cacheSearchText(content);
        }

        // Re-apply filter
        if (this._filterValue) {
            this._applyFilter(content, this._filterValue);
        }

        return { added, replaced, removed };
    }

    /** Bind an array of CardSection instances. */
    static bindAll(sections) {
        for (const s of sections) s.bind();
    }

    /** Expand all given sections. */
    static expandAll(sections) {
        const map = _getCollapsedMap();
        for (const s of sections) {
            map[s.sectionKey] = false;
            const content = document.querySelector(`[data-cs-content="${s.sectionKey}"]`);
            const header = document.querySelector(`[data-cs-toggle="${s.sectionKey}"]`);
            const section = document.querySelector(`[data-card-section="${s.sectionKey}"]`);
            if (content) content.style.display = '';
            if (section) section.classList.remove('cs-collapsed');
            if (header) {
                const chevron = header.querySelector('.cs-chevron');
                if (chevron) chevron.style.transform = 'rotate(90deg)';
            }
        }
        localStorage.setItem(STORAGE_KEY, JSON.stringify(map));
    }

    /** Collapse all given sections. */
    static collapseAll(sections) {
        const map = _getCollapsedMap();
        for (const s of sections) {
            map[s.sectionKey] = true;
            const content = document.querySelector(`[data-cs-content="${s.sectionKey}"]`);
            const header = document.querySelector(`[data-cs-toggle="${s.sectionKey}"]`);
            const section = document.querySelector(`[data-card-section="${s.sectionKey}"]`);
            if (content) content.style.display = 'none';
            if (section) section.classList.add('cs-collapsed');
            if (header) {
                const chevron = header.querySelector('.cs-chevron');
                if (chevron) chevron.style.transform = '';
            }
        }
        localStorage.setItem(STORAGE_KEY, JSON.stringify(map));
    }

    /** Programmatically expand this section if collapsed. */
    expand() {
        const header = document.querySelector(`[data-cs-toggle="${this.sectionKey}"]`);
        const content = document.querySelector(`[data-cs-content="${this.sectionKey}"]`);
        if (!header || !content) return;
        const map = _getCollapsedMap();
        if (map[this.sectionKey]) {
            map[this.sectionKey] = false;
            localStorage.setItem(STORAGE_KEY, JSON.stringify(map));
            content.style.display = '';
            const section = header.closest('[data-card-section]');
            if (section) section.classList.remove('cs-collapsed');
            const chevron = header.querySelector('.cs-chevron');
            if (chevron) chevron.style.transform = 'rotate(90deg)';
        }
    }

    /**
     * Reorder items array according to saved drag order.
     * Call before render() / reconcile().
     */
    applySortOrder(items) {
        if (!this.keyAttr) return items;
        const order = this._getSavedOrder();
        if (!order.length) return items;
        const orderMap = new Map(order.map((key, idx) => [key, idx]));
        const sorted = [...items];
        sorted.sort((a, b) => {
            const ia = orderMap.has(a.key) ? orderMap.get(a.key) : Infinity;
            const ib = orderMap.has(b.key) ? orderMap.get(b.key) : Infinity;
            if (ia !== ib) return ia - ib;
            return 0; // preserve original order for unranked items
        });
        return sorted;
    }

    _getSavedOrder() {
        try { return JSON.parse(localStorage.getItem(ORDER_PREFIX + this.sectionKey)) || []; }
        catch { return []; }
    }

    _saveOrder(keys) {
        localStorage.setItem(ORDER_PREFIX + this.sectionKey, JSON.stringify(keys));
    }

    // ── private ──

    _animateEntrance(content) {
        if (this._animated) return;
        this._animated = true;
        if (_reducedMotion.matches) return;
        const selector = this.keyAttr ? `[${this.keyAttr}]` : '.card, .template-card:not(.add-template-card)';
        const cards = content.querySelectorAll(selector);
        cards.forEach((card, i) => {
            card.style.animationDelay = `${i * 30}ms`;
            card.classList.add('card-enter');
            card.addEventListener('animationend', () => card.classList.remove('card-enter'), { once: true });
        });
    }

    _tagCards(content) {
        if (!this.keyAttr || !this._lastItems) return;
        const htmlMap = new Map(this._lastItems.map(i => [i.key, i.html]));
        const cards = content.querySelectorAll(`[${this.keyAttr}]`);
        cards.forEach(card => {
            const key = card.getAttribute(this.keyAttr);
            if (htmlMap.has(key)) card._csHtml = htmlMap.get(key);
        });
    }

    /** Cache each card's lowercased text content in data-search for fast filtering. */
    _cacheSearchText(content) {
        const cards = content.querySelectorAll('.card, .template-card:not(.add-template-card)');
        cards.forEach(card => {
            card.dataset.search = card.textContent.toLowerCase();
        });
    }

    _toggleCollapse(header, content) {
        const map = _getCollapsedMap();
        const nowCollapsed = !map[this.sectionKey];
        map[this.sectionKey] = nowCollapsed;
        localStorage.setItem(STORAGE_KEY, JSON.stringify(map));

        const section = header.closest('[data-card-section]');
        if (section) section.classList.toggle('cs-collapsed', nowCollapsed);

        const chevron = header.querySelector('.cs-chevron');
        if (chevron) chevron.style.transform = nowCollapsed ? '' : 'rotate(90deg)';

        // Cancel any running animation on this content
        if (content._csAnim) { content._csAnim.cancel(); content._csAnim = null; }

        // Skip animation when user prefers reduced motion
        if (_reducedMotion.matches) {
            content.style.display = nowCollapsed ? 'none' : '';
            return;
        }

        if (nowCollapsed) {
            const h = content.offsetHeight;
            content.style.overflow = 'hidden';
            const anim = content.animate(
                [{ height: h + 'px', opacity: 1 }, { height: '0px', opacity: 0 }],
                { duration: 200, easing: 'ease-in-out' }
            );
            content._csAnim = anim;
            anim.onfinish = () => {
                content.style.display = 'none';
                content.style.overflow = '';
                content._csAnim = null;
            };
        } else {
            // Intentional forced layout: reading scrollHeight after setting display=''
            // is required to measure the target height for the expand animation.
            content.style.display = '';
            content.style.overflow = 'hidden';
            const h = content.scrollHeight;
            const anim = content.animate(
                [{ height: '0px', opacity: 0 }, { height: h + 'px', opacity: 1 }],
                { duration: 200, easing: 'ease-in-out' }
            );
            content._csAnim = anim;
            anim.onfinish = () => {
                content.style.overflow = '';
                content._csAnim = null;
            };
        }
    }

    _applyFilter(content, query) {
        const cards = content.querySelectorAll('.card, .template-card:not(.add-template-card)');
        const addCard = content.querySelector('.cs-add-card');
        const countEl = document.querySelector(`[data-cs-toggle="${this.sectionKey}"] .cs-count`);
        const total = cards.length;

        if (!query) {
            content.classList.remove('cs-filtering');
            cards.forEach(card => { card.style.display = ''; });
            if (addCard) addCard.style.display = '';
            if (countEl) countEl.textContent = total;
            return;
        }
        content.classList.add('cs-filtering');

        const lower = query.toLowerCase();
        // Comma-separated segments → AND; spaces within a segment → OR
        const groups = lower.split(',').map(g => g.trim().split(/\s+/).filter(Boolean)).filter(g => g.length);
        let visible = 0;

        cards.forEach(card => {
            const text = card.dataset.search || card.textContent.toLowerCase();
            // Each group must have at least one matching term (AND of ORs)
            const match = groups.every(orTerms => orTerms.some(term => text.includes(term)));
            card.style.display = match ? '' : 'none';
            if (match) visible++;
        });

        if (addCard) addCard.style.display = 'none';
        if (countEl) countEl.textContent = `${visible}/${total}`;
    }

    // ── drag-and-drop reordering ──

    _injectDragHandles(content) {
        const cards = content.querySelectorAll(`[${this.keyAttr}]`);
        cards.forEach(card => {
            if (card.querySelector('.card-drag-handle')) return;
            const handle = document.createElement('span');
            handle.className = 'card-drag-handle';
            handle.textContent = '\u2807'; // ⠇ braille dots vertical
            handle.setAttribute('aria-hidden', 'true');
            card.prepend(handle);
        });
    }

    _initDrag(content) {
        if (this._dragBound) return;
        this._dragBound = true;

        content.addEventListener('pointerdown', (e) => {
            if (this._filterValue) return;
            const handle = e.target.closest('.card-drag-handle');
            if (!handle) return;
            const card = handle.closest(`[${this.keyAttr}]`);
            if (!card) return;

            e.preventDefault();
            this._dragState = {
                card,
                content,
                startX: e.clientX,
                startY: e.clientY,
                started: false,
                clone: null,
                placeholder: null,
                scrollRaf: null,
            };

            const onMove = (ev) => this._onDragMove(ev);
            const onUp = (ev) => {
                document.removeEventListener('pointermove', onMove);
                document.removeEventListener('pointerup', onUp);
                this._onDragEnd(ev);
            };
            document.addEventListener('pointermove', onMove);
            document.addEventListener('pointerup', onUp);
        });
    }

    _onDragMove(e) {
        const ds = this._dragState;
        if (!ds) return;

        if (!ds.started) {
            const dx = e.clientX - ds.startX;
            const dy = e.clientY - ds.startY;
            if (Math.abs(dx) < DRAG_THRESHOLD && Math.abs(dy) < DRAG_THRESHOLD) return;
            this._startDrag(ds, e);
        }

        // Position clone at pointer
        ds.clone.style.left = (e.clientX - ds.offsetX) + 'px';
        ds.clone.style.top = (e.clientY - ds.offsetY) + 'px';

        // Only move placeholder when cursor enters a card's rect
        const { card: target, before } = this._getDropTarget(e.clientX, e.clientY, ds.content);
        if (!target) return; // cursor is in a gap — keep placeholder where it is
        if (target === ds.lastTarget && before === ds.lastBefore) return; // same position
        ds.lastTarget = target;
        ds.lastBefore = before;
        if (before) {
            ds.content.insertBefore(ds.placeholder, target);
        } else {
            ds.content.insertBefore(ds.placeholder, target.nextSibling);
        }

        // Auto-scroll near viewport edges
        this._autoScroll(e.clientY, ds);
    }

    _startDrag(ds, e) {
        ds.started = true;
        const rect = ds.card.getBoundingClientRect();

        // Clone for visual drag
        const clone = ds.card.cloneNode(true);
        clone.className = ds.card.className + ' card-drag-clone';
        clone.style.width = rect.width + 'px';
        clone.style.height = rect.height + 'px';
        clone.style.left = rect.left + 'px';
        clone.style.top = rect.top + 'px';
        document.body.appendChild(clone);
        ds.clone = clone;
        ds.offsetX = e.clientX - rect.left;
        ds.offsetY = e.clientY - rect.top;

        // Placeholder
        const placeholder = document.createElement('div');
        placeholder.className = 'card-drag-placeholder';
        placeholder.style.width = rect.width + 'px';
        placeholder.style.height = rect.height + 'px';
        ds.card.parentNode.insertBefore(placeholder, ds.card);
        ds.placeholder = placeholder;

        // Hide original
        ds.card.style.display = 'none';
        ds.content.classList.add('cs-dragging');
        document.body.classList.add('cs-drag-active');

        // Cache card bounding rects for the duration of the drag
        this._cachedCardRects = this._buildCardRectCache(ds.content);
    }

    _onDragEnd() {
        const ds = this._dragState;
        this._dragState = null;
        this._cachedCardRects = null;
        if (!ds || !ds.started) return;

        // Cancel auto-scroll
        if (ds.scrollRaf) cancelAnimationFrame(ds.scrollRaf);

        // Move original card to placeholder position
        ds.content.insertBefore(ds.card, ds.placeholder);
        ds.card.style.display = '';
        ds.placeholder.remove();
        ds.clone.remove();
        ds.content.classList.remove('cs-dragging');
        document.body.classList.remove('cs-drag-active');

        // Save new order from DOM
        const keys = this._readDomOrder(ds.content);
        this._saveOrder(keys);

        // Flush any reconcile that was deferred during drag
        if (this._pendingReconcile) {
            const items = this._pendingReconcile;
            this._pendingReconcile = null;
            this.reconcile(items);
        }
    }

    _buildCardRectCache(content) {
        const cards = content.querySelectorAll(`[${this.keyAttr}]`);
        const rects = [];
        for (const card of cards) {
            if (card.style.display === 'none') continue;
            rects.push({ card, rect: card.getBoundingClientRect() });
        }
        return rects;
    }

    _getDropTarget(x, y, content) {
        const rects = this._cachedCardRects || [];
        for (const { card, rect: r } of rects) {
            if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) {
                return { card, before: x < r.left + r.width / 2 };
            }
        }
        return { card: null, before: false };
    }

    _readDomOrder(content) {
        return [...content.querySelectorAll(`[${this.keyAttr}]`)]
            .map(el => el.getAttribute(this.keyAttr));
    }

    _autoScroll(clientY, ds) {
        if (ds.scrollRaf) cancelAnimationFrame(ds.scrollRaf);
        const vp = window.innerHeight;
        let speed = 0;
        if (clientY < SCROLL_EDGE) speed = -SCROLL_SPEED * (1 - clientY / SCROLL_EDGE);
        else if (clientY > vp - SCROLL_EDGE) speed = SCROLL_SPEED * (1 - (vp - clientY) / SCROLL_EDGE);

        if (Math.abs(speed) < 0.5) return;
        const scroll = () => {
            window.scrollBy(0, speed);
            ds.scrollRaf = requestAnimationFrame(scroll);
        };
        ds.scrollRaf = requestAnimationFrame(scroll);
    }
}
