const Peer = require('../../server/src/peer')
const VideoConverter = require('./lib/dist/video-converter');
const msgpack = require('msgpack5')();
const rematrix = require('rematrix');
const THREE = require('three');

let current_data = {};
let peer;

/**
 * Validates that the user is logged in by sending the token 
 */
checkIfLoggedIn = async () => {
    //     const token = window.localStorage.getItem('token')
    //     console.log(token)
    //     if(!token){
    //         console.log("You need to login")
    //         renderLogin()
    //     }else{

    //         //Check if the token is valid
    //         const response = await fetch('http://localhost:8080/auth/validation', {
    //             method: 'POST',
    //             headers: {'Authorization': token}
    //         })
    //         console.log('RESPONSE', response)
            
    //         //Token is valid, show available streams
    //         if(response.status === 200){
    //             console.log("SUCCESS")
                 renderThumbnails()

    //         }
    //     }
}

/**
 * Returns a list of available streams
 */
getAvailableStreams = async () => {
    try{
        const streamsInJson = await fetch(`./streams`);
        const streams = await streamsInJson.json();
        console.log('AVAILABLE', streams)
        return streams;
    }catch(err){
        console.log(err)
    }
}


createVideoPlayer = () => {
	const containerDiv = document.getElementById('container');
	containerDiv.innerHTML = '';
    /*containerDiv.innerHTML = `<h1>Stream from source ${current_data.uri}</h1><br>
        <button onclick="renderThumbnails(); closeStream()">Go back</button>
        <button onclick="connectToStream('${current_data.uri}')">Start Stream</button><br>
        <button onclick="webSocketTest()">WebSocket Test</button><br>
        <video id="ftlab-stream-video" width="640" height="360"></video>`;
    containerDiv.innerHTML += '<br>'
    containerDiv.innerHTML += ''*/
    createPeer();
	//connectToStream();
	window.ftlstream = new FTLStream(peer, current_data.uri, containerDiv);
}

/**
 * Creates thumbnail (image) for all available streams and adds them to div class='container'
 */
renderThumbnails = async () => {
    const thumbnails = await getAvailableStreams();
    const containerDiv = document.getElementById('container')
    containerDiv.innerHTML = '';
    containerDiv.innerHTML = `<button onClick="configs()">change configs</button>`
    containerDiv.innerHTML += `<div class="ftlab-stream-thumbnails"></div>`
    if(thumbnails.length === 0){
        containerDiv.innerHTML = `<h3>No streams running currently</h3>`
    }else{
        for(var i=0; i<thumbnails.length; i++){
            const encodedURI = encodeURIComponent(thumbnails[i])
            current_data.uri = thumbnails[i]
            try{
                const someData = await fetch(`./stream/rgb?uri=${encodedURI}`)
                if(!someData.ok){
                    throw new Error('Image not found')
                }
                const myBlob = await someData.blob();
                const objectURL = URL.createObjectURL(myBlob);
                // containerDiv.innerHTML += createCard()
                containerDiv.innerHTML += createCard(objectURL, i+4)
            }catch(err){
                console.log("Couldn't create thumbnail");
                console.log(err) 
            }
        }
    }
}


/** 
 * Method to create a single thumbnail
 */
createCard = (url, viewers) => {
    return `<div class='ftlab-card-component' >
                <img src='${url}' class="thumbnail-img" alt="Hups" width="250px"></img>
                <p>Viewers: ${viewers}</p>
                <button onclick="createVideoPlayer()">button</button>
            </div>`
}


createPeer = () => {
	// FOR PRODUCTION
	console.log("HOST", location.host);
    const ws = new WebSocket("ws://" + location.host + location.pathname);
	//const ws = new WebSocket("ws://localhost:8080")
    ws.binaryType = "arraybuffer";
    peer = new Peer(ws)
}

webSocketTest = () => {
    peer.send("update_cfg", "ftl://utu.fi#reconstruction_default/0/renderer/cool_effect", "true")    
}

