var tradeDetails = new Map();
var toolTip = null
var CHART_SHOW_TEXT = get_from_config("CHART_SHOW_TEXT", false)
// const myLibrary = _export;
// const TOML = window['j-toml']
//console.log("TOML je", window)
console.log("CHART_SHOW_TEXT archchart", CHART_SHOW_TEXT)
// var vwapSeries = null
// var volumeSeries = null
var markersLine = null
var avgBuyLine = null
var profitLine = null
var slLine = []
//TRANSFORM object returned from REST API get_arch_run_detail
//to series and markers required by lightweigth chart
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
//output array [{ time: 111, open: 11, high: 33, low: 333, close: 333},..]
function transform_data(data) {
    //console.log(data)
    var SHOW_SL_DIGITS = get_from_config("SHOW_SL_DIGITS", true)
    transformed = []
    //get basic bars, volume and vvwap
    var bars = []
    var volume = []
    var vwap = []

    //pokud mame tak projedeme ext_data pro dane klice a s nimi pracujeme
    var sl_line = []
    var sl_line_markers = []
    var sl_line_sada = []
    var sl_line_markers_sada = []
    //console.log(JSON.stringify(data.ext_data.sl_history, null, 2))
    prev_id = 0
    //cas of first record, nekdy jsou stejny - musim pridat setinku
    prev_cas = 0
    if ((data.ext_data !== null) && (data.ext_data.sl_history)) {
        data.ext_data.sl_history.forEach((histRecord, index, array) => {
            
            //console.log("plnime")

            //nova sada
            if (prev_id !== histRecord.id) {
                if (prev_id !== 0) {
                    //push sadu do pole
                    sl_line.push(sl_line_sada)
                    sl_line_markers.push(sl_line_markers_sada)
                }
                //init nova sada
                sl_line_sada = []
                sl_line_markers_sada = []
            }

            prev_id = histRecord.id

            //prevedeme iso data na timestampy
            cas = histRecord.time

            if (cas == prev_cas) {
                cas = cas + 0.001
            }

            prev_cas = cas

            //line pro buy/sell markery
            sline = {}
            sline["time"] = cas
            sline["value"] = histRecord.sl_val
            sl_line_sada.push(sline)

            sline_markers = {}
            sline_markers["time"] = cas
            sline_markers["position"] = "inBar" 
            sline_markers["color"] = "#f5aa42"
            //sline_markers["shape"] = "circle"
            //console.log("SHOW_SL_DIGITS",SHOW_SL_DIGITS)
            sline_markers["text"] = SHOW_SL_DIGITS ? histRecord.sl_val.toFixed(3) : ""
            sl_line_markers_sada.push(sline_markers)

            if (index === array.length - 1) {
                    //pro posledni zaznam push sadu do pole
                    sl_line.push(sl_line_sada)
                    sl_line_markers.push(sl_line_markers_sada)
            }

            });
        }


    //pomocne
    var last_time = 0
    var time = 0

    data.bars.time.forEach((element, index, array) => {
        sbars = {};
        svolume = {};
        svwap = {};

        //tento algoritmus z duplicit dela posloupnosti a srovna i pripadne nekonzistence
        //napr z .911 .911 .912 udela .911 .912 .913
        //TODO - možná dat do backendu agregatoru
        if (last_time>=element) {
            console.log("bars", "problem v case - zarovnano",time, last_time, element)
            
            data.bars.time[index] = data.bars.time[index-1] + 0.000001
        }

        last_time = data.bars.time[index]
        sbars["time"] = data.bars.time[index];
        sbars["close"] = data.bars.close[index]
        sbars["open"] = data.bars.open[index]
        sbars["high"] = data.bars.high[index]
        sbars["low"] = data.bars.low[index]


        svwap["time"] = data.bars.time[index];
        svwap["value"] = data.bars.vwap[index]

        svolume["time"] = data.bars.time[index];
        svolume["value"] = data.bars.volume[index]

        bars.push(sbars)
        vwap.push(svwap)
        volume.push(svolume)
    }); 
    transformed["bars"] = bars
    //console.log(bars)
    transformed["vwap"] = vwap
    transformed["volume"] = volume
    var bars = []
    var volume = []
    var vwap = []


    if ((data.ext_data !== null) && (data.ext_data.dailyBars)) {
        data.ext_data.dailyBars.time.forEach((element, index, array) => {
            sbars = {};
            svolume = {};
            svwap = {};

            sbars["time"] = element;
            sbars["close"] = data.ext_data.dailyBars.close[index]
            sbars["open"] = data.ext_data.dailyBars.open[index]
            sbars["high"] = data.ext_data.dailyBars.high[index]
            sbars["low"] = data.ext_data.dailyBars.low[index]


            svwap["time"] = element
            svwap["value"] = data.ext_data.dailyBars.vwap[index]

            svolume["time"] = element
            svolume["value"] = data.ext_data.dailyBars.volume[index]

            bars.push(sbars)
            vwap.push(svwap)
            volume.push(svolume)
        }); 
        transformed["dailyBars"] = {}
        transformed["dailyBars"]["bars"] = bars
        transformed["dailyBars"]["vwap"] = vwap
        transformed["dailyBars"]["volume"] = volume
        var bars = []
        var volume = []
        var vwap = []
    }


    //get markers - avgp line for all buys
    var avgp_buy_line = []
    var avgp_markers = []
    var sum_profit_line = []
    var markers = []
    var markers_line = []
    var last_timestamp = 0.1
    var iterator = 0.002
    data.trades.forEach((trade, index, array) => {
        obj = {};
        a_markers = {}
        //tady z predchozi verze muze byt string (pak je to date v iso) a nebo v novejsi uz mame timestamp

        timestamp = (typeof trade.order.filled_at === 'string') ? Date.parse(trade.order.filled_at)/1000 : trade.order.filled_at
        //light chart neumi vice zaznamu ve stejny cas
        //protoze v BT se muze stat vice tradu v jeden cas, testujeme stejne hodnoty a pripadne pricteme jednu ms
        //tradu s jednim casem muze byt za sebou vic, proto iterator 
        if (last_timestamp > timestamp) {
            console.log("NEKONZISTENCE RAZENI",last_timestamp, timestamp)
            console.log("aktualni trade", JSON.stringify(trade,null,2))
            console.log("předchozí trade", JSON.stringify(data.trades[index-1],null,2))
        }
        if (last_timestamp == timestamp) {
            last_timestamp = timestamp
            console.log("DUPLICITA tradu aktual/predchozi/nasledujici", trade, data.trades[index-1], data.trades[index+1])
            console.log("původní timestamp je ",timestamp)
            timestamp = parseFloat(timestamp) + iterator
            console.log("nový timestamp je ",timestamp)
            iterator += 0.001
        }
        else {
            last_timestamp = timestamp
            iterator = 0.002
        }
        
        //AVG BUY LINE - zatim docasne vypiname
        if (((trade.order.side == "buy") || (trade.order.side == "sell")) && 1==2) {
                //avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
                if ((trade.pos_avg_price !== null) && (trade.pos_avg_price !== 0)) {
                //line pro avgp markers
                obj["time"] = timestamp;
                obj["value"] = trade.pos_avg_price;
                avgp_buy_line.push(obj)

                //avgp markers pro prumernou cenu aktualnich pozic
                a_markers["time"] = timestamp
                a_markers["position"] = "aboveBar"
                a_markers["color"] = "#e8c76d"
                a_markers["shape"] = "arrowDown"
                //if (CHART_SHOW_TEXT) 
                //a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3)
                //a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
                avgp_markers.push(a_markers)
            }
        }
        //PROFITLINE
        if ((trade.order.side == "buy") || (trade.order.side == "sell")) {
                //avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
            if ((trade.profit_sum !== null)) {
                //line pro avgp markers
                obj["time"] = timestamp;
                obj["value"] = trade.profit_sum.toFixed(1);
                sum_profit_line.push(obj)

                //avgp markers pro prumernou cenu aktualnich pozic
                // a_markers["time"] = timestamp
                // a_markers["position"] = "aboveBar"
                // a_markers["color"] = "#e8c76d"
                // a_markers["shape"] = "arrowDown"
                // a_markers["text"] = trade.profit_sum.toFixed(1);
                // //if (CHART_SHOW_TEXT) 
                // //a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3)
                // //a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
                // avgp_markers.push(a_markers)
            }
        }
        

        //buy sell markery
        marker = {}
        marker["time"] = timestamp;
        // marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar" 
        marker["position"] = (trade.order.side == "buy") ? "aboveBar" : "aboveBar" 
        marker["color"] = (trade.order.side == "buy") ? "#37cade" : "red"
        //marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
        marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
        //marker["text"] =  trade.qty + "/" + trade.price
        qt_optimized = (trade.order.qty % 1000 === 0) ? (trade.order.qty / 1000).toFixed(1) + 'K' : trade.order.qty
  
        if (CHART_SHOW_TEXT) {
            //včetně qty
            //marker["text"] =  qt_optimized + "@" + trade.price
                
            //bez qty
            marker["text"] =  trade.price
            closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
            marker["text"] += (trade.position_qty == 0) ? closed_trade_marker_and_profit : ""
        } else {
            closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
            marker["text"] = (trade.position_qty == 0) ? closed_trade_marker_and_profit : trade.price.toFixed(3)
        }

        markers.push(marker)

        //prevedeme iso data na timestampy
        //open bud zde je iso string (predchozi verze) nebo rovnou float - podporime oboji
        trade.order.submitted_at = (typeof trade.order.submitted_at === 'string') ? Date.parse(trade.order.submitted_at)/1000 : trade.order.submitted_at
        trade.order.filled_at = (typeof trade.order.filled_at === 'string') ? Date.parse(trade.order.filled_at)/1000 : trade.order.filled_at
        trade.timestamp = (typeof trade.timestamp  === 'string') ? Date.parse(trade.order.timestamp)/1000 : trade.order.timestamp
        tradeDetails.set(timestamp, trade)

        //line pro buy/sell markery
        mline = {}
        mline["time"] = timestamp
        mline["value"] = trade.price
        markers_line.push(mline)

    // time: datesForMarkers[i].time,
    // position: 'aboveBar',
    // color: '#e91e63',
    // shape: 'arrowDown',
    // text: 'Sell @ ' + Math.floor(datesForMarkers[i].high + 2),     
        
    });

    //pro jistotu jeste seradime podle casu
    //v BT se muze predbehnout a lightweight to pak nezobrazi
    markers.sort(sorter)
    markers_line.sort(sorter)
    avgp_buy_line.sort(sorter)
    avgp_markers.sort(sorter)
    sum_profit_line.sort(sorter)

    transformed["avgp_buy_line"] = avgp_buy_line
    transformed["sum_profit_line"] = sum_profit_line 
    transformed["avgp_markers"] = avgp_markers
    transformed["markers"] = markers
    transformed["markers_line"] = markers_line
    transformed["sl_line"] = sl_line
    transformed["sl_line_markers"] = sl_line_markers
    //console.log("naplnene", sl_line, sl_line_markers)
    //console_log(JSON.stringify(transformed["sl_line"],null,2))
    //console_log(JSON.stringify(transformed["sl_line_markers"],null,2))
    //get additional indicators
    return transformed
}

