const THREE = require('three');
const ThreeBSP = require('./three-js-csg')(THREE);
const Geometry = require('./primitives/geometry').Geometry;
const work = require('webworkify-webpack');
const Promise = require('promise-polyfill').default;
//const work = undefined;
const JSONLoader = THREE.BufferGeometryLoader;

const GeometryCSG = function (hostIn) {
  //ZincGeoemtry of the main geometry
  let host = undefined;
  if (hostIn && hostIn.isGeometry)
	host = hostIn;
  let core = undefined;
  let worker = undefined;
  let onProgress = false;
  let myResolve = undefined;
  
  var createGeometryFromJSON = json => {
	  const material = host.getMorph().material.clone();
	  material.morphTargets = false;
	  const newGeometry = new Geometry();
		const JSONParser = new JSONLoader();
		const geometry = JSONParser.parse(json);
    const mesh = new THREE.Mesh(geometry.geometry, material);
	  newGeometry.geometry = mesh.geometry;
    mesh.userData = newGeometry;
	  newGeometry.setMorph(mesh);
	  return newGeometry;
  }
  
  var workerEventHandler = ev => {
	  switch (ev.data.action) {
	  	case 'message':
	        console.log(ev.data.message);
	        break;
	  	case 'result':
	    	const csg = new GeometryCSG(createGeometryFromJSON(ev.data.object));
	    	if (myResolve)
	    		myResolve(csg);
	    	myResolve = undefined;
	    	onProgress = false;
	        break;
	  	default:
	    	throw 'Cannot handle specified action.';
    }
  }
    
  var initialise = hostIn => {
	  if (work !== undefined) {
		worker = work(require.resolve('./workers/geometryCSG.worker.js'));
	  }
	  if (!worker) {
	    core = new (require('./workers/geometryCSGInternal').GeometryCSGInternal)(hostIn);
	  } else {
		if (hostIn && hostIn.isGeometry) {
		  let mesh = hostIn.getMorph();
		  let json = mesh.geometry.clone().applyMatrix(mesh.matrix).toJSON();
		  worker.addEventListener('message', function (ev) {
			  workerEventHandler(ev);
		  });
		  worker.postMessage({action: "initialise", object: json});
		}
	  }
  }
  
  this.getHostGeometry = () => {
	const tempCSG = new ThreeBSP(host.getMorph());
    return new createZincGeometry(tempCSG);
  }
  
  this.getGeometry = () => host;
  
  const createZincGeometry = csgMesh => {
		const material = host.getMorph().material.clone();
		material.morphTargets = false;
		const newMesh = csgMesh.toMesh(material);
	    const newGeometry = new Geometry();
	    newGeometry.geometry = newMesh.geometry;
	    newMesh.userData = newGeometry;
	    newGeometry.setMorph(newMesh);
	    return newGeometry;
  }
  
  this.setCSG = CSG => {
	  core.setCSG(CSG);
  } 
  
  const sendToWork = (guestGeometry, action, resolve, reject) => {
	  if (!onProgress) {
		  let mesh = guestGeometry.getMorph();
		  const json = mesh.geometry.clone().applyMatrix(mesh.matrix).toJSON();
		  myResolve = resolve;
		  onProgress = true;
		  worker.postMessage({action: action, object: json});
	  } else {
		  reject("On progress");
	  }
  }
  
  this.intersect = guestGeometry => {
	  return new Promise((resolve, reject) => {
		  if (worker) {
			  sendToWork(guestGeometry, "intersect", resolve, reject);
		  } else {
			  const result = core.intersect(guestGeometry);
			  const newCSG = new GeometryCSG(createZincGeometry(result));
			  newCSG.setCSG(result);
			  resolve(newCSG);
		  }
	  });
	};
  
  this.subtract = guestGeometry => {
	  return new Promise((resolve, reject) => {
		  if (worker) {
			  sendToWork(guestGeometry, "intersect", resolve, reject);
		  } else {
			  const result = core.subtract(guestGeometry);
			  const newCSG = new GeometryCSG(createZincGeometry(result));
			  newCSG.setCSG(result);
			  resolve(newCSG);
		  }
	  });
  }
  
  this.union = guestGeometry => {
	  return new Promise((resolve, reject) => {
		  if (worker) {
			  sendToWork(guestGeometry, "intersect", resolve, reject);
		  } else {
			  const result = core.union(guestGeometry);
			  const newCSG = new GeometryCSG(createZincGeometry(result));
			  newCSG.setCSG(result);
			  resolve(newCSG);
		  }
	  });
  }
  
  this.terminateWorker = () => {
	  if (worker)
		  worker.terminate();
  }
  
  initialise(hostIn);
};

exports.GeometryCSG = GeometryCSG;