import * as BZ from './vbzt.js'

// I forsee genuine instantiation and state here ... 
// or a roll into view 

let port

function init(dom, prt) {
    port = prt 
    console.log('DT INIT with dom', dom, 'and port', port)
    BZ.init(dom)
}

/* ---------------------------        ---------------------------- */
/* ------------------------- TRANSFORMS -------------------------- */
/* ---------------------------        ---------------------------- */

function writeTransform(div, tf) {
    div.style.transform = `scale(${tf.s})`
    div.style.transformOrigin = '0px 0px'
    div.style.left = `${tf.x}px`
    div.style.top = `${tf.y}px`
}

function readTransform(div) {
    // a string (there must be a better way)
    let transform = div.style.transform

    let index = transform.indexOf('scale')
    let left = transform.indexOf('(', index)
    let right = transform.indexOf(')', index)
    let s = parseFloat(transform.slice(left + 1, right))

    let x = parseFloat(div.style.left)
    let y = parseFloat(div.style.top)

    return ({
        s: s,
        x: x,
        y: y
    })
}

function reposition(div){
    div.style.left = div.x + 'px'
    div.style.top = div.y + 'px'
}

function readXY(div){
    //console.log('READXY left', div.style.left, 'top', div.style.top)
    return {x: div.style.left, y: div.style.top}
}

/* ---------------------------        ---------------------------- */
/* -------------------- WRITING DOM ELEMENTS --------------------- */
/* ---------------------------        ---------------------------- */

function writeDefDom(def, parentdom, debug) {
    // debug
    if (debug) console.log('writing for def', def)
    // a div to locate it 
    let de = document.createElement('div')
    $(de).addClass('block').attr('id', def.id)

    // more html: the title
    $(de).append($('<div>' + de.id + '</div>').addClass('blockid'))

    let title = $(de).children('.blockid').get(0)

    title.onmousedown = function(evt) {
        evt.preventDefault()
        evt.stopPropagation()

        // stop the D3 sim 
        // halting ... we don't have access to that here
        // we need to roll this into view 
        // save it for post-link world 

        //offsetX = evt.clientX - domElem.getBoundingClientRect().left
        //offsetY = evt.clientY - domElem.getBoundingClientRect().top

        function domElemMouseMove(evt) {
            // TRANSFORMS here to move div about on drag 
            evt.preventDefault()
            evt.stopPropagation()
            let ct = readTransform(de)
            ct.x += evt.movementX
            ct.y += evt.movementY
            writeTransform(de, ct)
            drawLinks(parentdom)
            // TODO rewrite redraw 
            //redrawLinks()
        }

        function rmOnMouseUp(evt) {
            // would do save of position state here 
            // TODO /\
            document.removeEventListener('mousemove', domElemMouseMove)
            document.removeEventListener('mouseup', rmOnMouseUp)
        }

        document.addEventListener('mousemove', domElemMouseMove)
        document.addEventListener('mouseup', rmOnMouseUp)
    }

    if (Object.keys(def.inputs).length > 0) {
        let idom = $('<div>').addClass('inputs')
        for (let key in def.inputs) {
            if (debug) console.log('def.inputs[key]', def.inputs[key])
            $(idom).append(writePortDom(def.inputs[key], def, 'input', parentdom, debug))
        }
        $(de).append(idom)
    }

    if (Object.keys(def.inputs).length > 0) {
        let odom = $('<div>').addClass('outputs')
        for (let key in def.outputs) {
            if (debug) console.log('def.outputs[key]', def.outputs[key])
            $(odom).append(writePortDom(def.outputs[key], def, 'output', parentdom, debug))
        }
        $(de).append(odom)
    }

    if (Object.keys(def.inputs).length > 0) {
        let sdom = $('<div>').addClass('state')
        for (let key in def.state) {
            if (debug) console.log('state dom', sdom)
            if (debug) console.log('def.state[key]', def.state[key])
            $(sdom).append(writeStateDom(def.state[key], def, debug))
        }
        $(de).append(sdom)
    }
    return de
}