//unit: Min, Hour, Day, Week, Month
//prepares data before displaying archived chart - fetch history bars if necessary
function prepare_data(archRunner, timeframe_amount, timeframe_unit, archivedRunnerDetail) {
   req = {}
   req["symbol"] = archRunner.symbol

    if (archRunner.mode == "backtest") {
        req["datetime_object_from"] = archRunner.bt_from
        req["datetime_object_to"] = archRunner.bt_to
    }
    else 
    {
        req["datetime_object_from"] = archRunner.started
        req["datetime_object_to"] = archRunner.stopped
    }
    console.log("datum pred",req.datetime_object_from)
    //pridame z obou stran 1 minutu - kvuli zobrazeni na frontendu,
    req["datetime_object_from"] = subtractMinutes(new Date(req.datetime_object_from),1).toISOString()
    req["datetime_object_to"] = addMinutes(new Date(req.datetime_object_to),1).toISOString()
    console.log("datum po", req.datetime_object_from)
    req["timeframe_amount"] = timeframe_amount
    req["timeframe_unit"] = timeframe_unit
    $.ajax({
        url:"/history_bars/",
        beforeSend: function (xhr) {
            xhr.setRequestHeader('X-API-Key',
                API_KEY); },
        method:"GET",
        contentType: "application/json",
        data: req,
        success:function(data){
            //console.log("one minute bars before", JSON.stringify(data))
            data.map((el)=>{
                cas = new Date(el.timestamp)
                el.time = cas.getTime()/1000;
                delete el.timestamp
                });
            //console.log("one min bars_after_transformation", JSON.stringify(data))
            oneMinuteBars = data
            chart_archived_run(archRunner, archivedRunnerDetail, oneMinuteBars);
            //call function to continue
            //return data
            //$("#statusStratvars").text(JSON.stringify(data.stratvars,null,2))
        },
        error: function(xhr, status, error) {
            oneMinuteBars = null
            chart_archived_run(archRunner, archivedRunnerDetail, oneMinuteBars);
            var err = eval("(" + xhr.responseText + ")");
            window.alert(JSON.stringify(xhr));
            console.log(JSON.stringify(xhr));
        }
    })  
}

