import { Component } from 'react';
import * as React from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { AppState } from '../store';
import { connect } from 'react-redux';
import { Voxel, DEFAULT_MATERIAL, SWITCHED_ON_MATERIAL, SWITCHED_OFF_MATERIAL } from '../game/voxel';
import { LevelInitializer } from '../game/levels';



interface LevelPreviewProps {
    level: LevelInitializer;
    active: boolean;

}

class LevelPreview extends Component<LevelPreviewProps> {
    camera: THREE.OrthographicCamera;
    controls: OrbitControls;
    renderer: THREE.WebGLRenderer;
    scene: THREE.Scene;
    mount: HTMLElement;
    frameId: number;
    clock: THREE.Clock;

    constructor(props: LevelPreviewProps) {
        super(props);
        this.init()
    }

    init = () => {
        let width = 6;
        let height = 6;
        let w = 96;
        let h = 96;
        this.camera = new THREE.OrthographicCamera(width / - 2, width / 2, height / 2, height / - 2, 1, 30);
        this.camera.position.set(0, -1, 2);
        // this.camera.lookAt(1, 1, 0)
        this.camera.up.set(0, 0, 1);

        // RENDERER
        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(w, h);

        
        this.renderer.shadowMap.enabled = true;

        this.scene = new THREE.Scene();
        this.scene.background = new THREE.Color(0x555555);

        // LIGHTS
        this.scene.add(new THREE.HemisphereLight(0x443333, 0x111122,30));

        let pl = new THREE.PointLight(0xFFFFFFF, 100, 0,0.3);
        pl.position.set(50, -50, 10);
        this.scene.add(pl);

        let pl1 = new THREE.PointLight(0xFFFFFFF, 10, 0,0.3);
        pl1.position.set(-50, 50, 50);
        this.scene.add(pl1);
        // camera controller
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        this.controls.enabled = false;
        this.controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
        this.controls.autoRotateSpeed = 5;
        this.controls.autoRotate = true;
        this.controls.dampingFactor = 0.25;
        this.controls.screenSpacePanning = false;
        this.controls.minDistance = 8;
        this.controls.maxDistance = 13;
        // this.controls.minPolarAngle = -Math.PI;
        this.controls.maxPolarAngle = Math.PI / 2 - 0.1;


        let middle = getMeanVoxelPos(this.props.level.voxels)

        this.controls.target = new THREE.Vector3(middle.x, middle.y, 0)
        // const axesHelper = new THREE.AxesHelper(5);
        // axesHelper.position.set(middle.x, middle.y, 0)
        // this.scene.add(axesHelper);

        initScene(this.scene, this.props.level.voxels);

        this.controls.update()
        this.renderer.render(this.scene, this.camera);

        if (this.props.active) {
            this.startAnimating()
        }

    }

    animate = () => {
        this.frameId = requestAnimationFrame(this.animate);
        this.controls.update()
        this.renderer.render(this.scene, this.camera);
    }

    startAnimating = () => {
        if (!this.frameId) {
            this.frameId = requestAnimationFrame(this.animate);
        }

    }

    stopAnimating = () => {
        cancelAnimationFrame(this.frameId);
        this.frameId = 0;
    }

    componentDidMount() {
        this.mount.appendChild(this.renderer.domElement);

    }

    compoentWillUnmount() {
        this.stopAnimating()
    }

    componentDidUpdate(prevProps: LevelPreviewProps) {
        if (prevProps.active && !this.props.active) this.stopAnimating()
        if (!prevProps.active && this.props.active) this.startAnimating()
    }

    render() {
        return (<div
            className="LevelPreview"
            ref={(mount: HTMLElement) => { this.mount = mount }}

        // style={{
        //     width: "200px", height: "200px", display: "block"
        // }}
        />)
    }

}

function getMeanVoxelPos(voxels: Array<Voxel>) {
    let vsum = voxels.reduce((p, c, i, a) => { return { x: p.x + c.x, y: p.y + c.y, z: 0 } });
    return { x: vsum.x / voxels.length, y: vsum.y / voxels.length, z: 0 }
}


function initScene(scene: THREE.Scene, voxels: Array<Voxel>) {
    const h = 1;
    voxels.map((v, idx) => {
        let { x, y, z } = v
        var geometry = new THREE.BoxGeometry(h, h, h);
        // geometry.translate(-meanX, -meanY, (0.5) * h);
        geometry.translate(0, 0, (0.5) * h);

        let material = DEFAULT_MATERIAL();
        if (v.actionable) {
            switch (v.actionable.type) {
                case "BUTTON":
                    material = v.actionable.pushed ? SWITCHED_ON_MATERIAL()
                        : SWITCHED_OFF_MATERIAL();
                    break;
            }

        }
        let mesh = new THREE.Mesh(geometry, material);
        mesh.castShadow = true;
        mesh.receiveShadow = true;
        mesh.position.set(x, y, z);
        scene.add(mesh);
    })
    initGround(scene)

}

function initGround(scene: THREE.Scene) {
    const geometry = new THREE.PlaneGeometry(40, 40);

    // geometry.rotateX(Math.PI * -0.5)
    const material = new THREE.MeshPhongMaterial({
        color: 0x030211,
        specular: 0x0,
        shininess: 0x0
    });
    const plane = new THREE.Mesh(geometry, material);
    scene.add(plane)
    var grid = new THREE.GridHelper(40, 20, 0x99bbff, 0x99bbff);
    grid.rotateX(Math.PI * 0.5);
    grid.position.setX(0.5);
    grid.position.setY(0.5);
    // console.log(grid.material)
    (grid.material as THREE.LineBasicMaterial).opacity = 0.2;
    (grid.material as THREE.LineBasicMaterial).transparent = true;
    scene.add(grid);
}

const mapStateToProps = (state: AppState) => {
    return {
        modalOpen: state.gameScreenState.modalOpen
    }
}

export default connect(mapStateToProps)(LevelPreview);
