import React from 'react';
import * as THREE from 'three';
import OrbitControls from 'threejs-orbit-controls';
import io from 'socket.io-client';
import {server_ip} from "../../constants";

function invertColor(hex, bw) {
    if (hex.indexOf('#') === 0) {
        hex = hex.slice(1);
    }
    // convert 3-digit hex to 6-digits.
    if (hex.length === 3) {
        hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
    }
    if (hex.length !== 6) {
        throw new Error('Invalid HEX color.');
    }
    let r = parseInt(hex.slice(0, 2), 16),
        g = parseInt(hex.slice(2, 4), 16),
        b = parseInt(hex.slice(4, 6), 16);
    if (bw) {
        // http://stackoverflow.com/a/3943023/112731
        return (r * 0.299 + g * 0.587 + b * 0.114) > 186
            ? '#000000'
            : '#FFFFFF';
    }
    // invert color components
    r = (255 - r).toString(16);
    g = (255 - g).toString(16);
    b = (255 - b).toString(16);
    // pad each with zeros and return
    return "#" + padZero(r) + padZero(g) + padZero(b);
}

function padZero(str, len) {
    len = len || 2;
    const zeros = new Array(len).join('0');
    return (zeros + str).slice(-len);
}

function componentToHex(c) {
    const hex = c.toString(16);
    return hex.length == 1 ? "0" + hex : hex;
}

function rgbToHex(r, g, b) {
    return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}

function toColor(num) {
    num >>>= 0;
    const b = num & 0xFF,
        g = (num & 0xFF00) >>> 8,
        r = (num & 0xFF0000) >>> 16,
        a = ( (num & 0xFF000000) >>> 24 ) / 255 ;
    return rgbToHex(r, g, b);
}

function getProjectionMatrix(projectionMatrix){
    const projectionMatrixT = new THREE.Matrix4();
    projectionMatrixT.set(...projectionMatrix);
    projectionMatrixT.transpose();
    return projectionMatrixT;
}

function getPoseMatrix(poseMatrix){
    const otocena = new THREE.Matrix4();
    otocena.set(...poseMatrix);
    otocena.transpose();
    return otocena;
}

const add_android = function add_android(data){
  console.log('add android', data);
  const matrix = data.matrix;
    let pcGeometry = new THREE.Geometry();
    const p = new THREE.Vector3();
    p.x = matrix[12];
    p.y = matrix[13];
    p.z = matrix[14];

    pcGeometry.vertices.push(p);
    // const pcMaterial = new THREE.PointsMaterial( { color: 0x888888 } );
    const color = "#f4ee42";
    const pcMaterial = new THREE.PointsMaterial( { color, size: 14, sizeAttenuation: false } );
    const pcField = new THREE.Points( pcGeometry, pcMaterial );

    // var dotGeometry = new THREE.Geometry();
    // dotGeometry.vertices.push(new THREE.Vector3( 0, 0, 0));
    // var dotMaterial = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
    // var dot = new THREE.Points( dotGeometry, dotMaterial );
    this.scene.add( pcField );

    // this.androids.add(pcField);
};

const cam_pos_web = function cam_pos_web(data){
    console.log("cam pos web", data);
    const matrix = data.matrix;
    let projectionMatrix = data.matrixProjection;
    // projectionMatrix = [2.6625724, 0, 0, 0, 0, 1.497697, 0, 0, 0.0057704183, 0.0017041445, -1.0006669, -1, 0, 0, -0.020006668, 0]
    const hashCode = data.hashCode;
    const currentPos = new THREE.Vector3(matrix[12], matrix[13], matrix[14]);
    console.log('current pos', currentPos);

    if (this.phone_cam[hashCode] === undefined){
        // this.addCameraHelper();
        console.log('addcameraHelper');
        this.phone_cam[hashCode] = new THREE.PerspectiveCamera(75, this.width / this.height, 0.1, 1000);
        this.phone_cam[hashCode].matrixAutoUpdate = false;
        this.helper[hashCode] = new THREE.CameraHelper(this.phone_cam[hashCode]);
        this.helper[hashCode].name = "camerahelper" + hashCode;
        this.last[hashCode] = null;
        this.scene.add(this.helper[hashCode]);
        const newArray = this.props.hashCodes.slice();
        newArray.push(hashCode);
        this.props.onSetHashCodes(newArray);
    }

    this.phone_cam[hashCode].matrixWorldNeedsUpdate = true;
    this.phone_cam[hashCode].matrix = getPoseMatrix(matrix);
    this.phone_cam[hashCode].projectionMatrix = getProjectionMatrix(projectionMatrix);
    // phone_cam.set
    this.phone_cam[hashCode].updateMatrixWorld(true);
    if (this.last[hashCode] != null) {
        // console.log('12');
        const geometry = new THREE.Geometry();
        geometry.vertices.push(
            this.last[hashCode],
            currentPos,
        );
        const color = invertColor(toColor(hashCode));
        const materialLine = new THREE.LineBasicMaterial({color});
        const line = new THREE.Line(geometry, materialLine);
        this.scene.add(line);
    }
    this.last[hashCode] = currentPos;
    this.helper[hashCode].update();
};