//pomocna sluzba pro naplneni indListu a charting indikatoru
function chart_indicators(data, visible, offset) {
    console.log(data)
    //console.log("indikatory", JSON.stringify(data.indicators,null,2))
    //podobne v livewebsokcets.js - dat do jedne funkce
    if (data.hasOwnProperty("indicators")) { 
        // console.log("jsme uvnitr indikatoru")

        //vraci se pole indicatoru, kazdy se svoji casovou osou (time) - nyni standard indikatory a cbar indikatory
        var indicatorList = data.indicators

        //ze stratvars daneho runnera si dotahneme nastaveni indikatoru - pro zobrazeni v tooltipu

        //TOML parse
        //console.log("PUVODNI", data.archRecord.stratvars_toml)
        var stratvars_toml = TOML.parse(data.archRecord.stratvars_toml)
        

        //console.log("ZPETNE STRINGIFIED", TOML.stringify(TOML.parse(data.archRecord.stratvars_toml), {newline: '\n'}))
        //indicatory
        //console.log("indicatory TOML", stratvars_toml.stratvars.indicators)
        indId = 1
        var multiOutsCnf = {}
        indicatorList.forEach((indicators, index, array) => {

            //var indicators = data.indicators
            //index 0 - bar indikatory
            //index 1 - tick based indikatory
            //if there are indicators it means there must be at least two keys (time which is always present)
            if (Object.keys(indicators).length > 1) {
                for (const [key, value] of Object.entries(indicators)) {
                    if (key !== "time") {
                            //get cnf of indicator to display in the button tooltip
                            var cnf = null
                            //pokud je v nastaveni scale, pouzijeme tu
                            var scale = null
                            var instant = null
                            var returns = null
                            //console.log(key)
                            //zkusime zda nejde o instantni indikator z arch runneru
                            if ((data.ext_data !== null) && (data.ext_data.instantindicators)) {
                                let instantIndicator = data.ext_data.instantindicators.find(indicator => indicator.name == key);
                                //console.log("nalezen", key)
                                if (instantIndicator) {
                                    cnf = instantIndicator.toml
                                    scale = TOML.parse(cnf).scale
                                    instant = 1
                                    returns = TOML.parse(cnf).returns
                                }
                            }
                            //pokud nenalezeno, pak bereme standard
                            //pozor ted nebereme z addedInds
                            if (!cnf) {
                                if (stratvars_toml.stratvars.indicators[key]) {
                                    cnf = "#[stratvars.indicators."+key+"]"+TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'})
                                    scale = stratvars_toml.stratvars.indicators[key].scale
                                    returns = stratvars_toml.stratvars.indicators[key].returns
                                    }
                                }
                            //     //kontriolujeme v addedInds
                            //     if (addedInds[key]) {
                            //         cnf = addedInds[key]
                            //         scale = TOML.parse(cnf).scale
                            //         instant = 1
                            //       }
                            //       //a az potom bereme normos
                            //       else
                            //       {
                            //       cnf = "#[stratvars.indicators."+key+"]"+TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'})
                            //       scale = stratvars_toml.stratvars.indicators[key].scale
                            //       //cnf = TOML.stringify(stratvars_toml.stratvars.indicators[key], {newline: '\n'})
                            //       //a = TOML.parse(cnf)
                            //       //console.log("PARSED again",a)
                            //       }
                            // } 

                            //pro multioutput childs dotahneme scale z parenta
                            if (multiOutsCnf.hasOwnProperty(key)) {
                                scale = multiOutsCnf[key];
                            }

                            //initialize indicator and store reference to array
                            var obj = {name: key, type: index, series: null, cnf:cnf, instant: instant, returns: returns, indId:indId++}
    
                            //pokud jde o multioutput parenta ukladam scale parenta pro children
                            //varianty -  scale je jeden, ukladam jako scale pro vsechny parenty
                            //         -  scale je list - pouzijeme pro kazdy output scale v listu na stejnem indexu jako output
                            if (returns) {
                                returns.forEach((returned, index, array) => {
                                    //
                                    if (Array.isArray(scale)) {
                                        multiOutsCnf[returned] = scale[index]
                                    }
                                    else {
                                        multiOutsCnf[returned] = scale
                                    }
                                })
                            }                        //start
                            //console.log(key)
                            //get configuation of indicator to display
                            conf = get_ind_config(key, index)

                            //pokud neni v configuraci - zobrazujeme defaultne

                            //INIT INDICATOR BASED on CONFIGURATION

                            //DO BUDOUCNA zde udelat sorter a pripadny handling duplicit jako
                            //funkci do ktere muzu zavolat vse co pujde jako data do chartu

                            //MOVE TO UTILS ro reuse??
                            //if (conf && conf.display) {
                            if (conf && conf.embed) {

                                //tranform data do správného formátru
                                items = []
                                //var last = null
                                var last_time = 0
                                var time = 0

                                //tento algoritmus z duplicit dela posloupnosti a srovna i pripadne nekonzistence
                                //napr z .911 .911 .912 udela .911 .912 .913
                                value.forEach((element, index, array) => {
                                    item = {}
                                    //debug
                                    //TOTO odstranit po identifikovani chyby
                                    //if (indicators.time[index] !== undefined) {
                                        //{console.log("problem",key,last)}
                                    time = indicators.time[index]

                                    //pokud je nastaveny offset (zobrazujeme pouze bod vzdaleny N sekund od posledniho)
                                    //vynechavame prvni iteraci, aby se nam naplnil last_time
                                    if (offset && last_time !==0) {
                                        if (last_time + offset > time) {
                                            return;
                                        }
                                    }


                                    if (last_time>=time) {
                                        console.log(key, "problem v case - zarovnano",time, last_time, element)
                                        
                                        indicators.time[index] = indicators.time[index-1] + 0.000001
                                    }
                                    item["time"] = indicators.time[index]
                                    item["value"] = element

                                    last_time = indicators.time[index]

                                    if ((element == null) || (indicators.time[index] == null)) {
                                    console.log("probelem u indikatoru",key, "nekonzistence", element, indicators.time[index]) 
                                    }

                                    //console.log("objekt indicatoru",item)
                                    items.push(item)
                                        //debug
                                    //last = item
                                    // }
                                    // else
                                    // {
                                    //     console.log("chybejici cas", key)
                                    // }
                                });

                                //SERADIT PRO JISTOTU
                                //items.sort(sorter)

                                //FIND DUPLICITIES
                                // last_time = 0
                                // items.forEach((element, index, array) => {
                                //     if (last_time >= element.time) {
                                //         console.log("je duplicita/nekonzistence v ", element.time, element.value)
                                //     }
                                //     last_time = element.time
                                // })
                                

                                if (conf.embed)  {

                                    if (conf.histogram) {

                                        obj.series = chart.addHistogramSeries({
                                            title: (conf.titlevisible?key:""),
                                            color: colors.shift(),
                                            priceFormat: {type: 'volume'},
                                            priceScaleId: (scale)?scale:conf.priceScaleId,
                                            lastValueVisible: conf.lastValueVisible,
                                            visible: conf.display});
                                        
                                        obj.series.priceScale().applyOptions({
                                            // set the positioning of the volume series
                                            scaleMargins: {
                                                top: 0.7, // highest point of the series will be 70% away from the top
                                                bottom: 0,
                                            },
                                        });

                                    }
                                    else {
                                        var barva = colors.shift()
                                        obj.series = chart.addLineSeries({
                                            color: barva,
                                            priceScaleId: (scale)?scale:conf.priceScaleId,
                                            title: (conf.titlevisible?key:""),
                                            lineWidth: 1,
                                            visible: conf.display
                                        });   

                                        // //existuje statinds se stejnym klicem - bereme z nej minimum slope
                                        // a = data.statinds[key]
                                        // console.log("pro klic" + key + ":"+a, JSON.stringify(a,null,2))
                                        // console.log(data.statinds[key].minimum_slope)
                                        // console.log(JSON.stringify(data.statinds,null,2))
                                        //console.log((key in data.statinds), data.statinds, key)
                                        if (key in data.statinds) {
                                            //natvrdo nakreslime lajnu pro min angle
                                            //TODO predelat na configuracne
                                            const minSlopeLineOptopns = {
                                                price: data.statinds[key].minimum_slope,
                                                color: barva,
                                                lineWidth: 1,
                                                lineStyle: 2, // LineStyle.Dotted
                                                axisLabelVisible: true,
                                                title: "min",
                                            };
                                
                                            const minSlopeLine = obj.series.createPriceLine(minSlopeLineOptopns);

                                            const maxSlopeLineOptopns = {
                                                price: data.statinds[key].maximum_slope,
                                                color: barva,
                                                lineWidth: 1,
                                                lineStyle: 2, // LineStyle.Dotted
                                                axisLabelVisible: true,
                                                title: "max",
                                            };
                                
                                            const maxSlopeLine = obj.series.createPriceLine(maxSlopeLineOptopns);



                                        }
                                }


                                }
                                //INDICATOR on new pane
                                else { console.log("not implemented")}


                            //console.log("v chartovani",activatedButtons)
                            //pokud existuje v aktivnich pak zobrazujeme jako aktivni
                            if ((activatedButtons) && (activatedButtons.includes(obj.name))) {
                                //console.log("true",active?active:conf.display)
                                active = true
                            }
                            else {active = false}
                            
                            //pro main s multioutputem nezobrazujeme
                            if (returns) {
                                active = false
                            }
                            //add options
                            obj.series.applyOptions({
                                visible: active?active:visible,
                                lastValueVisible: false,
                                priceLineVisible: false,
                            });

                            //DEBUG
                            // if (key == 'tick_price') {
                            //     console.log("problem tu",JSON.stringify(items,null,2))
                            // }
                            //add data
                            obj.series.setData(items)

                            // add to indList array - pole zobrazovanych indikatoru    
                            indList.push(obj);   
                        }
                    }
                }
            }
        })
    }

    //sort by type first (0-bar,1-cbar inds) and then alphabetically
    // indList.sort((a, b) => {
    //     if (a.type !== b.type) {
    //         return a.type - b.type;
    //     } else {
    //         let nameA = a.name.toUpperCase();
    //         let nameB = b.name.toUpperCase();
    //         if (nameA < nameB) {
    //             return -1;
    //         } else if (nameA > nameB) {
    //             return 1;
    //         } else {
    //             // If uppercase names are equal, compare original names to prioritize uppercase
    //             return a.name < b.name ? -1 : 1;
    //         }
    //     }
    // });

    //SORTING tak, aby multioutputs atributy byly vzdy na konci dane skupiny (tzn. v zobrazeni jsou zpracovany svými rodiči)
    // Step 1: Create a Set of all names in 'returns' arrays
    const namesInReturns = new Set();
    indList.forEach(item => {
    if (Array.isArray(item.returns)) {
        item.returns.forEach(name => namesInReturns.add(name));
    }
    });

    // Step 2: Custom sort function
    indList.sort((a, b) => {
    // First, sort by 'type'
    if (a.type !== b.type) {
        return a.type - b.type;
    }

    // For items with the same 'type', apply secondary sorting
    const aInReturns = namesInReturns.has(a.name);
    const bInReturns = namesInReturns.has(b.name);

    if (aInReturns && !bInReturns) return 1;  // 'a' goes after 'b'
    if (!aInReturns && bInReturns) return -1; // 'a' goes before 'b'

    // If both or neither are in 'returns', sort alphabetically by 'name'
    return a.name.localeCompare(b.name);
    });



    //puvodni funkce
    // indList.sort((a, b) => {
    //     const nameA = a.name.toUpperCase(); // ignore upper and lowercase
    //     const nameB = b.name.toUpperCase(); // ignore upper and lowercase
    //     if (nameA < nameB) {
    //         return -1;
    //     }
    //     if (nameA > nameB) {
    //         return 1;
    //     }
    //     // names must be equal
    //     return 0;

    // });
    //vwap a volume zatim jen v detailnim zobrazeni
    if (!offset) {
        //display vwap and volume
        initialize_vwap()
        vwapSeries.setData(data.transformed_data["vwap"])

        initialize_volume()
        volumeSeries.setData(data.transformed_data["volume"])
        console.log("volume") 
    }       
}

