import {useChartOrderStore} from "@/orderbuild.js";
import {invokeCallbacks, prototype} from "@/common.js";
import {DataFeed, initFeeDropdown, lookupSymbol} from "@/charts/datafeed.js";
import {intervalToSeconds} from "@/misc.js";

export let widget = null
export let chart = null
export let crosshairPoint = null
let symbolChangedCbs = [] // callbacks for TV's chart.onSymbolChanged()
let lastSymbolChangedArgs = null

export function addSymbolChangedCallback(cb) {
    symbolChangedCbs.push(cb)
}

export function removeSymbolChangedCallback(cb) {
    symbolChangedCbs = symbolChangedCbs.filter((i)=>i!==cb)
}

function changeSymbol(symbol) {
    const info = lookupSymbol(symbol.full_name)
    lastSymbolChangedArgs = info
    symbolChangedCbs.forEach((cb)=>cb(info))
    useChartOrderStore().selectedSymbol = info
}


function changeInterval(interval, _timeframe) {
    useChartOrderStore().intervalSecs = intervalToSeconds(interval)
}


/*  TradingView event keystrings
const subscribeEvents = [
    'toggle_sidebar', 'indicators_dialog', 'toggle_header', 'edit_object_dialog', 'chart_load_requested',
    'chart_loaded', 'mouse_down', 'mouse_up', 'drawing', 'study', 'undo', 'redo', 'undo_redo_state_changed',
    'reset_scales', 'compare_add', 'add_compare', 'load_study_template', 'onTick', 'onAutoSaveNeeded',
    'onScreenshotReady', 'onMarkClick', 'onPlusClick', 'onTimescaleMarkClick', 'onSelectedLineToolChanged',
    'layout_about_to_be_changed', 'layout_changed', 'activeChartChanged', 'series_event', 'study_event',
    'drawing_event', 'study_properties_changed', 'series_properties_changed', 'panes_height_changed',
    'panes_order_changed',
]
*/


export function initWidget(el) {
    widget = window.tvWidget = new TradingView.widget({
        library_path: "/charting_library/",
        // debug: true,
        autosize: true,
        symbol: 'default',
        interval: '1',
        container: el,
        datafeed: DataFeed, // use this for ohlc
        locale: "en",
        disabled_features: [],
        enabled_features: ['saveload_separate_drawings_storage'],
        drawings_access: {type: 'white', tools: [],}, // show no tools
    });

    // debug dump all events
    // for( const event of subscribeEvents )
    //     widget.subscribe(event, ()=>console.log('event', event, arguments))

    widget.subscribe('drawing_event', handleDrawingEvent)
    widget.subscribe('onSelectedLineToolChanged', onSelectedLineToolChanged)
    widget.subscribe('mouse_down', mouseDown)
    widget.subscribe('mouse_up', mouseUp)
    widget.headerReady().then(()=>initFeeDropdown(widget))
    widget.onChartReady(initChart)
}


function initChart() {
    console.log('init chart')
    chart = widget.activeChart()
    chart.crossHairMoved().subscribe(null, (point)=>setTimeout(()=>handleCrosshairMovement(point),0) )
    chart.onSymbolChanged().subscribe(null, changeSymbol)
    chart.onIntervalChanged().subscribe(null, changeInterval)
    changeSymbol(chart.symbolExt())
    changeInterval(widget.symbolInterval().interval)
    useChartOrderStore().chartReady = true
    console.log('chart ready')
}


// noinspection JSUnusedLocalSymbols
export const ShapeCallback = {
    onDraw: () => {},  // start drawing a new shape for the builder
    onRedraw: () => {},  // the mouse moved while in drawing mode
    onUndraw: () => {},  // drawing was canceled by clicking on a different tool
    onAddPoint: () => {},  // the user clicked a point while drawing (that point is added to the points list)
    onCreate: (shapeId, shape, points, props) => {}, // the user has finished creating all the control points.  drawing mode is exited and the initial shape is created.
    onPoints: (shapeId, shape, points) => {}, // the control points of an existing shape were changed
    onProps: (shapeId, shape, props) => {},  // the display properties of an existing shape were changed
    onMove: (shapeId, shape, points) => {},  // the entire shape was moved without dragging control points
    onDrag: (shapeId, shape, points) => {},  // the shape is being dragged: this gets called on every mouse movement update during a drag
    onHide: (shapeId, shape, props) => {},
    onShow: (shapeId, shape, props) => {},
    onClick: (shapeId, shape) => {},   // the shape was selected
    onDelete: (shapeId) => {},
}