function FTLStream(peer, uri, element) {
	this.uri = uri;
	this.peer = peer;

	this.current = "";
	this.current_fs = 0;
	this.current_source = 0;
	this.current_channel = 0;

	//this.elements_ = {};
	//this.converters_ = {};

	//const element = document.getElementById('ftlab-stream-video');
	this.outer = element;
	this.outer.classList.add("ftl");
	this.outer.classList.add("container");
	this.element = document.createElement("VIDEO");
	this.element.setAttribute("width", 640);
	this.element.setAttribute("height", 360);
	this.element.setAttribute("controls", true);
	this.element.style.display = "none";
	this.element.classList.add("ftl");
	this.element.id = "ftl-video-element";
	this.outer.appendChild(this.element);

	//this.player = videojs('ftl-video-element');
	//this.player.vr({projection: '360'});

	if (false) {
		this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
	} else {
		this.camera = new THREE.OrthographicCamera(window.innerWidth/-2, window.innerWidth/2, window.innerHeight/2, window.innerHeight/-2, 1, 4);
	}
	this.camera.target = new THREE.Vector3( 0, 0, 0 );

	this.scene = new THREE.Scene();

	var geometry;
	
	if (false) {
		geometry = new THREE.SphereBufferGeometry( 500, 60, 40 );
	} else {
		geometry = new THREE.PlaneGeometry(1280, 720, 32);
	}
	// invert the geometry on the x-axis so that all of the faces point inward
	geometry.scale( - 1, 1, 1 );

	var texture = new THREE.VideoTexture( this.element );
	var material = new THREE.MeshBasicMaterial( { map: texture } );

	this.mesh = new THREE.Mesh( geometry, material );

	this.scene.add( this.mesh );

	this.renderer = new THREE.WebGLRenderer();
	this.renderer.setPixelRatio( window.devicePixelRatio );
	this.renderer.setSize( window.innerWidth, window.innerHeight );
	this.outer.appendChild( this.renderer.domElement );

	var me = this;

	this.isUserInteracting = false;
	this.onPointerDownPointerX = 0;
	this.onPointerDownPointerY = 0;
	this.onPointerDownLon = 0;
	this.onPointerDownLat = 0;
	this.lon = 0;
	this.lat = 0;
	this.distance = 2.0;

	this.overlay = document.createElement("DIV");
	this.overlay.classList.add("ftl");
	this.overlay.classList.add("overlay");
	this.overlay.setAttribute("tabindex","0");
	this.outer.appendChild(this.overlay);

	this.overlay.addEventListener('mousedown', (event) => {
		event.preventDefault();

		this.isUserInteracting = true;

		this.onPointerDownPointerX = event.clientX;
		this.onPointerDownPointerY = event.clientY;

		this.onPointerDownLon = this.lon;
		this.onPointerDownLat = this.lat;
	});

	this.overlay.addEventListener('mousemove', (event) => {
		if ( this.isUserInteracting === true ) {
			//this.lon = ( this.onPointerDownPointerX - event.clientX ) * 0.1 + this.onPointerDownLon;
			//this.lat = ( this.onPointerDownPointerY - event.clientY ) * 0.1 + this.onPointerDownLat;

			this.rotationX += event.movementY * (1/25) * 5.0;
			this.rotationY -= event.movementX * (1/25) * 5.0;
			this.updatePose();
		}
	});

	this.overlay.addEventListener('mouseup', (event) => {
		this.isUserInteracting = false;
	});

	this.overlay.addEventListener('wheel', (event) => {
		event.preventDefault();
		this.distance += event.deltaY * 0.05;
		this.distance = THREE.MathUtils.clamp( this.distance, 1, 50 );
	});

	function update() {
		me.lat = Math.max( - 85, Math.min( 85, me.lat ) );
		let phi = THREE.MathUtils.degToRad( 90 - me.lat );
		let theta = THREE.MathUtils.degToRad( me.lon );

		//me.camera.position.x = me.distance * Math.sin( phi ) * Math.cos( theta );
		//me.camera.position.y = me.distance * Math.cos( phi );
		//me.camera.position.z = me.distance * Math.sin( phi ) * Math.sin( theta );

		me.camera.position.x = 0;
		me.camera.position.y = 0;
		me.camera.position.z = -2;

		me.camera.lookAt( me.camera.target );

		me.renderer.render( me.scene, me.camera );

	}

	function animate() {

		requestAnimationFrame( animate );
		update();

	}

	animate();

	this.play_button = document.createElement("BUTTON");
	this.play_button.innerHTML = "Play";
	this.play_button.classList.add("ftl");
	this.play_button.classList.add("play");
	this.play_button.onclick = () => {
		this.start(0,0,0);
	}
	this.overlay.appendChild(this.play_button);

	this.pause_button = document.createElement("BUTTON");
	this.pause_button.innerHTML = "Pause";
	this.pause_button.classList.add("ftl");
	this.pause_button.classList.add("pause");
	this.pause_button.onclick = () => {
		this.pause();
	}
	this.overlay.appendChild(this.pause_button);

	this.paused = false;

	this.overlay.addEventListener('keydown', (event) => {
		console.log(event);
		switch(event.code) {
		case "KeyW"		: this.translateZ += 0.05; this.updatePose(); break;
		case "KeyS"		: this.translateZ -= 0.05; this.updatePose(); break;
		case "KeyA"		: this.translateX -= 0.05; this.updatePose(); break;
		case "KeyD"		: this.translateX += 0.05; this.updatePose(); break;
		}
	});

	/*this.element.onmousemove = (event) => {
		console.log(event);
		if (event.buttons == 1) {
			this.rotationX += event.movementY * (1/25) * 5.0;
			this.rotationY -= event.movementX * (1/25) * 5.0;
			this.updatePose();
		}
	}*/

	this.rotationX = 0;
	this.rotationY = 0;
	this.rotationZ = 0;
	this.translateX = 0;
	this.translateY = 0;
	this.translateZ = 0;

	//this.element.onclick = () => {
		//let pose = [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];
		//this.rotation += 10;
		//let pose = rematrix.rotateZ(this.rotation);
		//this.setPose(pose);
	//}

    this.converter = null;

    let rxcount = 0;
    let ts = 0;
	let dts = 0;

    this.peer.bind(uri, (latency, streampckg, pckg) => {
		if (this.paused) return;

        if(pckg[0] === 2){  // H264 packet.
			let id = "id-"+streampckg[1]+"-"+streampckg[2]+"-"+streampckg[3];

			if (this.current == id) {
				rxcount++;
				if (rxcount >= 25) {
					rxcount = 0;
					peer.send(uri, 0, [1,0,255,0],[255,7,35,0,0,Buffer.alloc(0)]);
					//peer.send(current_data.uri, 0, [255,7,35,0,0,Buffer.alloc(0)], [1,0,255,0]);
				}
			
				if (this.converter) {
					/*function decode(value){
						this.converter.appendRawData(value);
					}
					decode(pckg[5]);*/
					if (this.converter.sourceBuffer && this.converter.sourceBuffer.mode != "sequence") {
						this.converter.sourceBuffer.mode = 'sequence';
					}
					this.converter.appendRawData(pckg[5]);
				} else {
					if (ts > 0) {
						dts = streampckg[0] - ts;
						console.log("Framerate = ", 1000/dts);
						this.converter = new VideoConverter.default(this.element, 26, 1);
						this.converter.play();
					}
					ts = streampckg[0];
				}
			}
        } else if (pckg[0] === 103) {
			//console.log(msgpack.decode(pckg[5]));
		}
	});
	
	//this.start();
	if (this.peer.status == 2) {
		this.start(0,0,0);
	} else {
		this.peer.on("connect", (p)=> {
			this.start(0,0,0);
		});
	}
}