//pomocna
function remove_indicators() {
    //reset COLORS
    colors = reset_colors.slice()

    //remove CUSTOMS indicators if exists
    indList.forEach((element, index, array) => {
        if (element.series) {
            //console.log(element.series, "tady series")
            chart.removeSeries(element.series);
        }
    }
    );
    indList = [];
    //remove BASIC indicators
    if (vwapSeries) {
        chart.removeSeries(vwapSeries)
        vwapSeries = null;
    }
    if (volumeSeries) {
        chart.removeSeries(volumeSeries)
        volumeSeries = null;
    }
}

//switch to interval pomocna funkce
function switch_to_interval(interval, data, extra) {
    store_activated_buttons_state(extra);

    if (!data) {
        window.alert("no data switch to interval")
    }

    //prip prpenuti prepisujeme candlestick a markery
    if (candlestickSeries) {
        last_range = chart.timeScale().getVisibleRange()
        chart.removeSeries(candlestickSeries);
        candlestickSeries = null
    }
    else {
        last_range = null
    }

    intitialize_candles()
    candlestickSeries.setData(data.AllCandleSeriesesData.get(interval));

    var containerlower = document.getElementById('lowercontainer');
    remove_indicators();
    btnElement = document.getElementById("indicatorsButtons")
    if (btnElement) {
        containerlower.removeChild(btnElement);
    }

    if (interval == data.native_resolution) {
        //indicators are in native resolution only
        chart_indicators(data, false);
        var indbuttonElement = populate_indicator_buttons(false);   
    }
    else {
        //na nepuvodnim grafu zobrazit jako offset a zobrazit jako neviditelne 
        chart_indicators(data,false,30)
        //buttonky jako vypnute
        var indbuttonElement = populate_indicator_buttons(false);
    }
    containerlower.append(indbuttonElement);



    display_buy_markers(data);
    //TADY JSEM SKONCIL - toto  nize predelat na hide button pro display bar markers
    // btnElement = document.getElementById("pricelineButtons")
    // var indbuttonElement = populate_indicator_buttons(false);
    // if (btnElement) {
    //     container1.removeChild(btnElement);
    // }
    //container1.append(indbuttonElement);   

    if (last_range) {
        chart.timeScale().setVisibleRange(last_range);
    }
}

