/*
 * work in progress
 * milestone demo release no build needed just change the `svgFilename` variable to any svg `path` based image
 * it means path is the only node recognised by the moment for definition another basic shaped are not considered at the moment.
 * position queries are rect-boundingboxes based by default, boundaries precision be added later
 */

import { HandRecogniser } from './hand-recogniser.js'; 
import './filereader.js';
import { log } from './logbar.js';



const { stringify } = JSON;
const { random, floor } = Math;
const doc = document;

const svgFilename = './svg/world.svg'
const fig = doc.querySelector('figure');
const video = doc.querySelector('video');


const svgPalettes = [
    {name: 'dark', fill: 'rgba(01, 10, 12, 0.75)'},
    {name: 'warm-red', fill: 'rgba(225, 78, 69, 0.75)'},
    {name: 'outer-space', fill: 'rgba(31, 40, 83, 0.75)'},
    {name: 'aqua', fill: 'rgba(69, 246, 255, 0.75)'},
    {name: 'sand', fill: 'rgba(255, 171, 69, 0.75)'}
];
let paletteIndex = -1,
    svg
;

function getPalette() {
    paletteIndex +=1 ;
    if(paletteIndex > svgPalettes.length)
        paletteIndex = 0;
    
    return svgPalettes[paletteIndex];
}



const hoverClasses = ['teal', 'orange', 'red', 'blue', 'lilah', 'purple', 'green'];

let boundsRects = Array(),
    paths = Array(),
    svgPath,
    svgPathPrev
;
	
// gesture svg connector, it finds which path is being pointed by hand gesture 
function matchPath(bounds, coord) {
    coord.x *= window.innerWidth
    coord.y *= window.innerHeight
    return _.findKey(bounds, function(item) {
        return item.left < coord.x &&
            coord.x < item.right &&
            item.top < coord.y &&
            coord.y < item.bottom
    })
}

// Remove xml file declaration and comments before append contents to document, like xml heading or unnecessary file comments
function cleanSvg(svgText) {
    const parts = svgText.split('<svg')
    return '<svg' + parts[1]
}

