<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>静态股票图表 (离线版)</title>
    <style>
        html, body {
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #chart-container, #tv-chart {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
<div id="chart-container">
    <div id="tv-chart"></div>
    <div class="loading-overlay" id="loading-overlay">
        <div class="spinner"></div>
    </div>
</div>

<script>
    let isFrist = true;
    let shapesDrawn = new Set();
    const symbol = "{{ symbol }}";
    const interval = "{{ interval }}";
    const chan_config = {{ chan_config | tojson }};
    const STATIC_DATA = {{ static_data | tojson }};
    const STATIC_SHAPES = {{ static_shapes | tojson }};

    function createShape(shape) {
        const chart = window._tvWidget?.activeChart();
        if (!chart) return;
        const shapePoints = shape.points.map(p => ({time: p.time, price: p.price}));
        const options = {
            shape: shape.shapeType, lock: false, disableSelection: false,
            disableSave: false, disableUndo: true, overrides: shape.overrides || {}, zOrder: 'top',
        };
        try {
            const result = chart.createMultipointShape(shapePoints, options);
            if (!result) return;
            if (typeof result === 'object' && typeof result.then === 'function') {
                result.then(id => console.log('图形创建成功, ID:', id));
            } else {
                console.log('图形创建成功, ID:', result);
            }
        } catch (e) {
            console.warn('创建图形失败:', e);
        }
    }

    function shapeReady(shape, latestTimeMs) {
        if (!shape.points || shape.points.length === 0) return false;
        const maxTimeSec = Math.max(...shape.points.map(p => p.time));
        return maxTimeSec * 1000 <= latestTimeMs;
    }

    function drawShapesUpTo(latestTime) {
        STATIC_SHAPES.forEach((shape, idx) => {
            if (!shapesDrawn.has(idx) && shapeReady(shape, latestTime)) {
                createShape(shape);
                shapesDrawn.add(idx);
            }
        });
    }

    class StaticDatafeed {
        constructor() {
            this._subscribers = {};
            this._allBars = STATIC_DATA.bars;
            this._pushIndex = 0;
        }

        onReady(callback) {
            setTimeout(() => callback({
                supports_search: false, supports_group_request: false,
                supports_marks: false, supports_timescale_marks: true,
                supports_time: true, supported_resolutions: [interval],
            }), 0);
        }

        resolveSymbol(symbolName, onResolve, onError) {
            const sym = symbolName.toUpperCase();
            const symbolInfo = {
                exchange: "exchange", ticker: symbol, name: symbol,
                description: symbol, type: "Unknown", session: "24x7",
                timezone: "Asia/Shanghai", minmov: 1, pricescale: 100,
                visible_plots_set: "ohlcv", has_no_volume: false,
                has_weekly_and_monthly: false, supported_resolutions: [interval],
                volume_precision: 1, has_intraday: true, has_daily: true,
                monthly_multipliers: [], weekly_multipliers: [], format: "price",
            };
            setTimeout(() => onResolve(symbolInfo), 0);
        }

        searchSymbols(userInput, exchange, symbolType, onResult) {
        }

        getBars(symbolInfo, resolution, periodParams, onResult, onError) {
            console.log("[Datafeed.getBars]", symbolInfo, resolution, periodParams);
            if (isFrist) {
                const initialCount = Math.min(10, this._allBars.length);
                const bars = this._allBars.slice(0, initialCount).map(([t, o, h, l, c, v]) => ({
                    time: t * 1000, open: o, high: h, low: l, close: c, volume: v
                }));
                this._pushIndex = initialCount;
                isFrist = false;
                setTimeout(() => onResult(bars, {noData: false}), 0);
            } else {
                onResult([], {noData: true});
            }
        }

        subscribeBars(symbolInfo, resolution, onTick, listenerGuid, onResetCacheNeededCallback) {
            this.unsubscribeBars(listenerGuid);
            const bars = this._allBars;
            let idx = this._pushIndex;
            if (idx > 0) {
                const lastBar = bars[idx - 1];          // 最后一条已加载的K线
                drawShapesUpTo(lastBar[0] * 1000);      // 转为毫秒
            }
            const sub = {};
            sub.timerId = setInterval(() => {
                if (idx >= bars.length) {
                    clearInterval(sub.timerId);
                    return;
                }
                const [t, o, h, l, c, v] = bars[idx];
                const bar = {time: t * 1000, open: o, high: h, low: l, close: c, volume: v};
                onTick(bar);
                drawShapesUpTo(bar.time);
                idx++;
            }, 20);
            this._subscribers[listenerGuid] = sub;
        }

        unsubscribeBars(listenerGuid) {
            const sub = this._subscribers[listenerGuid];
            if (sub && sub.timerId) clearInterval(sub.timerId);
            delete this._subscribers[listenerGuid];
        }

        getServerTime(callback) {
            setTimeout(() => callback(Math.floor(Date.now() / 1000)), 0);
        }
    }

    // 创建静态图形的函数
    function createStaticShapes(chart) {
        STATIC_SHAPES.forEach(shape => {
            const shapePoints = shape.points.map(p => ({
                time: p.time,
                price: p.price
            }));
            const options = {
                shape: shape.shapeType,
                lock: false,
                disableSelection: false,
                disableSave: false,
                disableUndo: true,
                overrides: shape.overrides || {},
                zOrder: 'top',
            };
            try {
                const result = chart.createMultipointShape(shapePoints, options);
                if (result === null) return;
                if (typeof result === 'object' && typeof result.then === 'function') {
                    result.then(id => console.log('图形创建成功, ID:', id));
                } else {
                    console.log('图形创建成功, ID:', result);
                }
            } catch (e) {
                console.warn('创建图形失败:', e);
            }
        });
    }

    // ====================== 图形控制面板 ======================
    function formatPeriod(seconds) {
        const map = {
            60: '1分钟', 180: '3分钟', 300: '5分钟', 900: '15分钟', 1800: '30分钟',
            3600: '1小时', 7200: '2小时', 14400: '4小时', 21600: '6小时',
            43200: '12小时', 86400: '日线', 259200: '3日线'
        };
        return map[seconds] || `${seconds}秒`;
    }

    function setShapesVisibleByPeriod(rawType, periodSeconds, visible) {
        if (!window._tvWidget || !window._tvWidget.activeChart()) return;
        const allShapes = window._tvWidget.activeChart().getAllShapes();
        allShapes.forEach(({id}) => {
            const shape = window._tvWidget.activeChart().getShapeById(id);
            if (!shape) return;
            const props = shape.getProperties();
            const title = props.title || '';
            const parts = title.split(':');
            if (parts.length < 3) return;
            const [, periodStr, typeFromTitle] = parts;
            const periodFromTitle = Number(periodStr);
            if (typeFromTitle === rawType && periodFromTitle === periodSeconds) {
                shape.setProperties({visible});
            }
        });
    }

    function isPeriodVisible(rawType, periodSeconds) {
        if (!window._tvWidget || !window._tvWidget.activeChart()) return true;
        const allShapes = window._tvWidget.activeChart().getAllShapes();
        let hasAny = false;
        for (const {id} of allShapes) {
            const shape = window._tvWidget.activeChart().getShapeById(id);
            if (!shape) continue;
            const props = shape.getProperties();
            const title = props.title || '';
            const parts = title.split(':');
            if (parts.length < 3) continue;
            const [, periodStr, typeFromTitle] = parts;
            const periodFromTitle = Number(periodStr);
            if (typeFromTitle === rawType && periodFromTitle === periodSeconds) {
                hasAny = true;
                if (props.visible === false) return false;
            }
        }
        return hasAny;
    }

    function setupCustomDropdown(triggerElement, widget) {
        const panel = document.createElement('div');
        panel.style.cssText = `
        position: absolute; z-index: 10000;
        background: ${widget.getTheme() === 'dark' ? '#1e2532' : '#ffffff'};
        border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,0.3);
        padding: 8px 0; min-width: 240px; max-height: 70vh; overflow-y: auto;
        border: 1px solid ${widget.getTheme() === 'dark' ? '#3a4a6a' : '#d1d4dc'};
        color: ${widget.getTheme() === 'dark' ? '#d1d9e6' : '#131722'};
        font-family: -apple-system, BlinkMacSystemFont, 'Trebuchet MS', Roboto, sans-serif;
        display: none;
        backdrop-filter: blur(4px);
    `;
        document.body.appendChild(panel);

        const refreshMenu = () => {
            panel.innerHTML = '';
            if (!window._tvWidget || !window._tvWidget.activeChart()) return;
            const allShapes = window._tvWidget.activeChart().getAllShapes();

            const typePeriods = {}; // { rawType -> Set of periods }

            allShapes.forEach(({id}) => {
                const shape = window._tvWidget.activeChart().getShapeById(id);
                if (!shape) return;
                const props = shape.getProperties();
                const title = props.title || '';
                const parts = title.split(':');
                if (parts.length < 3) return;
                const period = Number(parts[1]);
                const rawType = parts[2];          // 直接使用第三段
                if (!typePeriods[rawType]) typePeriods[rawType] = new Set();
                typePeriods[rawType].add(period);
            });

            if (Object.keys(typePeriods).length === 0) {
                const empty = document.createElement('div');
                empty.textContent = '暂无图形数据';
                empty.style.padding = '12px 16px';
                empty.style.opacity = '0.7';
                panel.appendChild(empty);
                return;
            }

            // 为每个基础类型创建折叠组
            Object.keys(typePeriods).sort().forEach(baseType => {
                const section = document.createElement('div');
                const header = document.createElement('div');
                header.style.cssText = `
                padding: 10px 16px; cursor: pointer; display: flex; align-items: center;
                justify-content: space-between; user-select: none;
                background: ${widget.getTheme() === 'dark' ? 'rgba(255,255,255,0.03)' : 'rgba(0,0,0,0.02)'};
                border-bottom: 1px solid rgba(128,128,128,0.2);
            `;
                header.innerHTML = `<span style="font-weight:600;">${baseType}</span><span class="toggle-arrow" style="transition: transform 0.2s;">▼</span>`;
                const content = document.createElement('div');
                content.style.display = 'block';

                header.addEventListener('click', () => {
                    const arrow = header.querySelector('.toggle-arrow');
                    if (content.style.display === 'none') {
                        content.style.display = 'block';
                        arrow.style.transform = 'rotate(0deg)';
                    } else {
                        content.style.display = 'none';
                        arrow.style.transform = 'rotate(-90deg)';
                    }
                });

                const periods = Array.from(typePeriods[baseType]).sort((a, b) => a - b);
                periods.forEach(period => {
                    const row = document.createElement('div');
                    row.style.cssText = `
                    padding: 6px 16px 6px 28px; display: flex; align-items: center;
                    cursor: pointer; transition: background 0.15s;
                `;
                    row.addEventListener('mouseenter', () => row.style.background = widget.getTheme() === 'dark' ? '#2a374b' : '#f0f3fa');
                    row.addEventListener('mouseleave', () => row.style.background = 'transparent');

                    const checkbox = document.createElement('input');
                    checkbox.type = 'checkbox';
                    checkbox.checked = isPeriodVisible(baseType, period);
                    checkbox.style.cssText = `
                    margin-right: 12px; accent-color: #2962ff;
                    width: 16px; height: 16px; cursor: pointer;
                `;

                    const label = document.createElement('span');
                    label.textContent = formatPeriod(period);
                    label.style.flex = '1';

                    row.appendChild(checkbox);
                    row.appendChild(label);

                    row.addEventListener('click', (e) => {
                        if (e.target !== checkbox) {
                            checkbox.checked = !checkbox.checked;
                        }
                        setShapesVisibleByPeriod(baseType, period, checkbox.checked);
                    });

                    content.appendChild(row);
                });

                section.appendChild(header);
                section.appendChild(content);
                panel.appendChild(section);
            });

            // 底部全局控制按钮
            const footer = document.createElement('div');
            footer.style.cssText = `
            border-top: 1px solid ${widget.getTheme() === 'dark' ? '#3a4a6a' : '#d1d4dc'};
            padding: 8px 16px; display: flex; gap: 8px;
        `;
            const showAllBtn = document.createElement('button');
            showAllBtn.textContent = '全部显示';
            showAllBtn.style.cssText = `flex:1; padding: 4px; background: #2563eb; color:white; border:none; border-radius: 4px; cursor:pointer;`;
            showAllBtn.addEventListener('click', () => {
                Object.keys(typePeriods).forEach(baseType => {
                    typePeriods[baseType].forEach(period => {
                        setShapesVisibleByPeriod(baseType, period, true);
                    });
                });
                refreshMenu();
            });
            const hideAllBtn = document.createElement('button');
            hideAllBtn.textContent = '全部隐藏';
            hideAllBtn.style.cssText = `flex:1; padding: 4px; background: #ef4444; color:white; border:none; border-radius: 4px; cursor:pointer;`;
            hideAllBtn.addEventListener('click', () => {
                Object.keys(typePeriods).forEach(baseType => {
                    typePeriods[baseType].forEach(period => {
                        setShapesVisibleByPeriod(baseType, period, false);
                    });
                });
                refreshMenu();
            });
            footer.appendChild(showAllBtn);
            footer.appendChild(hideAllBtn);
            panel.appendChild(footer);
        };

        const showPanel = () => {
            refreshMenu();
            panel.style.display = 'block';
            const rect = triggerElement.getBoundingClientRect();
            // 等待 layout 完成以获取正确的 panel 宽度
            requestAnimationFrame(() => {
                const panelWidth = panel.offsetWidth;
                let left = rect.right - panelWidth;
                if (left < 0) left = 5;
                panel.style.top = `${rect.bottom + 4}px`;
                panel.style.left = `${left}px`;
            });
        };
        const hidePanel = () => {
            panel.style.display = 'none';
        };

        triggerElement.addEventListener('click', (e) => {
            e.stopPropagation();
            panel.style.display === 'block' ? hidePanel() : showPanel();
        });
        document.addEventListener('click', (e) => {
            if (!panel.contains(e.target) && e.target !== triggerElement) hidePanel();
        });
        window.addEventListener('resize', () => {
            if (panel.style.display === 'block') showPanel();
        });

        // 保存引用以便外部强制刷新
        this.refreshDropdown = refreshMenu;
    }

    function createButton() {
        // 创建自定义按钮（注意：useTradingViewStyle 省略或设为 false）
        const customBtn = window._tvWidget.createButton({
            align: 'right' // 可选 'left' 或 'right'
        });

        // 设置按钮内容与样式
        customBtn.innerHTML = `⚙️ 图表工具`;
        customBtn.style.cssText = `
                display: inline-flex;
                align-items: center;
                justify-content: center;
                height: 32px;
                padding: 0 12px;
                margin: 0 4px;
                border-radius: 4px;
                font-size: 14px;
                font-weight: 500;
                color: ${window._tvWidget.getTheme() === 'dark' ? '#d1d4dc' : '#131722'};
                background: transparent;
                border: none;
                cursor: pointer;
                transition: background 0.2s, color 0.2s;
                font-family: -apple-system, BlinkMacSystemFont, 'Trebuchet MS', Roboto, Ubuntu, sans-serif;
                user-select: none;
            `;

        customBtn.addEventListener('mouseenter', () => {
            customBtn.style.background = window._tvWidget.getTheme() === 'dark'
                    ? 'rgba(255, 255, 255, 0.08)'
                    : 'rgba(0, 0, 0, 0.05)';
        });
        customBtn.addEventListener('mouseleave', () => {
            customBtn.style.background = 'transparent';
        });

        // 现在你可以安全地使用 customBtn 作为触发器
        this.setupCustomDropdown(customBtn, window._tvWidget);
    }

    function initChart() {
        if (typeof TradingView === 'undefined') {
            document.getElementById('tv-chart').innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#888;">请将 charting_library 放在同级目录</div>';
            document.getElementById('loading-overlay').classList.add('hidden');
            return;
        }

        const widget = new TradingView.widget({
            container: 'tv-chart', library_path: 'charting_library/', locale: 'zh',
            datafeed: new StaticDatafeed(), symbol, interval,
            favorites: {drawingTools: ["LineToolPath", "LineToolRectangle", "LineToolTrendLine"]},
            disabled_features: [
                "use_localstorage_for_settings", "header_symbol_search",
                "header_undo_redo", "header_screenshot", "header_compare",
                "header_chart_type", "go_to_date",
            ],
            theme: "light",
            fullscreen: false, autosize: true,
            //toolbar_bg: '#2a2a3e',
            overrides: {
                //'paneProperties.background': '#1e1e2e',
                //'mainSeriesProperties.candleStyle.upColor': '#00c853',
                //'mainSeriesProperties.candleStyle.downColor': '#ff1744',
            }
        });
        window._tvWidget = widget;

        widget.onChartReady(() => {
            document.getElementById('loading-overlay').classList.add('hidden');
            console.log('✅ 开始实时回放');
        });

        widget.headerReady().then(() => {
            widget.activeChart().createStudy("MACD", false, false, {
                in_0: 13, in_1: 31, in_2: 11, in_3: "close"
            });
            createButton();
        });
    }

    window.addEventListener('DOMContentLoaded', initChart);
</script>
<script src="charting_library/charting_library.js"></script>
</body>
</html>