//displays (redraws) buy markers
function display_buy_markers(data) {

    transformed_data = data.transformed_data
    // if (profitLine) {
    //     console.log(profitLine)
    //     chart.removeSeries(profitLine)
    //     console.log("nd")
    // }

    if (avgBuyLine) {
        chart.removeSeries(avgBuyLine)
    }

    if (markersLine) {
        chart.removeSeries(markersLine)
    }

    if (slLine) {
        slLine.forEach((series, index, array) => {
            chart.removeSeries(series)
        })
        slLine=[]

    }
    // if (slLine) {
    //     chart.removeSeries(slLine)
    // }      

    //console.log("avgp_buy_line",JSON.stringify(transformed_data["avgp_buy_line"],null,2))
    //console.log("avgp_markers",JSON.stringify(transformed_data["avgp_markers"],null,2))

    //if (transformed_data["sl_line"].length > 0) {
    //console.log(JSON.stringify(transformed_data["sl_line"]), null,2)
    //xx - ted bude slLine pole
    transformed_data["sl_line"].forEach((slRecord, index, array) => {

        //console.log("uvnitr")
        slLine_temp = chart.addLineSeries({
            //    title: "avgpbuyline",
                color: '#e4c76d',
            //    color: 'transparent',
                lineWidth: 1,
                lastValueVisible: false
            });

            slLine_temp.applyOptions({
            lastValueVisible: false,
            priceLineVisible: false,
        });

        slLine_temp.setData(slRecord);
        slLine_temp.setMarkers(transformed_data["sl_line_markers"][index]);
        slLine.push(slLine_temp)

        //xx
    })

    //}

    if (transformed_data["sum_profit_line"].length > 0) {
        profitLine = chart.addLineSeries({
            //    title: "avgpbuyline", e8c76d
                color: '#99912b',
            //    color: 'transparent',
                lineWidth: 1,
                lastValueVisible: false
            });

            profitLine.applyOptions({
            lastValueVisible: false,
            priceLineVisible: false,
            priceScaleId: "own",
            visible: false
        });


        profitLine.setData(transformed_data["sum_profit_line"]);
        //profitLine.setMarkers(transformed_data["sum_profit_line_markers"]);
    }


    if (transformed_data["avgp_buy_line"].length > 0) {
        avgBuyLine = chart.addLineSeries({
            //    title: "avgpbuyline",
                color: '#e8c76d',
            //    color: 'transparent',
                lineWidth: 1,
                lastValueVisible: false
            });

        avgBuyLine.applyOptions({
            lastValueVisible: false,
            priceLineVisible: false,
        });


        avgBuyLine.setData(transformed_data["avgp_buy_line"]);
        avgBuyLine.setMarkers(transformed_data["avgp_markers"]);
    }

    markersLine = chart.addLineSeries({
        //  title: "avgpbuyline",
        //  color: '#d6d1c3',
            color: 'transparent',
            lineWidth: 1,
            lastValueVisible: false
        });

    //console.log("markers_line",JSON.stringify(transformed_data["markers_line"],null,2))
    //console.log("markers",JSON.stringify(transformed_data["markers"],null,2))

    markersLine.setData(transformed_data["markers_line"]);
    markersLine.setMarkers(transformed_data["markers"])

    const container1 = document.getElementById('chart');

    //chart.subscribeCrosshairMove(param => {
    chart.subscribeCrosshairMove(param => {
        //LEGEND SECTIOIN
        firstRow.style.color = 'white';
        update_chart_legend(param);

        //TOOLTIP SECTION
        //$('#trade-timestamp').val(param.time)
        if (
            param.point === undefined ||
            !param.time ||
            param.point.x < 0 ||
            param.point.x > container1.clientWidth ||
            param.point.y < 0 ||
            param.point.y > container1.clientHeight
        ) {
            toolTip.style.display = 'none';
        } else {
            //vyber serie s jakou chci pracovat - muzu i dynamicky
            //je to mapa https://tradingview.github.io/lightweight-charts/docs/api/interfaces/MouseEventParams

            //key = series (key.seriestype vraci Line/Candlestick atp.)  https://tradingview.github.io/lightweight-charts/docs/api/interfaces/SeriesOptionsMap

            toolTip.style.display = 'none';
            toolTip.innerHTML = "";
            var data = param.seriesData.get(markersLine);
            var data2 = param.seriesData.get(avgBuyLine);
            var profitdata = param.seriesData.get(profitLine);
            if ((data !== undefined) || (data2 !== undefined)) {
            //param.seriesData.forEach((value, key) => {
                //console.log("key",key)
                //console.log("value",value)
            
                //data = value
                //DOCASNE VYPNUTO
                toolTip.style.display = 'block';
                
                //console.log(JSON.safeStringify(key))
                // if (toolTip.innerHTML == "") {
                //     toolTip.innerHTML = `<div>${param.time}</div>`
                // }
                buy_price = 0
                //u sell markeru nemame avgBuyLine
                if (data2 !== undefined) {
                    buy_price = parseFloat(data2.value).toFixed(3)
                }

                toolTip.innerHTML += `<div>POS:${tradeDetails.get(param.time).position_qty}/${buy_price}</div><div>T:${tradeDetails.get(param.time).order.qty}/${data.value}</div>`;
                if (profitdata !== undefined) {
                    toolTip.innerHTML += `<div>P:${parseFloat(profitdata.value).toFixed(1)}</div>`
                }
                    //inspirace
                // toolTip.innerHTML = `<div style="color: ${'#2962FF'}">Apple Inc.</div><div style="font-size: 24px; margin: 4px 0px; color: ${'black'}">
                // ${Math.round(100 * price) / 100}
                // </div><div style="color: ${'black'}">
                // ${dateStr}
                // </div>`;


                // Position tooltip according to mouse cursor position
                toolTip.style.left = param.point.x+120 + 'px';
                toolTip.style.top = param.point.y + 'px';
            }
                //});
        }
    });
}

