
import { Head } from "./Head";
import { PlayerPart } from "./PlayerPart";
import {getfactor} from "../utils"
enum PlayerStat {
	RELEASE,
	RISING,
	FALL,
}

const debug = false;

export class Player extends engine.EventDispatcher{
    partMass = 0.1;
	footMass = 0.1;
	shouldersDistance = 50 / getfactor();
	speed = 30;

	skin = '';

	world: p2.World;

	originPos: any = {};

	leftFoot: p2.Body;
	lowerLeftLeg: p2.Body;
	upperLeftLeg: p2.Body;
	rightFoot: p2.Body;
	lowerRightLeg: p2.Body;
	upperRightLeg: p2.Body;
	body: p2.Body;
	head: p2.Body;
	upperLeftArm: p2.Body;
	lowerLeftArm: p2.Body;
	upperRightArm: p2.Body;
	lowerRightArm: p2.Body;

	leftFootGroundCons: p2.RevoluteConstraint;
	rightFootGroundCons: p2.RevoluteConstraint;

	foots: p2.Body[];
	lowerLegs: p2.Body[];
	upperLegs: p2.Body[];
	footGroundCons: p2.RevoluteConstraint[];

	state: PlayerStat;
	step;

    safePos = 0;
    setup(options) {
		const {collisionGroup, collisionMask, world, ground} = options;

		this.world = world;

		world.on('postStep', this.onPostStep);

		const bodyPartShapes = [];

		const leftFootDisplay = this.createDisplay('foot');
		const lowerLeftLegDisplay = this.createDisplay('lower_left_leg');
		const upperLeftLegDisplay = this.createDisplay('upper_left_leg');
		const rightFootDisplay = this.createDisplay('foot');
		const lowerRightLegDisplay = this.createDisplay('lower_right_leg');
		const upperRightLegDisplay = this.createDisplay('upper_right_leg');
		const bodyDisplay = this.createDisplay('body', {x:50, y: 0});
		const headDisplay = new Head();
		const upperLeftArmDisplay = this.createDisplay('upper_left_arm');
		const lowerLeftArmDisplay = this.createDisplay('lower_left_arm', {x: -20, y: 0});
		const upperRightArmDisplay = this.createDisplay('upper_right_arm');
		const lowerRightArmDisplay = this.createDisplay('lower_right_arm', {x: -39, y: 0});

		//left leg
		const leftFootShape = new p2.Box(leftFootDisplay.fSize);
        bodyPartShapes.push(leftFootShape);

		this.originPos['leftFoot'] = [
			-this.shouldersDistance / 2,
			leftFootDisplay.fHeight / 2
		];
		const leftFoot = this.leftFoot = new p2.Body({
			allowSleep: false,
			mass: this.footMass,
			position: this.originPos['leftFoot'],
		});
		if (!debug) leftFoot.displays = [leftFootDisplay];
		leftFoot.addShape(leftFootShape);

		const lowerLeftLegShape = new p2.Box(lowerLeftLegDisplay.fSize);
		bodyPartShapes.push(lowerLeftLegShape);
		this.originPos['lowerLeftLeg'] = [
			-this.shouldersDistance / 2,
			leftFoot.position[1] + lowerLeftLegDisplay.fHeight / 2 + 5 / getfactor()
		];
		const lowerLeftLeg = this.lowerLeftLeg = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['lowerLeftLeg'],
		});
		if (!debug) lowerLeftLeg.displays = [lowerLeftLegDisplay];
		lowerLeftLeg.addShape(lowerLeftLegShape);

		const leftAnkleJoint = new p2.RevoluteConstraint(lowerLeftLeg, leftFoot, {
			localPivotA: [0, -lowerLeftLegDisplay.fHeight / 2 + 5 / getfactor()],
			localPivotB: [-leftFootDisplay.fWidth / 2 + 18 / getfactor(), 0],
		});
		leftAnkleJoint.setLimits(-Math.PI / 3, Math.PI / 3);

		const leftFootGroundCons = this.leftFootGroundCons = new p2.RevoluteConstraint(leftFoot, ground, {
			localPivotA: [0, -leftFootDisplay.fHeight / 2],
			localPivotB: [leftFoot.position[0], 0],
		});
		leftFootGroundCons.setLimits(0, 0);

		const upperLeftLegShape = new p2.Box(upperLeftLegDisplay.fSize);
		bodyPartShapes.push(upperLeftLegShape);
		this.originPos['upperLeftLeg'] = [
			-this.shouldersDistance / 2,
			lowerLeftLeg.position[1] + lowerLeftLegDisplay.fHeight / 2 + upperLeftLegDisplay.fHeight / 2 - 40 / getfactor()
		];
		const upperLeftLeg = this.upperLeftLeg = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['upperLeftLeg'],
		});
		if (!debug) upperLeftLeg.displays = [upperLeftLegDisplay];
		upperLeftLeg.addShape(upperLeftLegShape);

		const leftKneeJoint = new p2.RevoluteConstraint(upperLeftLeg, lowerLeftLeg, {
			localPivotA: [0, -upperLeftLegDisplay.fHeight / 2 + 20 / getfactor()],
			localPivotB: [0, lowerLeftLegDisplay.fHeight / 2 - 20 / getfactor()],
		});
		leftKneeJoint.setLimits(-Math.PI, 0);

		//right leg
		const rightFootShape = new p2.Box(rightFootDisplay.fSize);
		bodyPartShapes.push(rightFootShape);
		this.originPos['rightFoot'] = [
			this.shouldersDistance / 2,
			rightFootDisplay.fHeight / 2
		];
		const rightFoot = this.rightFoot = new p2.Body({
			allowSleep: false,
			mass: this.footMass,
			position: this.originPos['rightFoot'],
		});
		if (!debug) rightFoot.displays = [rightFootDisplay];
		rightFoot.addShape(rightFootShape);

		const lowerRightLegShape = new p2.Box(lowerRightLegDisplay.fSize);
		bodyPartShapes.push(lowerRightLegShape);
		this.originPos['lowerRightLeg'] = [
			this.shouldersDistance / 2,
			rightFoot.position[1] + lowerRightLegDisplay.fHeight / 2 + 5 / getfactor()
		];
		const lowerRightLeg = this.lowerRightLeg = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['lowerRightLeg'],
		});
		if (!debug) lowerRightLeg.displays = [lowerRightLegDisplay];
		lowerRightLeg.addShape(lowerRightLegShape);

		const rightAnkleJoint = new p2.RevoluteConstraint(lowerRightLeg, rightFoot, {
			localPivotA: [0, -lowerRightLegDisplay.fHeight / 2 + 5 / getfactor()],
			localPivotB: [-rightFootDisplay.fWidth / 2 + 18 / getfactor(), 0],
		});
		rightAnkleJoint.setLimits(-Math.PI / 3, Math.PI / 3);

		const rightFootGroundCons = this.rightFootGroundCons = new p2.RevoluteConstraint(rightFoot, ground, {
			localPivotA: [0, -rightFootDisplay.fHeight / 2],
			localPivotB: [rightFoot.position[0], 0],
		});
		rightFootGroundCons.setLimits(0, 0);

		const upperRightLegShape = new p2.Box(upperRightLegDisplay.fSize);
		bodyPartShapes.push(upperRightLegShape);
		this.originPos['upperRightLeg'] = [
			this.shouldersDistance / 2,
			lowerRightLeg.position[1] + lowerRightLegDisplay.fHeight / 2 + upperRightLegDisplay.fHeight / 2 - 40 / getfactor()
		];
		const upperRightLeg = this.upperRightLeg = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['upperRightLeg'],
		});
		if (!debug) upperRightLeg.displays = [upperRightLegDisplay];
		upperRightLeg.addShape(upperRightLegShape);

		const rightKneeJoint = new p2.RevoluteConstraint(upperRightLeg, lowerRightLeg, {
			localPivotA: [0, -upperRightLegDisplay.fHeight / 2 + 20 / getfactor()],
			localPivotB: [0, lowerRightLegDisplay.fHeight / 2 - 20 / getfactor()],
		});
		rightKneeJoint.setLimits(-Math.PI, 0);

		//body
		var bodyWidth = 120 / getfactor();
		var bodyHeight = 250 / getfactor();
		const bodyShape = new p2.Box({width: bodyWidth, height: bodyHeight});
		bodyPartShapes.push(bodyShape);
		this.originPos['body'] = [
			0,
			upperRightLeg.position[1] + upperRightLegDisplay.fHeight / 2 + bodyHeight / 2 - 60 / getfactor()
		];
		const body = this.body = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['body'],
		});
		if (!debug) body.displays = [bodyDisplay];
		body.addShape(bodyShape);

		const leftHipJoint = new p2.RevoluteConstraint(body, upperLeftLeg, {
			localPivotA: [-20 / getfactor(), -bodyHeight / 2 + 30 / getfactor()],
			localPivotB: [0, upperLeftLegDisplay.fHeight / 2 - 30 / getfactor()],
		});
		leftHipJoint.setLimits(-Math.PI / 6 * 2, Math.PI / 6 * 2);

		const rightHipJoint = new p2.RevoluteConstraint(body, upperRightLeg, {
			localPivotA: [-20 / getfactor(), -bodyHeight / 2 + 30 / getfactor()],
			localPivotB: [0, upperRightLegDisplay.fHeight / 2 - 30 / getfactor()],
		});
		rightHipJoint.setLimits(-Math.PI / 6 * 2, Math.PI / 6 * 2);

		//head
		const headShape = new p2.Circle({radius: 85 / getfactor()});
        bodyPartShapes.push(headShape);
         //
		this.originPos['head'] = [
            0,
			body.position[1] + bodyHeight / 2 + 85 / getfactor()
		];
		const head = this.head = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['head'],
		});
		head.addShape(headShape);
		if (!debug) head.displays = [headDisplay];

		const headJoint = new p2.RevoluteConstraint(body, head, {
			localPivotA: [0, bodyHeight / 2 - 10 / getfactor()],
			localPivotB: [0, (-85 + 10) / getfactor()],
		});
		headJoint.setLimits(-Math.PI / 6 * 2, Math.PI / 6 * 2);

		//left arm
		const upperLeftArmShape = new p2.Box(upperLeftArmDisplay.fSize);
		bodyPartShapes.push(upperLeftArmShape);
		this.originPos['upperLeftArm'] = [
			-this.shouldersDistance / 2 - 10 / getfactor(),
			body.position[1] + bodyHeight / 2 - upperLeftArmDisplay.fHeight / 2
		];
		const upperLeftArm = this.upperLeftArm = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['upperLeftArm'],
		});
		if (!debug) upperLeftArm.displays = [upperLeftArmDisplay];
		upperLeftArm.addShape(upperLeftArmShape);

		const leftShoulderJoint = new p2.RevoluteConstraint(body, upperLeftArm, {
			localPivotA: [-this.shouldersDistance / 2 + 10 / getfactor(), bodyHeight / 2],
			localPivotB: [0, upperLeftArmDisplay.fHeight / 2],
		});
		leftShoulderJoint.setLimits(-Math.PI / 2, 0);

		var width = 50 / getfactor();
		const lowerLeftArmShape = new p2.Box({width, height: lowerLeftArmDisplay.fHeight});
		bodyPartShapes.push(lowerLeftArmShape);
		this.originPos['lowerLeftArm'] = [
			-this.shouldersDistance / 2 - 10 / getfactor() + 10 / getfactor(),
			upperLeftArm.position[1] - upperLeftArmDisplay.fHeight / 2 - lowerLeftArmDisplay.fHeight / 2 + 20 / getfactor()
		];
		const lowerLeftArm = this.lowerLeftArm = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['lowerLeftArm'],
		});
		if (!debug) lowerLeftArm.displays = [lowerLeftArmDisplay];
		lowerLeftArm.addShape(lowerLeftArmShape);

		const leftElbowJoint = new p2.RevoluteConstraint(upperLeftArm, lowerLeftArm, {
			localPivotA: [0, -upperLeftArmDisplay.fHeight / 2 + 10 / getfactor()],
			localPivotB: [-10 / getfactor(), lowerLeftLegDisplay.fHeight / 2 - 10 / getfactor()],
		});
		leftElbowJoint.setLimits(0, Math.PI * 2 / 3);

		//right arm
		const upperRightArmShape = new p2.Box(upperRightArmDisplay.fSize);
		bodyPartShapes.push(upperRightArmShape);
		this.originPos['upperRightArm'] = [
			this.shouldersDistance / 2,
			body.position[1] + bodyHeight / 2 - upperRightArmDisplay.fHeight / 2
		];
		const upperRightArm = this.upperRightArm = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['upperRightArm'],
		});
		if (!debug) upperRightArm.displays = [upperRightArmDisplay];
		upperRightArm.addShape(upperRightArmShape);

		const rightShoulderJoint = new p2.RevoluteConstraint(body, upperRightArm, {
			localPivotA: [this.shouldersDistance / 2 + 10 / getfactor(), bodyHeight / 2],
			localPivotB: [0, upperRightArmDisplay.fHeight / 2],
		});
		rightShoulderJoint.setLimits(-Math.PI / 2, 0);

		var width = 30 / getfactor();
		const lowerRightArmShape = new p2.Box({width, height: lowerRightArmDisplay.fHeight});
		bodyPartShapes.push(lowerRightArmShape);
		this.originPos['lowerRightArm'] = [
			this.shouldersDistance / 2,
			upperRightArm.position[1] - upperRightArmDisplay.fHeight / 2 - lowerRightArmDisplay.fHeight / 2 + 40 / getfactor()
		];
		const lowerRightArm = this.lowerRightArm = new p2.Body({
			allowSleep: false,
			mass: this.partMass,
			position: this.originPos['lowerRightArm'],
		});
		if (!debug) lowerRightArm.displays = [lowerRightArmDisplay];
		lowerRightArm.addShape(lowerRightArmShape);

		const rightElbowJoint = new p2.RevoluteConstraint(upperRightArm, lowerRightArm, {
			localPivotA: [0, -upperRightArmDisplay.fHeight / 2 + 20 / getfactor()],
			localPivotB: [0, lowerRightLegDisplay.fHeight / 2 - 20 / getfactor()],
		});
		rightElbowJoint.setLimits(0, Math.PI * 2 / 3);

		const bodies = [
			lowerRightArm, upperRightArm,
			lowerRightLeg, upperRightLeg, rightFoot,
			body,
			lowerLeftLeg, upperLeftLeg, leftFoot,
			lowerLeftArm, upperLeftArm,
			head,
		];

		bodies.forEach(body => {
			world.addBody(body);
		});

		const joints = [
			leftAnkleJoint,
			leftKneeJoint,
			rightAnkleJoint,
			rightKneeJoint,
			headJoint,
			leftHipJoint,
			rightHipJoint,
			leftShoulderJoint,
			leftElbowJoint,
			rightShoulderJoint,
			rightElbowJoint,
			leftFootGroundCons,
			rightFootGroundCons,
		];

		joints.forEach(joint => {
			world.addConstraint(joint);
		});

		for (let i = 0; i < bodyPartShapes.length; i++) {
			let s = bodyPartShapes[i];
			s.collisionGroup = collisionGroup;
			s.collisionMask = collisionMask;
		}


		this.foots = [leftFoot, rightFoot];
		this.lowerLegs = [lowerLeftLeg, lowerRightLeg];
		this.upperLegs = [upperLeftLeg, upperRightLeg];
        this.footGroundCons = [leftFootGroundCons, rightFootGroundCons];
        //this.addEventListener('move',this.moveTest,this);
    }
    // moveTest(){
	// 	console.log('移动');
	// }
    reset(revive = false) {
		if (revive) {

		} else {
			this.step = -1;
			this.safePos = 0;

			[
				'lowerLeftLeg',
				'upperLeftLeg',
				'lowerRightLeg',
				'upperRightLeg',
				'body',
				'head',
				'upperLeftArm',
				'lowerLeftArm',
				'upperRightArm',
				'lowerRightArm',
			].forEach(partName => {
				const part = this[partName];
				part.position[0] = this.safePos + this.originPos[partName][0];
			})
		}

		for (let i = 0; i < 2; i++) {
			let cons = this.footGroundCons[i];
			cons.pivotB[0] = this.foots[i].position[0] = this.safePos + (i == 0 ? -1 : 1) * this.shouldersDistance / 2;
			if (this.state == PlayerStat.FALL) {
				this.world.addConstraint(cons);
			}
		}

		this.switchFace('normal', false);
		this.setSkin('normal');
		this.state = PlayerStat.RELEASE;
	}
	createDisplay(resName, offset = null) {
        let bmp = new PlayerPart(resName);
        let anchorX = bmp.width / 2 + (offset ? offset.x : 0);
        let anchorY = bmp.height / 2 + (offset ? offset.y : 0);
        bmp.anchorTexture.set(anchorX/bmp.width,anchorY/bmp.height);
		return bmp;
    }
    
	get currentSide() {
		return this.step % 2;
	}

	get anotherSide() {
		return (this.step + 1) % 2;
    }
    onPostStep = (e = null) => {
		const side = this.currentSide;

		let footHang = this.foots[this.currentSide];
		let footFall = this.foots[this.anotherSide];

		switch (this.state) {
			case PlayerStat.FALL:

				break;
			case PlayerStat.RISING:
				let footOffset = footHang.position[0] - footFall.position[0];
				let speed = this.speed;
				this.upperLegs[side].angularVelocity = 10;
				this.upperLegs[side].velocity = [
					footOffset < 0 ? speed - 10 : speed,
					0
				];
				this.lowerLegs[side].velocity = [
					footOffset < 0 ? speed - 10 : speed,
					footOffset < 0 ? 3 : 2
				];

				this.head.velocity = [speed * 0.5, 20];

				this.dispatchEvent('move', {
					pos: (this.leftFoot.position[0] + this.rightFoot.position[0]) / 2 * getfactor(),
					bodyPos: this.body.position[0] * getfactor(),
				},true);

				if (footOffset > 50) {
					this.up();
				}
				break;
			case PlayerStat.RELEASE:
				this.head.velocity = [0, 30];
				break;
		}
    };
    onFootDown = () => {
		const pos = this.foots[this.currentSide].position[0] * getfactor();
		const mileage = Math.max(this.foots[0].position[0], this.foots[1].position[0]) * getfactor();
		this.safePos = this.foots[this.currentSide].position[0];
		this.dispatchEvent('foot_down', {
			mileage,
			pos,
			//revivePos: this.safePos * factor,
		}, true)
    };
    down() {
		if (this.state == PlayerStat.RISING) {
			return;
		}

		this.step++;

		const cons = this.footGroundCons[this.currentSide];
		this.world.removeConstraint(cons);
		this.state = PlayerStat.RISING;
    }
    up() {
		if (this.state == PlayerStat.RELEASE) {
			return;
		}

		const side = this.currentSide;
		this.foots[side].velocity = [0, 0];
		this.body.velocity = [0, 0];
		const currentFoot = this.foots[side];
		const cons = this.footGroundCons[side];

		cons.pivotB[0] = currentFoot.position[0];
		this.world.addConstraint(cons);

		this.state = PlayerStat.RELEASE;

		setTimeout(this.onFootDown, 200);
    }
    fall() {
		this.body.velocity = [0, -30];
		this.head.velocity = [-50, -50];

		this.world.removeConstraint(this.footGroundCons[0]);
		this.world.removeConstraint(this.footGroundCons[1]);
		this.foots[this.currentSide].velocity[0] = 60;
		this.foots[this.anotherSide].velocity[0] = 70;

		this.state = PlayerStat.FALL;
    }
    switchFace(type, playAni = true) {
		const headDisplay: Head = <Head>this.head.displays[0];
		headDisplay.switchFace(type, playAni);
	}
	
    setSkin(name) {
		if (this.skin == name) {
			return;
		}
		this.skin = name;

		[
			'leftFoot',
			'lowerLeftLeg',
			'upperLeftLeg',
			'rightFoot',
			'lowerRightLeg',
			'upperRightLeg',
			'body',
			'upperLeftArm',
			'lowerLeftArm',
			'upperRightArm',
			'lowerRightArm',
		].forEach(partName => {
			const part = this[partName];
			part.displays[0].setSkin(name);
		})
	}
    fade(alpha, animation = true){
		[
			'leftFoot',
			'lowerLeftLeg',
			'upperLeftLeg',
			'rightFoot',
			'lowerRightLeg',
			'upperRightLeg',
			'body',
			'head',
			'upperLeftArm',
			'lowerLeftArm',
			'upperRightArm',
			'lowerRightArm',
		].forEach(partName => {
			const display = this[partName].displays[0];
			if(animation){
				engine.Tween.get(display, null, null, true)
					.to({alpha}, 200);
			}else{
				display.alpha = alpha;
			}
		})
	}


}