FTLStream.prototype.pause = function() {
	this.paused = !this.paused;
	if (!this.paused) {
		this.start(0,0,0);
		this.element.play();
	} else {
		this.element.pause();
	}
}

FTLStream.prototype.updatePose = function() {
	let poseRX = rematrix.rotateX(this.rotationX);
	let poseRY = rematrix.rotateY(this.rotationY);
	let poseRZ = rematrix.rotateZ(this.rotationZ);
	let poseT = rematrix.translate3d(this.translateX, this.translateY, this.translateZ);
	let pose = [poseT,poseRX,poseRY,poseRZ].reduce(rematrix.multiply);
	this.setPose(pose);
}

FTLStream.prototype.setPose = function(pose) {
	if (pose.length != 16) {
		console.error("Invalid pose");
		return;
	}
	this.peer.send(this.uri, 0, [1, this.current_fs, this.current_source, 66],
		[103, 7, 1, 0, 0, msgpack.encode(pose)]);
}

FTLStream.prototype.start = function(fs, source, channel) {
	let id = "id-"+fs+"-"+source+"-"+channel;
	this.current = id;
	this.current_fs = fs;
	this.current_source = source;
	this.current_channel = channel;

	if (this.found) {
		this.peer.send(this.uri, 0, [1,fs,255,channel],[255,7,35,0,0,Buffer.alloc(0)]);
	} else {
		this.peer.rpc("find_stream", (res) => {
			this.found = true;
			this.peer.send(this.uri, 0, [1,fs,255,channel],[255,7,35,0,0,Buffer.alloc(0)]);
		}, this.uri);
	}
}


