;(function($){

	//vrbk
	jQuery.fn.vrGameBase=function(optUser){

		//options
		const optDef={
			enableGyro : true,
			camera     : {
				fov  : 45,
				near : 1,
				far  : 20000
			}
		};
		const opt=$.extend(optDef,optUser);

		//canvas
		const myCanvas=$(this);
		var cv=new Canvas();
		function Canvas(){
			this._id     =myCanvas.attr("id");
			this._box    =myCanvas.closest(".canvas_box");
			this._loader =myCanvas.closest(".canvas_box").find(".canvas_loader");
			this._start  =myCanvas.closest(".canvas_box").find(".canvas_start");
			this._ctrl   =myCanvas.closest(".canvas_box").find(".canvas_ctrl");

			this._w      =this._box.width();
			this._h      =this._box.height();
		}
		myCanvas.attr({
			'width'  : cv._w,
			'height' : cv._h
		});

		//ジャイロセンサー確認
		var isGyro=false;
		if((opt.enableGyro)&&(window.DeviceOrientationEvent)&&('ontouchstart' in window)){
			isGyro=true;
		}

		//three.js
		var renderer;
		var scene;
		var camera;
		var controls;
		var mixer;
		var clock;

		var camera_height=200;

		//DeviceOrientationControls用カメラ
		var camera_doc;
		var camera_defx;
		var camera_defy;
		var camera_defz;

		//group
		var grpWorld;
		var grpBase;
		var grpHorse;

		//loader
		var loader={};

		//timer
		var timerLoading;


		///////////////////////////////////////////////////////////////////////////////////////////
		// Canvas
		///////////////////////////////////////////////////////////////////////////////////////////

		setCanvas();
		function setCanvas(){
			if(!Detector.webgl){Detector.addGetWebGLMessage();}

			renderer=new THREE.WebGLRenderer({
				canvas    : document.querySelector('#'+cv._id),
				antialias : true,
				alpha     : true
			});

			renderer.setPixelRatio(window.devicePixelRatio);
			renderer.setSize(cv._w,cv._h);

			scene=new THREE.Scene();
			grpWorld=new THREE.Group();
			grpBase=new THREE.Group();
			grpHorse=new THREE.Group();

			clock=new THREE.Clock();

			//スマホなどジャイロセンサーが有効なときはカメラを2台使う
			if(isGyro){
				//通常カメラ
				camera=new THREE.PerspectiveCamera(opt.camera.fov,cv._w/cv._h,opt.camera.near,opt.camera.far);
				camera.position.set(0,0,0.01);
				camera.lookAt(new THREE.Vector3(0,0,0));

				//ジャイロセンサーによる角度を取得する目的だけのカメラ
				camera_doc=new THREE.PerspectiveCamera(opt.camera.fov,cv._w/cv._h,opt.camera.near,opt.camera.far);
				camera_doc.position.set(0,0,0.01);
				camera_doc.lookAt(new THREE.Vector3(0,0,0));

				controls=new THREE.DeviceOrientationControls(camera_doc);
				controls.connect();

				controls.update();
				camera_defx=camera_doc.rotation.x;
				camera_defy=camera_doc.rotation.y;
				camera_defz=camera_doc.rotation.z;

			//PCなどジャイロセンサーがない場合はOrbitControlsのみ
			}else{
				camera=new THREE.PerspectiveCamera(opt.camera.fov,cv._w/cv._h,opt.camera.near,opt.camera.far);
				camera.position.set(0,0,0.01);
				camera.lookAt(new THREE.Vector3(0,0,0));
				controls=new THREE.OrbitControls(camera,renderer.domElement);

				controls.autoRotate    =false;
				controls.enableRotate  =true;
				controls.rotateSpeed   =-0.05;
				controls.enableDamping =true;
				controls.dampingFactor =0.1;
				controls.enableZoom    =false;
				controls.enablePan     =false;
			}


			///////////////////////////////////////////////////////////////////////
			// ↓テスト用3Dオブジェクト配置
			///////////////////////////////////////////////////////////////////////

			//world
			const srcWorld="./src/texture/world.jpg";
			var gmWorld=new THREE.SphereBufferGeometry(10000,60,40);
			gmWorld.scale(-1,1,1);
			var mtrWorld;

			loader[srcWorld]=false;
			var textureWorld=new THREE.TextureLoader().load(srcWorld,function(){
				loader[srcWorld]=true;
			});
			mtrWorld=new THREE.MeshBasicMaterial({
				map : textureWorld
			});
			textureWorld.minFilter=textureWorld.magFilter=THREE.LinearFilter;
			textureWorld.mapping=THREE.UVMapping;

			var world=new THREE.Mesh(gmWorld,mtrWorld);
			world.rotation.y=(180*Math.PI/180)*-1;
			grpWorld.add(world);

			//light
			var light=new THREE.AmbientLight(0xffffff);
			light.castShadow=true;
			grpWorld.add(light);
			var pointLight=new THREE.PointLight(0xFFFFFF,1.5,1000);
			pointLight.position.set(0,400,0);
			pointLight.castShadow=true;
			grpWorld.add(pointLight);

			//horse
			const srcHorse="./src/dae/uma.dae";
			loader[srcHorse]=false;
			var loaderHorse=new THREE.ColladaLoader();
			loaderHorse.options.convertUpAxis=true;
			loaderHorse.load(srcHorse,function(collada){
				var horse=collada.scene;

				horse.traverse(function(node){
					if(node.isSkinnedMesh){
						node.frustumCulled=false;
					}
				});

				mixer=new THREE.AnimationMixer(horse);
				var action=mixer.clipAction(collada.animations[0]).play();

				horse.scale.x=horse.scale.y=horse.scale.z=100;
				horse.rotation.z=(180*Math.PI/180);

				horse.position.set(0,0,0);
				horse.updateMatrix();
				grpHorse.add(horse);
				grpHorse.rotation.y=0;

				grpHorse.position.set(0,0,-800);
				grpBase.add(grpHorse);

				loader[srcHorse]=true;
			});

			var wky=camera_height*-1;
			grpBase.position.set(0,wky,0);
			grpWorld.add(grpBase);
			scene.add(grpWorld);

			///////////////////////////////////////////////////////////////////////


			//checkLoading
			var check_sec=500;
			var check_cnt=0;
			var check_limit=30000;

			checkLoading();
			function checkLoading(){
				var flag_loading=false;
				Object.keys(loader).forEach(function(key){
					if(!loader[key]){
						flag_loading=true;
					}
				},loader);

				//読み込み中
				if(flag_loading){
					timerLoading=setTimeout(function(){
						check_cnt+=check_sec;
						if(check_cnt<check_limit){
							checkLoading();
						}else{
							//エラー処理など
							alert("読み込みに失敗しました。");
						}
					},check_sec);

				//読み込み完了
				}else{
					clearTimeout(timerLoading);
					runAnimate();

					gameReset();
				}
			}

			//runAnimate
			function runAnimate(){
				var delta=clock.getDelta();
				if(mixer!==undefined){
					mixer.update(delta);
				}

				controls.update();

				if(isGyro){
					var camera_nowx=camera_doc.rotation.x;
					var camera_nowy=camera_doc.rotation.y;
					var camera_nowz=camera_doc.rotation.z;

					//Y方向のみ制御する場合
					camera.rotation.x=camera_nowx;
					camera.rotation.y=(camera_defy-camera_nowy)*-1;
					camera.rotation.z=camera_nowz;

					//全方向参照する場合
					//camera.rotation.x=(camera_defx-camera_nowx)*-1;
					//camera.rotation.y=(camera_defy-camera_nowy)*-1;
					//camera.rotation.z=(camera_defz-camera_nowz)*-1;
				}

				grpHorse.rotation.y+=0.05;

				renderer.render(scene,camera);
				requestAnimationFrame(runAnimate);
			}
		}


		///////////////////////////////////////////////////////////////////////////////////////////
		// Window Resize
		///////////////////////////////////////////////////////////////////////////////////////////

		var timerResize=false;
		$(window).on("resize",function(){
			if(timerResize!==false){
				clearTimeout(timerResize);
			}
			timerResize=setTimeout(function(){
				resizeCanvas();
			},500);
		});

		function resizeCanvas(){
			cv._w=cv._box.width();
			cv._h=cv._box.height();
			myCanvas.attr({
				'width'  : cv._w,
				'height' : cv._h
			});

			renderer.setPixelRatio(window.devicePixelRatio);
			renderer.setSize(cv._w,cv._h);
			camera.aspect=cv._w/cv._h;
			camera.updateProjectionMatrix();
		}


		///////////////////////////////////////////////////////////////////////////////////////////
		// 操作イベント
		///////////////////////////////////////////////////////////////////////////////////////////

		$("#btn_reset").on("click",function(){
			gameReset();
		});

		cv._start.on("click",function(){
			cv._start.hide();
			cv._ctrl.show();

			//START画面を挟みユーザのアクションを得る
			//スマホなどでaudioの読み込みなど、アクションが必要な処理はここで行う
		});


		///////////////////////////////////////////////////////////////////////////////////////////
		// リセット処理
		///////////////////////////////////////////////////////////////////////////////////////////

		function gameReset(){
			clearTimeout(timerLoading);
			grpHorse.rotation.y=0;

			cv._start.show();
			cv._loader.hide();
			cv._ctrl.hide();

			if(isGyro){
				controls.update();
				camera_defx=camera_doc.rotation.x;
				camera_defy=camera_doc.rotation.y;
				camera_defz=camera_doc.rotation.z;
			}
			camera.position.set(0,0,0.01);
			camera.lookAt(new THREE.Vector3(0,0,0));
		}

	};

})(jQuery);