//render chart of archived runs
function chart_archived_run(archRecord, data, oneMinuteBars) {
    cleanup_chart()

    var transformed_data = transform_data(data)

    data["transformed_data"] = transformed_data
    data["archRecord"] = archRecord

    //initialize resolutions
    data["native_resolution"] = data.bars.resolution[0]+"s"
    //console.log("native", native_resolution)
 
    //available intervals zatim jen 1m
    var intervals = [data.native_resolution, '1m', '1d'];
    
    var dailyData = null
    if (transformed_data["dailyBars"]) {
        dailyData = transformed_data["dailyBars"]["bars"]
    }
    //zkusime daily data dat do minuty
    //console.log("daily", dailyData)

    nativeData  = transformed_data["bars"]
    //console.log("native")

    //get one minute data
    //tbd prepare volume
    //console.log("oneMinuteData",oneMinuteBars)

    data["AllCandleSeriesesData"] = new Map([
        [data.native_resolution, nativeData ],
        ["1m", dailyData?dailyData.concat(oneMinuteBars):oneMinuteBars],
        ["1d", dailyData ],
      ]);

    //dame si data do globalni, abychom je mohli pouzivat jinde (trochu prasarna, predelat pak)
    archData = data

   var switcherElement = createSimpleSwitcher(intervals, intervals[1], switch_to_interval, data);

    //define tooltip
    const container1 = document.getElementById('chart');
    const containerlower = document.getElementById('lowercontainer');

    const toolTipWidth = 90;
    const toolTipHeight = 90;
    const toolTipMargin = 15;
    
    // Create and style the tooltip html element
    toolTip = document.createElement('div');
    //width: 90px; , height: 80px; 
    toolTip.style = `position: absolute; display: none; padding: 8px; box-sizing: border-box; font-size: 12px; text-align: left; z-index: 1000; top: 12px; left: 12px; pointer-events: none; border: 1px solid; border-radius: 2px;font-family: -apple-system, BlinkMacSystemFont, 'Trebuchet MS', Roboto, Ubuntu, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;`;
    toolTip.style.backgroundColor = '#2d323e';
    toolTip.style.color = '#babfcd';
    toolTip.style.borderColor = "#a7a9b0";
    //'#2962FF';
    container1.appendChild(toolTip);
    //initialize chart
    document.getElementById("chart").style.display = "block"

    initialize_chart()

    containerlower.append(switcherElement)

    candlestickSeries = null

    //v pripade, ze neprojde get bars, nastavit na intervals[0]
    switch_to_interval(intervals[1], data)
    chart.timeScale().fitContent();

    chart.subscribeClick(param => {
        $('#trade-timestamp').val(param.time)
        //toggle_vertical_line(param.time);
        if (archRecord.ilog_save == true) {
            fetch_log_data(param.time, archRecord.id);
            }
        if (
            param.point === undefined ||
            !param.time ||
            param.point.x < 0 ||
            param.point.x > container1.clientWidth ||
            param.point.y < 0 ||
            param.point.y > container1.clientHeight
        ) {
            toolTip.style.display = 'none';
        } else {
            //vyber serie s jakou chci pracovat - muzu i dynamicky
            //je to mapa https://tradingview.github.io/lightweight-charts/docs/api/interfaces/MouseEventParams

            //key = series (key.seriestype vraci Line/Candlestick atp.)  https://tradingview.github.io/lightweight-charts/docs/api/interfaces/SeriesOptionsMap

            toolTip.style.display = 'none';
            toolTip.innerHTML = "";
            var data = param.seriesData.get(markersLine);
            if (data !== undefined) {
            //param.seriesData.forEach((value, key) => {
                //console.log("key",key)
                //console.log("value",value)
               
                //data = value
                //DOCASNE VYPNUTO
                toolTip.style.display = 'block';
                
                //console.log(JSON.safeStringify(key))
                if (toolTip.innerHTML == "") {
                    toolTip.innerHTML = `<div>${param.time}</div>`
                }
                var price = data.value
                // !== undefined ? data.value : data.close;


                toolTip.innerHTML += `<pre>${JSON.stringify(tradeDetails.get(param.time),null,2)}</pre><div>${price.toFixed(3)}</div>`;

                //console.log("toolTip.innerHTML",toolTip.innerHTML)

                //inspirace
                // toolTip.innerHTML = `<div style="color: ${'#2962FF'}">Apple Inc.</div><div style="font-size: 24px; margin: 4px 0px; color: ${'black'}">
                // ${Math.round(100 * price) / 100}
                // </div><div style="color: ${'black'}">
                // ${dateStr}
                // </div>`;


                // Position tooltip according to mouse cursor position
                toolTip.style.left = param.point.x+120 + 'px';
                toolTip.style.top = param.point.y-100 + 'px';
            }
                //});
        }
    });


    //add status
    $("#statusArchId").text(archRecord.id)
    $("#statusRegime").text("PAST RUN: "+archRecord.id)
    $("#statusName").text(archRecord.name)
    $("#statusMode").text(archRecord.mode)
    $("#statusAccount").text(archRecord.account)
    $("#statusIlog").text("Logged:" + archRecord.ilog_save)
    $("#statusStratvars").text(((archRecord.strat_json)?archRecord.strat_json:archRecord.stratvars),null,2)
    $("#statusSettings").text(JSON.stringify(archRecord.settings,null,2) + JSON.stringify(data.ext_data,null,2))
    
//mezitim se master zmenil //test /novy test

}