// PORTS (inputs and outputs) are <li> objects with unique IDs,
// outputs hold a list of the unique id's they are connected to, for drawing 
// beziers with 
// we're close, just
/*
    - write message to manager
    - receive newlink messages from manager 
    - something for deleting links ? 
    - f it all and get to the link link 
*/
function writePortDom(port, parent, inout, bigdom, debug) {
    if (false) console.log('port dom', port, parent.id, inout)
    let dom = $('<li>' + port.name + '</li>').addClass(inout).get(0)
    dom.id = parent.id + '_' + inout + '_' + port.name
    // this goes in so that we can handily draw links
    dom.connectedTo = new Array() 
    // let ... in is OK for zero-length arrays, but for ... of throws errors 
    for(let conn in port.connections){
        let cn = port.connections[conn]
        dom.connectedTo.push('#' + cn.parentid + '_' + 'input' + '_' + cn.input)
    }
    // messy global for the potential floater 
    let floater = {}
    // the events
    function evtDrag(drag) {
        let cp = readTransform(floater)
        cp.x += drag.movementX
        cp.y += drag.movementY
        writeTransform(floater, cp)
        drawLinks(bigdom)
    }
    // startup the floater 
    dom.addEventListener('mousedown', (down) => {
        console.log('MOUSEDOWN for', port.type, port.name, down)
        // get the location to make the floater, 
        let cp = BZ.getRightHandle(dom, bigdom.subscale)
        floater = $('<div>').attr('id', 'floater').append(port.type).get(0)
        floater.style.zIndex = '1'
        bigdom.appendChild(floater)
        // 'hookup' our floater and its berth
        dom.connectedTo.push('#floater')
        // init out floater position, and put it in the dom 
        writeTransform(floater, { 
            s: bigdom.subscale, 
            x: cp.x - 80 * bigdom.subscale, 
            y: cp.y - (floater.clientHeight / 2) * bigdom.subscale
        })
        // do relative moves 
        bigdom.addEventListener('mousemove', evtDrag)
        // and delete / act when mouse comes up 
        bigdom.addEventListener('mouseup', function evtUp(up) {
            console.log('MOUSEUP ON', up.target.id)
            // recall name and parent id from id
            let str = up.target.id
            let last = str.lastIndexOf('_')
            let name = str.substring(last + 1)
            console.log('with name', name)
            let first = str.indexOf('_')
            let next = str.indexOf('_', first + 1)
            let id = str.substring(0, next)
            console.log('and id', id)
            let msg = {
                header: 'add link',
                content: [parent.id, port.name, id, name]
            }
            writeMsg(msg)
            // do things to conn, then
            // cleanup 
            bigdom.removeEventListener('mouseup', evtUp)
            bigdom.removeEventListener('mousemove', evtDrag)
            dom.connectedTo.splice(dom.connectedTo.indexOf('#floater'), 1)
            $('#floater').remove()
            drawLinks(bigdom)
        })
    })
    if (debug) console.log('port', dom)
    return dom
}

function writeStateDom(state, parent) {
    let dom = $('<li>' + state.name + '</li>').get(0)
    switch (typeof state.value) {
        case 'string':
            let strinput = $('<input>').attr('type', 'text').attr('size', 24).attr('value', state.value).get(0)
            strinput.addEventListener('change', (evt) => {
                // ask for a change,
                writeStateChangeMessage(parent.id, state, strinput.value)
                // but assert that we don't change the definition unless
                strinput.value = state.value
            })
            state.dom = strinput
            $(dom).append(strinput)
            break

        case 'number':
            let ninput = $('<input>').attr('type', 'text').attr('size', 24).attr('value', state.value.toString()).get(0)
            ninput.addEventListener('change', (evt) => {
                // ask for a change,
                writeStateChangeMessage(parent.id, state, parseFloat(ninput.value))
                // but assert that we don't change the definition unless
                ninput.value = state.value
            })
            state.dom = ninput
            $(dom).append(ninput)
            break

        case 'boolean':
            $(dom).append('<span style="float:right;">' + state.value.toString() + '</span>')
            dom.addEventListener('click', (evt) => {
                // ask for a change,
                writeStateChangeMessage(parent.id, state, !state.value)
            })
            break

        default:

            // + note on nonrec type 
            break
    }
    return dom
}


function writeStateChangeMessage(parentid, state, value) {
    let msg = {
        header: 'state change',
        content: {
            id: parentid,
            name: state.name,
            value: value
        }
    }
    writeMsg(msg)
}

function writeMsg(msg){
    console.log('div msg down', msg)
    console.log('port is', port)
    port(msg)
}

/* ---------------------------        ---------------------------- */
/* ----------------------- RENDERING LINKS ----------------------- */
/* ---------------------------        ---------------------------- */

function drawLinks(dom) {
    // from within the div
    let outputs = $(dom).children('.block').children('.outputs').children('.output')
    // clear all links 
    BZ.clear(dom)
    // and draw new ones
    for (let output of outputs) {
        // finding the children to hookup to 
        for(let conn in output.connectedTo){
            let hookup = $(dom).find(output.connectedTo[conn])
            if(hookup.length !== 1){
                // this can happen when a dependent is not loaded yet 
                console.log('missing connection')
            } else {
                let hk = hookup.get(0)
                let head = BZ.getRightHandle(output, dom.subscale)
                let tail 
                if(hk.id === 'floater'){
                    tail = BZ.getFloaterHandle(hk, dom.subscale)
                } else {
                    tail = BZ.getLeftHandle(hk, dom.subscale)
                }
                BZ.writeBezier(head, tail, output.id + hk.id, dom)
            }
        }
    }
}

export {
    init,
    readTransform,
    readXY,
    reposition,
    writeTransform,
    writeDefDom,
    drawLinks
}