// noinspection JSUnusedLocalSymbols
export const VerboseCallback = prototype(ShapeCallback, {
    onDraw: ()=>{console.log('onDraw')},  
    // onRedraw: ()=>{console.log('onRedraw')},
    onUndraw: ()=>{console.log('onUndraw')},
    onAddPoint: ()=>{console.log('onAddPoint')},
    onCreate: (shapeId, shape, points, props)=>{console.log('onCreate')},
    onPoints: (shapeId, shape, points)=>{console.log('onPoints')},
    onProps: (shapeId, shape, props)=>{console.log('onProps')},
    onMove: (shapeId, shape, points)=>{console.log('onMove')},
    onDrag: (shapeId, shape, points) => {console.log('onDrag')},
    onHide: (shapeId, shape, props)=>{console.log('onHide')},
    onShow: (shapeId, shape, props)=>{console.log('onShow')},
    onClick: (shapeId, shape)=>{console.log('onClick')},
    onDelete: (shapeId)=>{console.log('onDelete')},
})

let drawingTool = null
let previousDrawingTool = null

export function drawShape(shapeType, ...callbacks) {
    // puts the chart into a line-drawing mode for a new shape
    console.log('drawShape', callbacks, shapeType.name, shapeType.code)
    const co = useChartOrderStore()
    if( co.drawingCallbacks )
        invokeCallbacks(co.drawingCallbacks, 'onUndraw')
    co.drawingCallbacks = callbacks
    drawingTool = null
    previousDrawingTool = widget.selectedLineTool()
    co.drawing = true
    widget.selectLineTool(shapeType.code)
    invokeCallbacks(callbacks, 'onDraw')
}


export function createShape(shapeType, points, options={}, ...callbacks) {
    const co = useChartOrderStore()
    co.drawingCallbacks = null
    co.drawing = false
    options.shape = shapeType.code
    // programatically adds a shape to the chart
    let shapeId
    try {
        if (points.time || points.price)
            shapeId = chart.createShape(points, options)
        else if (points.length === 1)
            shapeId = chart.createShape(points[0], options)
        else
            shapeId = chart.createMultipointShape(points, options)
    }
    catch (e) {
        console.error('Could not create shape', shapeType, points, options, e)
        return null
    }
    if( callbacks.length )
        shapeCallbacks[shapeId] = callbacks
    // console.log(`created ${shapeType.name}`, shapeId)
    const shape = chart.getShapeById(shapeId)
    shape.bringToFront()
    const props = shape.getProperties()
    invokeCallbacks(callbacks, 'onCreate', shapeId, shape, points, props)
    return shapeId
}


export function cancelDrawing() {
    const co = useChartOrderStore()
    if (co.drawing) {
        co.drawing = false
        widget.selectLineTool(previousDrawingTool)
        invokeCallbacks(co.drawingCallbacks, 'onUndraw')
    }
}


const shapeCallbacks = {}

function onSelectedLineToolChanged() {
    const tool = widget.selectedLineTool();
    console.log('line tool changed', tool)
    if (drawingTool===null)
        drawingTool = tool
    else if (tool!==drawingTool && useChartOrderStore().drawing)
        cancelDrawing();
}

export let dragging = false
function mouseDown() {
    // console.log('mouseDown')
    dragging = true
}

function mouseUp() {
    // console.log('mouseUp')
    dragging = false
    draggingShapeIds = []
}


export let draggingShapeIds = []


