import { onHomeworkFinish, RandomInt, playAudioByUrl, loadDragonBones } from "../script/util";
import { defaultData } from "../script/defaultData";
import { assign, createMachine, interpret, actions, forwardTo, matchesState  } from "../script/xstate";
const { pure , send, raise, sendParent  } = actions;

class AssetCacher {
	constructor(data, progress, complete) {
		this.data = data;
		this.obj = JSON.parse(JSON.stringify(data))
		this.complete = complete;
		this.progress = progress;
	}
	_extractResource(data) {
		if (!Array.isArray(data)) {
			data = Object.values(data)
		}
		const urls = [];
		for (var item of data) {
			if (typeof item === 'string' && item.startsWith('http')) {
				urls.push(item);
				continue
			}
			const us = this._extractResource(item);
			for (const u of us) {
				urls.push(u)
			}
		}
		return urls
	}
	_replaceResource(objs, urls, assets) {
		const fn = (obj) => {
			Object.keys(obj).forEach(key => {
				const v = obj[key];
				if (typeof v == 'string' && v.startsWith('http')) {
					const i = urls.indexOf(v);
					if (i > -1) {
						obj[key] = assets[i]
					}
					return
				}
				if (v){
					this._replaceResource(v)	
				}
				
			})
			
		}
		fn(objs);
		return objs
	}
	start() {
		const urls = this._extractResource(this.data);
		cc.assetManager.loadAny(urls, null, this.progress, (err, data) => {
			const objs = [];
			for (const asset of data) {
				if (asset.constructor.name === 'ImageBitmap') {
					const t = new cc.Texture2D()
					t.initWithData(asset);
					objs.push(t);
				} else if (asset.constructor.name === 'AudioBuffer') {
					const clip = new cc.AudioClip()
					clip._nativeAsset = asset;
					objs.push(clip);
				}

			}
			const r = this._replaceResource(JSON.parse(JSON.stringify(this.data)),urls, objs)

			this.complete && this.complete();
		});
	}
}
cc.Class({

	extends: cc.Component,

	properties: {
		/*ant: {
			default: null,
			type: cc.Node,
		},
		apple: {
			default: null,
			type: cc.Node,
		},
		bird: {
			default: null,
			type: cc.Node,
		},
		car: {
			default: null,
			type: cc.Node,
		},
		cat: {
			default: null,
			type: cc.Node,
		},
		cow: {
			default: null,
			type: cc.Node,
		},
		duck: {
			default: null,
			type: cc.Node,
		},
		egg: {
			default: null,
			type: cc.Node,
		},
		farmer: {
			default: null,
			type: cc.Node,
		},
		fish: {
			default: null,
			type: cc.Node,
		},
		girl: {
			default: null,
			type: cc.Node,
		},
		hen: {
			default: null,
			type: cc.Node,
		},
		house: {
			default: null,
			type: cc.Node,
		},*/
		rightAudio: {
			default: null,
			type: cc.AudioClip,
		},
		wrongAudio: {
			default: null,
			type: cc.AudioClip,
		},

	},

	playAni(idx, name, times = 1) {

		// const cat = cc.find(`Canvas/frame-border/ground/${node}`);
		const node = this._animaNodeList[idx]
		var dragonDisplay = node.getComponent(dragonBones.ArmatureDisplay);

		const state = dragonDisplay.playAnimation(name, times);
		return state;
	},
	// 生命周期 onLoad
	onLoad() {
		this.initSceneData();
		this.initSize();
		window.ccc = this;
	},

	_imageResList: null,
	_audioResList: null,
	_animaResList: null,
	_animaNodeList : null,
	_lastFingerPosition: null,
	initSceneData() {
		this._imageResList = [];
		this._audioResList = [];
		this._animaResList = [];
		this._animaNodeList = [];
		this._lastFingerPosition= {
			pos: null,
			dirty: false
		};
	},

	_designSize: null,  // 设计分辨率
	_frameSize: null,  // 屏幕分辨率
	_mapScaleMin: null,  // 场景中常用缩放（取大值）
	_mapScaleMax: null,  // 场景中常用缩放（取小值）
	_cocosScale: null,  // cocos 自缩放 （较少用到）
	
	initSize() {
		// 注意cc.winSize只有在适配后(修改fitHeight/fitWidth后)才能获取到正确的值,因此使用cc.getFrameSize()来获取初始的屏幕大小
		let screen_size = cc.view.getFrameSize().width / cc.view.getFrameSize().height
		let design_size = cc.Canvas.instance.designResolution.width / cc.Canvas.instance.designResolution.height
		let f = screen_size >= design_size
		cc.Canvas.instance.fitHeight = f
		cc.Canvas.instance.fitWidth = !f

		const frameSize = cc.view.getFrameSize();
		this._frameSize = frameSize;

		this._designSize = cc.view.getDesignResolutionSize();

		let sx = cc.winSize.width / frameSize.width;
		let sy = cc.winSize.height / frameSize.height;
		this._cocosScale = Math.min(sx, sy);

		sx = frameSize.width / this._designSize.width;
		sy = frameSize.height / this._designSize.height;
		this._mapScaleMin = Math.min(sx, sy) * this._cocosScale;
		this._mapScaleMax = Math.max(sx, sy) * this._cocosScale;
	},
	createStateMachine(){
		const check =  [
							{ target: '#game.end', cond: (ctx, evt) => {
								console.log('[answer NEXT] check if end', ctx.questionIndex >= ctx.questionsCount - 1);
								return ctx.questionIndex >= ctx.questionsCount -1

							} },
        					{ target: 'hint' } // reenter 'hint' state
						]
		const QuestionState = {
			id: 'question',
			initial: 'hint',
			states: {
				hint: {
					invoke: {
						src: (ctx, event) => {
							console.log(`[hint] play question ${ctx.questionIndex}: ${ctx.questionPlayTimes } audio`);
							ctx.questionPlayTimes += 1;
							return new Promise((resolve,reject) => {
								playAudioByUrl(this.data.hotZoneItemArr[ctx.questionIndex].audio_url, () => {
									console.log('hint finish');
									resolve()
								})
							})
						},
						onDone: 'answer',
						onError: 'answer'
					}
				},
				answer: {
					after: {
						10000: [
							{ 
								target: '#QuestionsArray.next', 
								actions: raise('CHECK'),
								cond: (ctx, evt) => {
									console.log('[timeout check] check if end', ctx.questionPlayTimes , ctx.maxQuestionTryTimes);
									return ctx.questionPlayTimes >= ctx.maxQuestionTryTimes;
								} 
							},
        					{ target: 'retry', actions: raise('CHECK') } // reenter 'hint' state
						]
						
					},
					
					invoke: {
						/*src: (ctx) => {
							ctx.questionPlayTimes += 1;
							const rt = RandomInt(4500, 5500)
							console.log('[answer] thinking and playing', rt, rt<5000 ? 'should 1111': '');
							return new Promise((resolve,reject) => {
								setTimeout(() => {
									if(rt < 5000) {
										console.log('[answer] finish, go done', rt % 2);			
									}
									if (rt % 2 == 1) {
										resolve()
									} else {
										reject()
									}
								}, rt)
							})
						},
						onDone: {
							target: '#QuestionsArray.next',
							actions: raise('CHECK')
						},
						onError: {
							target: 'retry',
							actions: raise('CHECK')
						},*/
						src: (context, event) => (callback, onReceive) => {
							
							console.log('[answer] thinking and playing');
							// This will send the 'INC' event to the parent every second
							const id = setInterval(() => {
								const region = this.data.hotZoneItemArr[context.questionIndex];
								const result = this.checkInRegion(region.rectPercent);
								console.log('checkInRegion', region.rectPercent, result);
								if (result) {
									callback('RIGHT')
									// playAudioByUrl(this.data.begin_audio, () => {
									// 	console.log('game begin audio finish');
									// })
								} else if (this._lastFingerPosition.dirty) {
									callback('WRONG')
								}
							}, 1000);
					
							// Perform cleanup
							return () => clearInterval(id);
						},
						
					},
					on: {
						/*CHECK: {
							actions: (context, evt) => {
								const region = this.data.hotZoneItemArr[context.questionIndex];
								const result = this.checkInRegion(region.rectPercent);
								console.log('checkInRegion', region.rectPercent, result);
								if (result) {
									// forwardTo('RIGHT')
									send({type: 'RIGHT' });
								}
							}
						},*/
						/*CHECK: [
							{
								actions: send('RIGHT'), 
								cond: (context, evt) => {
									const region = this.data.hotZoneItemArr[context.questionIndex];
									const result = this.checkInRegion(region.rectPercent);
									console.log('checkInRegion', region.rectPercent, result);
									if (result) {
										// forwardTo('RIGHT')
										send({type: 'RIGHT' });
									}
								}
							},
							{actions: raise('WRONG')},
						],*/
						/*RIGHT: {
							target: '#QuestionsArray.next',
							actions: raise('CHECK')
						},
						WRONG: {
							target: 'retry',
							actions: raise('CHECK')
						}*/
						RIGHT: {
							target: 'right',
						},
						WRONG: {
							target: 'wrong',
						}
					}
				},
				
				right: {
					invoke: {
						src: (context, event) => {
							this._lastFingerPosition.pos = null;
							this._lastFingerPosition.dirty = false;
							return new Promise((resolve, reject) => {
								console.log('answer right');
								playAudioByUrl(this.rightAudio, () => {
									resolve()
								})
							});
						},
						onDone: {
							target: '#QuestionsArray.next',
							actions: raise('CHECK')
						},
						onError: {
							target: '#QuestionsArray.next',
							actions: raise('CHECK')
						}
					}
				},
				wrong:{
					invoke: {
						src: (context, event) => {
							this._lastFingerPosition.pos = null;
							this._lastFingerPosition.dirty = false;
							return new Promise((resolve, reject) => {
								console.log('answer wrong');
								playAudioByUrl(this.wrongAudio, () => {
									resolve()
								})
							});
						},
						onDone: {
							target: 'retry',
							actions: raise('CHECK')
						},
						onError: {
							target: 'retry',
							actions: raise('CHECK')
						}
					}
				},
				retry: {
					on: {
						CHECK: [
							{ target: 'hint' },
							{
								target: '#QuestionsArray.next',
								actions: raise('CHECK'),
								cond: (ctx, evt) => {
									const c = ctx.questionPlayTimes >= ctx.maxQuestionTryTimes;
									// ctx.questionPlayTimes = ctx.questionPlayTimes + 1;
									console.log('#QuestionsArray.next', c)
									return c
								}
							}
						]
					},
					
				},
				

			}
		}
		const QuestionsArrayStatus = {
			id: 'QuestionsArray',
			initial: 'play',
			states: {
				play: {
					
					...QuestionState
				},
				next: {
					on: {
						CHECK: [
							{
								target: '#game.end',
								cond: (ctx, evt) => {
									ctx.questionIndex += 1;
									ctx.questionPlayTimes = 0
									console.log('#game.end',  ctx.questionIndex >= ctx.questionsCount )
									return ctx.questionIndex >= ctx.questionsCount
								}
							},
							{target: 'play'}
						]
					
					}
				}
			}
		}
		const state = {
			id: 'game',
			initial: 'begin',
			context: {
				questionIndex: 0,
				questionPlayTimes: 0,
				maxQuestionTryTimes: 2,
				questionsCount: this.data.hotZoneItemArr.length,
			},
			states: {
				begin: {
					
					invoke: {
						src: (ctx) => {
							console.log('game begin');
							if(window.air && window.air.openOsmoFingerRead) {
								window.air.openOsmoFingerRead()
							}
							return new Promise((resolve,reject) => {
								playAudioByUrl(this.data.begin_audio, () => {
									console.log('game begin audio finish');
									resolve()
								})
							});
						},
						onDone:  'playing',
						onError: 'playing'
					}
				},
				playing: {
					entry: () => {
						console.log('entry play stage');
						cc.assetManager.loadRemote(this.data.playing_audio, (err, audioClip) => {
							cc.audioEngine.playMusic(audioClip, true, 0.8);
						});
					},
					exit: () => {
						console.log('exit play stage');
						cc.audioEngine.stopMusic();
					},
					on: {
						NEXT: 'end'
					},
					// ...QuestionsStates
					...QuestionsArrayStatus
				},
				end: {
					
						entry: (ctx, event) => {
							console.log('game end', ctx, event);
							if(window.air && window.air.closeOsmoFingerRead) {
								window.air.closeOsmoFingerRead()
							}
							playAudioByUrl(this.data.end_audio, () => {
								console.log('game finish');
							})
							// return new Promise((resolve,reject) => {
							// 	setTimeout(() => {
							// 		console.log('hint finish');
							// 		resolve()
							// 	}, 2000)
							// })
						}
					
				},
			}
		};
		const after = {};
		for(let i = 0 ;i  < this.data.hotZoneItemArr.length; i++) {
			const t = this.data.hotZoneItemArr[i];
			const k = +t.labelText * 1000;
			after[k]= {
				actions: () => {
					console.log(k)
					this.playAni(i, 'normal')
				} 
			}
		}
		state.states.begin.after = after;
		// state.states.begin.after = {
		// 	1000: { actions: () => {console.log(1000)} },
		// 	2000: { actions: () => {console.log(2000)} },
		// }

		const gameMachine = createMachine(state);
		window.gameMachine = gameMachine;
		const gameMachineService = interpret(gameMachine).onTransition((state) => {
		// console.log(1, state.value, state.context);
		});
		gameMachineService.start();
		window.gameMachineService = gameMachineService;


	},

	// 生命周期 start
	start() {
		let getData = this.getData.bind(this);
		if (window && window.courseware) {
			getData = window.courseware.getData;
		}

		getData((data) => {
			console.log('data:', data);
			this.data = data || this.getDefaultData();
			this.data = JSON.parse(JSON.stringify(this.data))
			const baseRect = this.data.bgItem.rect;
			this.data.hotZoneItemArr.forEach(block => {
				block.rectPercent = {
					x: block.rect.x / baseRect.width,
					y: block.rect.y / baseRect.height,
					width: block.rect.width / baseRect.width,
					height: block.rect.height / baseRect.height,
				}
			});
			
			console.log(this.data);
			this.preloadItem()
		});
		window.air = window.air || {}
		if (window.air) {
			window.air.osmoFingerReadCallback = (pos) => {
				if (this.gameMachineService) {
					return;
				}
				// const st = this.gameMachineService.state.value.constructor.name == 'Object' 
				// && this.gameMachineService.state.value['playing']
				// && this.gameMachineService.state.value['playing'].constructor.name == 'Object'
				// && this.gameMachineService.state.value['playing']['play']
				// && this.gameMachineService.state.value['playing']['play'] == 'answer'
				// if (st) {
				// }
				const p = JSON.parse(pos);
				this._lastFingerPosition = {
					pos:{
						x: p.x * 2,
						y: p.y
					},
					dirty: true
				}
				return
				
				let dirty = false;
				const lp = this._lastFingerPosition;
				const p2 = lp.pos;
				if (!p2) {
					lp.pos = {
						x: p.x * 2,
						y: p.y
					};
					lp.dirty = true;
					return
				}

				const detlaX = Math.abs(p2.x - p.x);
				const detlaY = Math.abs(p2.y - p.y);  
				lp.pos = {
					x: p.x * 2,
					y: p.y
				};
				if (detlaX < 0.05 && detlaY < 0.05) {
					lp.dirty = false;
				} else {
					lp.dirty = true;
				}
				
			}
		}
	},
	checkInRegion(r) {
		const p = this._lastFingerPosition ? this._lastFingerPosition.pos || {x:0, y:0} : {x:0, y:0};
		const w = p.x > r.x && p.x < (r.x + r.width);
		const h = p.y > r.y && p.y < (r.y + r.height);
		return w && h;

	},

	getData(func) {
		if (window && window.courseware) {
			window.courseware.getData(func, 'scene');
			return;
		}

		const middleLayer = cc.find('middleLayer');
		if (middleLayer) {
			const middleLayerComponent = middleLayer.getComponent('middleLayer');
			middleLayerComponent.getData(func);
			return;
		}

		func(this.getDefaultData());
	},

	getDefaultData() {
		return defaultData;
	},

	preloadItem() {
		this.addPreloadImage();
		this.addPreloadAudio();
		this.addPreloadAnima();
		this.preload();
	},


	addPreloadImage() {

		this._imageResList.push({ url: this.data.bgItem.url});
		// this._imageResList.push({ url: this.data.pic_url_2 });
		for (const q of this.data.hotZoneItemArr) {
			this._audioResList.push({ url: q.texPngData.url });
		}
	},

	addPreloadAudio() {

		this._audioResList.push({ url: this.data.begin_audio });
		this._audioResList.push({ url: this.data.playing_audio });
		this._audioResList.push({ url: this.data.end_audio });


		for (const q of this.data.hotZoneItemArr) {
			this._audioResList.push({ url: q.audio_url });
		}
	},

	addPreloadAnima() {

	},

	preload() {

		const preloadArr = this._imageResList.concat(this._audioResList).concat(this._animaResList);
		console.log(preloadArr);
		cc.assetManager.loadAny(preloadArr, null, (f, t, item) => {
			// console.log(f, t, item)
		}, (err, data) => {
			console.log(err, data);
			// for (const asset of data) {
			// 	if (asset.constructor.name === 'ImageBitmap') {
			// 		const t = new cc.Texture2D()
			// 		t.initWithData(asset);
			// 	} else if (asset.constructor.name === 'AudioBuffer') {
			// 		const clip = new cc.AudioClip()
			// 		clip._nativeAsset = asset;
			// 	}
			// }
			const ground = cc.find(`Canvas/frame-border/ground`);
			for(const zone of this.data.hotZoneItemArr) {
				const n = new cc.Node();
				n.scale = 0.25;
				const db = n.addComponent(dragonBones.ArmatureDisplay);
				this._animaNodeList.push(n);
				loadDragonBones(db, {
					tex: zone.texPngData.url, 
					atlas:  zone.texJsonData.url,
					ske: zone.skeJsonData.url,
				}).then(({width, height}) => {
					// v.armatureName = 'armature
					n.width = width;
					n.height = height;
					n.parent = ground
				});
			}

			this.loadEnd();
			if (window && window["air"] && window["air"].hideAirClassLoading) {
				window["air"].hideAirClassLoading();
			}

			cc.debug.setDisplayStats(false);
		});
	},


	loadEnd() {
		this.initData();
		this.initAudio();
		this.initView();
		// this.initListener();
		this.createStateMachine();
		
	},

	_cantouch: null,
	initData() {
		// 所有全局变量 默认都是null 
		this._cantouch = true;
	},

	audioBtn: null,
	initAudio() {
		const audioNode = cc.find('Canvas/res/audio');

		const getAudioByResName = (resName) => {
			return audioNode.getChildByName(resName).getComponent(cc.AudioSource);
		}

		this.audioBtn = getAudioByResName('btn');

	},


	initView() {

		this.initBg();
		this.initPic();
		this.initBtn();
		this.initIcon();
	},

	initBg() {
		const bgNode = cc.find('Canvas/bg');
		bgNode.scale = this._mapScaleMax;
		console.log('this._mapScaleMax', this._mapScaleMax);
	},

	pic1: null,
	pic2: null,
	initPic() {
		const canvas = cc.find('Canvas');
		const maxW = canvas.width * 0.7;

		// this.getSprNodeByUrl(this.data.pic_url, (sprNode) => {
		// 	const picNode1 = sprNode;
		// 	picNode1.scale = maxW / picNode1.width;
		// 	picNode1.baseX = picNode1.x;
		// 	canvas.addChild(picNode1);
		// 	this.pic1 = picNode1;

		// 	const labelNode = new cc.Node();
		// 	labelNode.color = cc.Color.YELLOW;
		// 	const label = labelNode.addComponent(cc.Label);
		// 	label.string = this.data.text;
		// 	label.fontSize = 60;
		// 	label.lineHeight = 60;
		// 	label.font = cc.find('Canvas/res/font/BRLNSDB').getComponent('cc.Label').font;
		// 	picNode1.addChild(labelNode);
		// });

		// this.getSprNodeByUrl(this.data.pic_url_2, (sprNode) => {
		// 	const picNode2 = sprNode;
		// 	picNode2.scale = maxW / picNode2.width;
		// 	canvas.addChild(picNode2);
		// 	picNode2.x = canvas.width;
		// 	picNode2.baseX = picNode2.x;
		// 	this.pic2 = picNode2;

		// 	const labelNode = new cc.Node();
		// 	const label = labelNode.addComponent(cc.RichText);
		// 	const size = 60
		// 	label.font = cc.find('Canvas/res/font/BRLNSDB').getComponent(cc.Label).font;
		// 	label.string = `<outline color=#751e00 width=4><size=${size}><color=#ffffff>${this.data.text}</color></size></outline>`
		// 	label.lineHeight = size;
		// 	picNode2.addChild(labelNode);
		// });

	},

	initIcon() {
		// const iconNode = this.getSprNode('icon');
		// iconNode.zIndex = 5;
		// iconNode.anchorX = 1;
		// iconNode.anchorY = 1;
		// iconNode.parent = cc.find('Canvas');
		// iconNode.x = iconNode.parent.width / 2 - 10;
		// iconNode.y = iconNode.parent.height / 2 - 10;

		// iconNode.on(cc.Node.EventType.TOUCH_START, () => {
		// 	this.playAudioByUrl(this.data.audio_url);
		// })
	},

	curPage: null,
	initBtn() {

		this.curPage = 0;
		const bottomPart = cc.find('Canvas/bottomPart');
		bottomPart.zIndex = 5; // 提高层级

		bottomPart.x = bottomPart.parent.width / 2;
		bottomPart.y = -bottomPart.parent.height / 2;

		const leftBtnNode = bottomPart.getChildByName('btn_left');
		//节点中添加了button组件 则可以添加click事件监听
		leftBtnNode.on('click', () => {
			if (!this._cantouch) {
				return;
			}
			if (this.curPage == 0) {
				return;
			}
			this.curPage = 0
			this.leftMove();

			// 游戏结束时需要调用这个方法通知系统作业完成
			onHomeworkFinish();

			cc.audioEngine.play(this.audioBtn.clip, false, 0.8)
		})

		const rightBtnNode = bottomPart.getChildByName('btn_right');
		//节点中添加了button组件 则可以添加click事件监听
		rightBtnNode.on('click', () => {
			if (!this._cantouch) {
				return;
			}
			if (this.curPage == 1) {
				return;
			}

			this.curPage = 1
			this.rightMove();

			cc.audioEngine.play(this.audioBtn.clip, false, 0.5)
		})
	},

	leftMove() {
		// this._cantouch = false;
		// const len = this.pic1.parent.width;
		// cc.tween(this.pic1)
		// 	.to(1, { x: this.pic1.baseX }, { easing: 'cubicInOut' })
		// 	.start();

		// cc.tween(this.pic2)
		// 	.to(1, { x: this.pic2.baseX }, { easing: 'cubicInOut' })
		// 	.call(() => {
		// 		this._cantouch = true;
		// 	})
		// 	.start();
	},

	rightMove() {
		// this._cantouch = false;
		// const len = this.pic1.parent.width;
		// cc.tween(this.pic1)
		// 	.to(1, { x: this.pic1.baseX - len }, { easing: 'cubicInOut' })
		// 	.start();

		// cc.tween(this.pic2)
		// 	.to(1, { x: this.pic2.baseX - len }, { easing: 'cubicInOut' })
		// 	.call(() => {
		// 		this._cantouch = true;
		// 	})
		// 	.start();
	},
	// update (dt) {},
























	// ------------------------------------------------
	getSprNode(resName) {
		const sf = cc.find('Canvas/res/img/' + resName).getComponent(cc.Sprite).spriteFrame;
		const node = new cc.Node();
		node.addComponent(cc.Sprite).spriteFrame = sf;
		return node;
	},


	getSpriteFrimeByUrl(url, cb) {
		cc.loader.load({ url }, (err, img) => {
			const spriteFrame = new cc.SpriteFrame(img)
			if (cb) {
				cb(spriteFrame);
			}
		})
	},

	getSprNodeByUrl(url, cb) {
		const node = new cc.Node();
		const spr = node.addComponent(cc.Sprite);
		this.getSpriteFrimeByUrl(url, (sf) => {
			spr.spriteFrame = sf;
			if (cb) {
				cb(node);
			}
		})
	},

	playAudioByUrl(audio_url, cb = null) {
		if (audio_url) {
			cc.assetManager.loadRemote(audio_url, (err, audioClip) => {
				const audioId = cc.audioEngine.play(audioClip, false, 0.8);
				if (cb) {
					cc.audioEngine.setFinishCallback(audioId, () => {
						cb();
					});
				}
			});
		}
	},

	// ------------------------------------------

});