function fetch_log_data(timestamp, runner_id) {
    timestamp_from = timestamp - 20
    timestamp_to = timestamp + 20
    req = {}
    req["runner_id"] = runner_id;
    req["timestamp_from"] = timestamp_from;
    req["timestamp_to"] = timestamp_to;
    $.ajax({
        url:"/archived_runners_log/"+runner_id,
        beforeSend: function (xhr) {
            xhr.setRequestHeader('X-API-Key',
                API_KEY); },
        method:"GET",
        contentType: "application/json",
        data: req,
        success:function(data){
            //console.log("archived logs", JSON.stringify(data))
            display_log(data, timestamp)
        },
        error: function(xhr, status, error) {
            var err = eval("(" + xhr.responseText + ")");
            //window.alert(JSON.stringify(xhr));
            console.log("Chyb pri dotazeni logu", JSON.stringify(xhr));
        }
    })  
}

function display_log(iterLogList, timestamp) {
        //console.log("Incoming logline object")


        var lines = document.getElementById('lines')
        var line = document.createElement('div')
        line.classList.add("line")
        const newLine = document.createTextNode("---------------")
        line.appendChild(newLine)
        lines.appendChild(line)

        iterLogList.forEach((logLine) => {
            //console.log("logline item")
            //console.log(JSON.stringify(logLine,null,2))

            // <div class="line">
            //     <div data-toggle="collapse" data-target="#rec1">12233 <strong>Event</strong></div>
            //     <div id="rec1" class="collapse">
            //     Detaila mozna structured
            //     Lorem ipsum dolor sit amet, consectetur adipisicing elit,
            //     sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
            //     quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
            //     </div>
            // </div>

            highlighted = (parseInt(logLine.time) == parseInt(timestamp)) ? "highlighted" : ""
            logcnt++;
            hdr = logLine.time + " " + logLine.event + ' - '+ (logLine.message == undefined ? "" : logLine.message)
            hdr = Prism.highlight(hdr, Prism.languages.log, 'log');

            row = '<div data-bs-toggle="collapse" class="'+ highlighted + '" onclick="set_timestamp(' + logLine.time + ')" data-bs-target="#rec'+logcnt+'">'
            +hdr + '</div>'
            str_row = JSON.stringify(logLine.details, null, 2)
            //row_detail = '<div id="rec'+logcnt+'" data-toggle="collapse" data-target="#rec'+logcnt+'"class="collapse pidi"><pre>' + str_row + '</pre></div>'

            const html = Prism.highlight(str_row, Prism.languages.json, 'json');
            //console.log("tady", html)
            row_detail = '<div id="rec'+logcnt+'" class="collapse pidi"><pre><code class="language-log">' + html + '</code></pre></div>'

            var lines = document.getElementById('lines')
            var line = document.createElement('div')
            line.classList.add("line")
            line.dataset.timestamp = logLine.time
            
            line.insertAdjacentHTML( 'beforeend', row );
            line.insertAdjacentHTML( 'beforeend', row_detail );
            //line.appendChild(newLine)
            //var pre = document.createElement("span")
            //pre.classList.add("pidi")
            //const stLine = document.createTextNode(str_row)
            //pre.appendChild(stLine)
            //line.appendChild(pre)
            lines.appendChild(line)
        });
        $('#messages').animate({
            scrollTop: $('#lines')[0].scrollHeight}, 2000);

}