// apply some svg styles
function colorsSvg(svgText) {
    let palette = getPalette()
    return svgText.replace(/(fill=\"(\#[a-z0-9]+)\")/gi, `fill="${palette.fill}"`)
}

// hover/mouseover-mouseout dispatcher, it is called after Pointing_Up gesture is detected 
function dispatchHovering(result) {
    
    // index finger landmark
    // match will be the index in the collection sorted
    let lm = result.landmarks[0][8];
	
    let match = matchPath(boundsRects, lm)
    // to get the path by its originl index
    if (!match) return;
    
    if (svgPathPrev)
        svgPathPrev.dispatchEvent(new MouseEvent('mouseout'))
    
    svgPath = paths[boundsRects[match].svgIndex];
    svgPathPrev = svgPath;
    svgPath.dispatchEvent(new MouseEvent('mouseover', {
        clientX: boundsRects[match].x,
        clientY: boundsRects[match].y,
        view: window,
        bubbles: true,
        cancelable: true
    }))
    
    
}

function panToLeft() {
    svg.style.marginLeft = '-100%'
}

function panToRight() {
    svg.style.marginLeft = 0
}

function toggleColor() {
    
    let svg = fig.querySelector('svg')
    let palette = getPalette()
    log(stringify(palette))
    svg.setAttribute('fill', palette.fill)
    //alert('blink')
}

function swipe(ev) {
    if(ev.swipeX > 0)
        panToLeft()
    else
        panToRight()
    //log(stringify(ev))
}

const card = doc.querySelector('.user-card')
    const loading = card.querySelector('.loading')
    const loadingCurrent = card.querySelector('.loading-current')
    const loadingError = card.querySelector('.loading-error')
    const userTpl = card.querySelector('template')
    const cardContent = card.querySelector('.card-content')
    
    let userCardTimer;

    async function loadUser() {
        card.classList.remove('opacity-0')
        loading.classList.remove('opacity-0')
        loadingCurrent.hidden = false
        setTimeout(async () => {
            try {
                const url = navigator.onLine ? 'https://ws.devignia.site/api/user-card' : 'ws/user.json'
                const res = await fetch(url)
                if(!res.ok)
                  throw Error('server error')
                
                const user = await res.json()
                const render = Mustache.render(userTpl.innerHTML, user)
                cardContent.innerHTML = render
                loading.classList.add('opacity-0')
            } catch(e) {
                loadingCurrent.hidden = true
                loadingError.hidden = false        
                log(e)
            }
              
        }, 3000) 
        
    }
    function unloadUser() {
        card.classList.add('opacity-0')
        setTimeout(() => {
            loadingError.hidden = true
            loadingCurrent.hidden = false
        }, 1000)
        
    }

function animateCard(params) {
    log(stringify(params.rotation))
    const {x, y, z} = params.rotation

    card.style.transform = `perspective(450px) rotateX(${x}deg) rotateZ(${(90+z)*0.5}deg)` // rotateY(${-y}deg) 


}

function removeTipCircles() {
    // Remove landmarks, and offload user related
    doc.querySelectorAll('.landmarks-holdopenpalm').forEach((item) => { item.remove() })
    unloadUser()
    clearTimeout(userCardTimer)
}

async function drawTipCircles(result) {
    let landmarks = doc.querySelectorAll('.landmarks-holdopenpalm'),
        coords = result.coords
    ;
    //log(landmarks.length)
    coords.forEach((item, i) => {
        item.x *= video.clientWidth * 0.85 // ratio adjustment
        item.y *= video.clientHeight
    })
    
    animateCard(result)
    
    // Load for the first time, add landmarks, userdata and show usercard
    if(landmarks.length == 0) {
        
        
        coords.forEach((item, i) => {
            //item.x *= window.innerWidth
            //item.y *= window.innerHeight
            
            
            
            let icon = document.querySelector('.landmark-circle').cloneNode(true)
            let circle = document.createElement('div')
            circle.appendChild(icon)
            //circle.innerText = icon
            circle.classList.add('landmarks-holdopenpalm')
            circle.style.position = 'absolute'
            //circle.style.color = 'aqua'
            circle.style.top = item.y + 'px'
            circle.style.left = item.x + 'px'
            circle.style.zIndex = 10000
            document.body.appendChild(circle)
        })
        
        userCardTimer = setTimeout(loadUser, 3000)
        
    } else {
        
        // Just update landmark position / animation
        landmarks.forEach((item, i) => {
            item.style.top = coords[i].y + 'px'
            item.style.left = coords[i].x + 'px'
        })
    }
}

function setSvg(svgAsText) {
	
	const svgPlain = colorsSvg(cleanSvg(svgAsText));
    // Hopefuly transform goes in the browser
    fig.innerHTML = svgPlain
    
    // stored sorted paths in memory to query which one is being pointed
    svg = fig.querySelector('svg')
    paths = fig.querySelectorAll('path');
    paths.forEach((item, svgIndex) => {
        let bound = item.getBoundingClientRect()
        let fillHoverClass = hoverClasses[floor(hoverClasses.length * random())];
        bound.svgIndex = svgIndex
        boundsRects.push(bound);
        item.addEventListener('mouseover', (ev) => {
            ev.target.classList.add(fillHoverClass)
        })
        item.addEventListener('mouseout', (ev) => {
            ev.target.classList.remove(fillHoverClass)
        })
    })
    boundsRects = _.sortBy(boundsRects, ['left'])
}



/*
 * anon module start
 * fetches and appends svg file contents
 * stores `<path />` references in memory
 * attaches hovering listeners
 */
export default (async () => {
	
	// start with a default remote svg
    
	
    try {
        
             lucide.createIcons({
                 attrs: {
                     class: 'inline'
                 },
                 inTemplates: true
             })
        
        const res = await fetch(svgFilename);
    setSvg(await res.text())
		// Start the recogniser module with the Pointing_Up gesture, it can be used with another gestures like open palm, index finger is being referenced
		let handRecon = await HandRecogniser(video, { gesture: 'pointing_up', onrecognised: dispatchHovering, onswipe: swipe, onpantoleft: panToLeft, onpantoright: panToRight,
		    onhandblink: toggleColor,
		    
		    onholdopenpalm: drawTipCircles,
		    onholdopenpalmend: removeTipCircles
		});
		handRecon.startCamera();
		
		// attach a file reader instance to change svg as desired by user
		FileReader.mount(setSvg)
        
   
	
    }catch(e) { log(stringify(e))}
})();


// yakko.js nov 2025