const cam_pointcloud_web = function cam_pointcloud_web(data){
    console.log("cam_pointcloud_web");
    const points = data.pointcloud;
    const hashCode = data.hashCode;
    let pcGeometry = new THREE.Geometry();

    for (let i = 0; i < points.length/4;i++ ) {
        const p = new THREE.Vector3();
        p.x = points[i*4];
        p.y = points[i*4+1];
        p.z = points[i*4+2];

        this.pointcloud.push(p);
        pcGeometry.vertices.push(p);
    }
    // const pcMaterial = new THREE.PointsMaterial( { color: 0x888888 } );
    const color = toColor(hashCode);
    const pcMaterial = new THREE.PointsMaterial( { color, size: 1, sizeAttenuation: false } );
    const pcField = new THREE.Points( pcGeometry, pcMaterial );

    this.cloud.add(pcField);
    this.props.onSetPointCloud(this.pointcloud);
};

const addCameraHelper = function addCameraHelper(hashCode){
//     console.log('addcameraHelper');
//     this.phone_cam[hashCode] = new THREE.PerspectiveCamera(75, this.width / this.height, 0.1, 1000);
//     this.phone_cam[hashCode].matrixAutoUpdate = false;
//     this.helper[hashCode] = new THREE.CameraHelper(this.phone_cam[hashCode]);
//     this.helper[hashCode].name = "camerahelper" + hashCode;
//     this.last[hashCode] = null;
//     this.scene.add(this.helper[hashCode]);
//     const newArray = this.props.hashCodes.slice();
//     newArray.push(hashCode);
//     this.props.onSetHashCodes(newArray);
};

const removeCameraHelper = function removeCameraHelper(hashCode){
    this.scene.remove(this.helper[hashCode]);
    delete this.helper[hashCode];
    delete this.phone_cam[hashCode];
    delete this.last[hashCode];
};

class MainScene extends React.Component{
    constructor(props) {
        super(props);
        this.cloud = new THREE.Object3D();
        this.androids = new THREE.Object3D();
        this.pointcloud = [];
        this.last = {};
        this.cam_pos_web = cam_pos_web.bind(this);
        this.add_android = add_android.bind(this);
        this.cam_pointcloud_web = cam_pointcloud_web.bind(this);
        this.addCameraHelper = addCameraHelper.bind(this);
        this.removeCameraHelper = removeCameraHelper.bind(this);
    }

    componentDidMount(){
        const socket = io(server_ip);
        socket.on('web', function (msg) {
            console.log("asd", msg)
        });
        // const width = this.mount.clientWidth;
        // const height = this.mount.clientHeight;
        this.width = window.innerWidth;
        this.height = window.innerHeight;
        console.log("asdd", this.width, this.height);
        //ADD SCENE, CAMERA
        this.scene = new THREE.Scene();
        // this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        // this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
        // this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
        this.props.camera.fov = 75;
        this.props.camera.aspect = this.width / this.height;
        this.props.camera.near = 0.1;
        this.props.camera.far = 1000;

        console.log("camera main", this.props.camera);

        this.props.camera.position.z = 5;

        //ADD RENDERER
        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        // this.renderer.setClearColor( 0xffffff, 1 );
        this.renderer.setClearColor('#000000');
        this.renderer.setSize(this.width, this.height);
        this.mount.appendChild(this.renderer.domElement);
        //ADD CUBE
        const edge = 0.1;
        const geometry = new THREE.BoxGeometry(edge, edge, edge);
        const material = new THREE.MeshBasicMaterial({color: 0x00ff00});
        this.cube = new THREE.Mesh(geometry, material);
        // this.scene.add(this.cube);
        //ADD HELPERS
        this.phone_cam = {};
        this.helper = {};
        // console.log("didmount phone_cam", this.phone_cam);
        const axesHelper = new THREE.AxesHelper( 1 );
        this.scene.add( axesHelper );
        //ADD controls
        const controls = new OrbitControls(this.props.camera, this.renderer.domElement);
        // point cloud
        this.scene.add(this.cloud);
        this.scene.add(this.androids);

        socket.on('cam_pos_web', this.cam_pos_web);
        socket.on('add_android', this.add_android);
        socket.on('cam_pointcloud_web', this.cam_pointcloud_web);
        this.start();
    }
    componentDidUpdate(prevProps, prevState, snapshot) {
        this.cloud.visible = this.props.showPointCloud;
    }

    componentWillUnmount(){
        this.stop();
        this.mount.removeChild(this.renderer.domElement);
    };
    start = () => {
        if (!this.frameId) {
            this.frameId = requestAnimationFrame(this.animate)
        }
    };
    stop = () => {
        cancelAnimationFrame(this.frameId)
    };
    animate = () => {
        this.cube.rotation.x += 0.01;
        this.cube.rotation.y += 0.01;
        // this.helper.update();
        this.renderScene();
        this.frameId = window.requestAnimationFrame(this.animate)
    };
    renderScene = () => {
        this.renderer.render(this.scene, this.props.camera)
    };
    render(){
        return(
            <div
                // style={{ width: '100%', height: '100%' }}
                ref={(mount) => { this.mount = mount }}
            />
        )
    }
}
export default MainScene;