function handleCrosshairMovement(point) {
    crosshairPoint = point
    const co = useChartOrderStore()
    if (co.drawing)
        invokeCallbacks(co.drawingCallbacks, 'onRedraw')
    else if (dragging) {
        draggingShapeIds = chart.selection().allSources();
        // console.log('dragging selected', draggingShapeIds)
        for (const shapeId of draggingShapeIds) {
            const shape = chart.getShapeById(shapeId);
            const points = shape.getPoints();
            // console.log(`dragging ${shapeId}`, shape, points1, points2, points3)
            // invokeCallbacks(shapeCallbacks[shapeId], 'onDrag', shapeId, shape, points)
            // console.log('calling onPoints')
            invokeCallbacks(shapeCallbacks[shapeId], 'onPoints', shapeId, shape, points)
        }
    }
    else if (draggingShapeIds.length > 0) {
        draggingShapeIds = []
    }
}


let drawingEventQueue = []
let eventLock = false // this is set to true before events are processed, in order to avoid any loops
let propsEvents = {}

function handleDrawingEvent(id, event) {
    if (eventLock && event !== 'properties_changed' && event !== 'remove') { // properties has its own locking mechanism
        console.log('ignoring event', id, event)
        return
    }
    // it's important to decouple our actions from the TV thread.  we must wait until the TV loop is completed
    // before interacting with the chart
    if (drawingEventQueue.length===0)
        setTimeout(drawingEventWorker,1)
    drawingEventQueue.push([id,event])
}


function drawingEventWorker() {
    eventLock = true
    try {
        const queue = drawingEventQueue
        drawingEventQueue = []
        propsEvents = {}
        for (const [id, event] of queue) {
            if (event === 'properties_changed')
                propsEvents[id] = event
            else
                doHandleDrawingEvent(id, event)
        }
        for (const [k,v] of Object.entries(propsEvents))
            doHandleDrawingEvent(k, v)
    }
    finally {
        eventLock = false
        // console.log('events unlocked')
    }
}

function doHandleDrawingEvent(id, event) {
    // console.log('drawing event', id, event)
    let shape
    if (event==='remove')
        shape = null
    else {
        try {
            shape = chart.getShapeById(id)
        }
        catch {
            return
        }
    }
    if (event === 'create') {
        const co = useChartOrderStore();
        const callbacks = co.drawingCallbacks
        // console.log('drawing callbacks', callbacks)
        if (callbacks !== null) {
            shapeCallbacks[id] = callbacks
            co.drawing = false
            const points = shape.getPoints()
            const props = shape.getProperties()
            invokeCallbacks(shapeCallbacks[id], 'onCreate', id, shape, points, props)
            invokeCallbacks(shapeCallbacks[id], 'onPoints', id, shape, points)
            invokeCallbacks(shapeCallbacks[id], 'onProps', id, shape, props)
        }
    } else if (event === 'points_changed') {
        if (id in shapeCallbacks) {
            const points = shape.getPoints()
            // console.log('points', points)
            invokeCallbacks(shapeCallbacks[id], 'onPoints', id, shape, points)
        }
    } else if (event === 'properties_changed') {
        const props = shape.getProperties()
        if (id in shapeCallbacks)
            invokeCallbacks(shapeCallbacks[id], 'onProps', id, shape, props)
        else
            // otherwise it's an event on a shape we don't "own"
            console.log('warning: ignoring setProperties on TV shape', id, props)
    } else if (event === 'move') {
        if (id in shapeCallbacks) {
            invokeCallbacks(shapeCallbacks[id], 'onMove', id, shape)
        }
    } else if (event === 'remove') {
        if (id in shapeCallbacks) {
            invokeCallbacks(shapeCallbacks[id], 'onDelete', id)
        }
    } else if (event === 'click') {
        if (id in shapeCallbacks) {
            invokeCallbacks(shapeCallbacks[id], 'onClick', id, shape)
        }
    } else if (event === 'hide') {
        if (id in shapeCallbacks) {
            invokeCallbacks(shapeCallbacks[id], 'onHide', id, shape)
        }
    } else if (event === 'show') {
        if (id in shapeCallbacks) {
            invokeCallbacks(shapeCallbacks[id], 'onShow', id, shape)
        }
    } else
        console.log('unknown drawing event', event)
}

export function deleteShapeId(id) {
    if( id in shapeCallbacks )
        delete shapeCallbacks[id]
    // console.log('removing entity', id)
    chart.removeEntity(id)
}