/*connectToStream = () => {
    const element = document.getElementById('ftlab-stream-video');
    let converter = null;

    let rxcount = 0;
    let ts = 0;
    let dts = 0;

    peer.bind(current_data.uri, (latency, streampckg, pckg) => {
        if(pckg[0] === 2){
            rxcount++;
            if (rxcount >= 25) {
                rxcount = 0;
                peer.send(current_data.uri, 0, [1,0,255,0],[255,7,35,0,0,Buffer.alloc(0)]);
                //peer.send(current_data.uri, 0, [255,7,35,0,0,Buffer.alloc(0)], [1,0,255,0]);
            }

            if (converter) {
                function decode(value){
                    converter.appendRawData(value);
                }
                decode(pckg[5]);
                converter.play();
            } else {
                if (ts > 0) {
                    dts = streampckg[0] - ts;
                    console.log("Framerate = ", 1000/dts);
                    converter = new VideoConverter.default(element, 30, 1);
                }
                ts = streampckg[0];
            }
        } else if (pckg[0] === 103) {
			console.log(msgpack.decode(pckg[5]));
		}
    })

    // Start the transaction
    //peer.send("get_stream", (current_data.uri, 30, 0, current_data.uri));

    peer.rpc("find_stream", (res) => {
        peer.send(current_data.uri, 0, [1,0,255,0],[255,7,35,0,0,Buffer.alloc(0)]);
        //peer.send(current_data.uri, [255,7,35,0,0,Buffer.alloc(0)], [1,0,255,0]);
    }, current_data.uri);
}*/

closeStream = () => {
    peer.sock.close()
}



/**
 * **************
 * CONFIGURATIONS
 * **************
 */


current_data.configURI = "ftl://utu.fi#reconstruction_snap8/net"

configs = () => {
    const container = document.getElementById("container");
    container.innerHTML = `<div class="ftlab-configurations"></div>`;
    renderConfigOptions();
}


renderConfigOptions = () => {
    const input = `<p>input1</p><br>ftl://utu.fi#<input type="text">`
    const doc = document.getElementsByClassName('ftlab-configurations')[0];
    doc.innerHTML = input;
}

/**
 * 
 */
loadConfigs = async (str) => {
    const configURI = encodeURIComponent(`ftl://utu.fi#reconstruction_snap8${str}`);
    const uri = encodeURIComponent(current_data.uri)
    const rawResp = await fetch(`./stream/config?settings=${configURI}&uri=${uri}`)
    const response = await rawResp.json();
    const content = JSON.parse(response);
    container.innerHTML += `<p>${response}</p>`;
}

// current_data.configData = '{"peers": 1}';

/**
 * Method to send configurations to backend 
 */
saveConfigs = async () => {
    let {uri, configURI, configData} = current_data
    const rawResp = await fetch('./stream/config', {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({peerURI: uri, configURI, data: configData, saveToCPP: true})
    });
    const content = await rawResp.json();
}