include(Resources.idRelative("mtr:lib/harrys_lib_mod.js"));

var renderWindows = false;

var rawModels = ModelManager.loadPartedRawModel(Resources.manager(), Resources.idRelative("mtr:minsk/717.obj"), null);
var head = uploadPartedModels(rawModels, true, true);

var rawModels2 = ModelManager.loadPartedRawModel(Resources.manager(), Resources.idRelative("mtr:minsk/714.obj"), null);
var prom = uploadPartedModels(rawModels2, true, true);

var nameToFolderMap = {
	"141lvz": "",
	"222lvz": "",
	"222mvm": "",
	"podstekol": "",
	"22mvm": ""
}

function parseVariant(train) {
	
	let name = train.siding() == null ? "base" : train.siding().name.split('\\|')[0] + "";

	for (key in nameToFolderMap) {
		if (name == key) return name;
	}

	return "base";
}

function create(ctx, state, train) {

	initRoll(train, state);
	initRocking(train, state);

	state.postCreateFunctionCalled = false;

	state.extDoorLightTimer = 0.0;
	state.extDoorLightFlag = false;
	state.oldDoorVal = train.doorValue();
	state.extDoorLightLeftMemory = new Array(train.trainCars());
	state.extDoorLightRightMemory = new Array(train.trainCars());

	for (let i = 0; i < train.trainCars(); i++) {
		// door memory
		state.extDoorLightLeftMemory[i] = train.doorLeftOpen[i];
		state.extDoorLightRightMemory[i] = train.doorRightOpen[i];
	}

	state.rate2 = new RateLimit(0.09);
	state.rate3 = new RateLimit(0.2);
	state.tekpozctr = 0;
	state.tekpozcrn = 0;
	state.reqpozctr = 0;
	state.reqpozcrn = 0;
}


function dispose(ctx, state, train) {

}

function render(ctx, state, train) {
	//setPostcreateInfo(ctx, state, train); // train.siding() при вызове в функции create возвращает null, поэтому нужна доп. проверка.
	updateLogic(state, train);
	renderLogic(ctx, state, train);

	if (state.rate2.shouldUpdate()){
		if (state.tekpozcrn > state.reqpozcrn) state.tekpozcrn--;
		else if (state.tekpozcrn < state.reqpozcrn) state.tekpozcrn++;
	}
	if (state.rate3.shouldUpdate()){
		if (state.tekpozctr > state.reqpozctr) state.tekpozctr--;
		else if (state.tekpozctr < state.reqpozctr) state.tekpozctr++;
	}
}

function setPostcreateInfo(ctx, state, train) {
	if (!state.postCreateFunctionCalled) {
		
		if (train.siding() == null) {
			return;
		}
		if (train.siding().name.includes('|')) {
		state.infoArr = train.siding().name.split('\\|');
		state.postCreateFunctionCalled = true;
		}
	}
}

function updateLogic(state, train) {
	updateRoll(train, state, -0.5);
	updateRocking(train, state, 140, 88, 20, 27);
	//updateWipers(train, state);
}

function updateWipers(train, state) {
	let pos = train.lastCarPosition[0];
	let isRaining = MinecraftClient.worldIsRainingAt(pos);
	if (isRaining || state.wipeCoef > 0) {
		state.wipeCoef += Timing.delta() / 1.5;
		if (state.wipeCoef > 1.0) {
			state.wipeCoef = 0.0;
			state.wipeReverseMark = train.isReversed();
		}
	}
}

function renderLogic(ctx, state, train) {
	let matrices = new Matrices();
	renderHead(ctx, state, train, 0, matrices);

	for (var i = 1; i < train.trainCars() - 1; i++) {
		renderProm(ctx, state, train, i, matrices);
	}
	if (train.trainCars() > 1) {
		renderTail(ctx, state, train, train.trainCars()-1, matrices);
	}
}

function renderHead(ctx, state, train, i, matrices) {
	let name = parseVariant(train);

	let roll = state.rolls[i];
	roll = clamp(roll, -0.05, 0.05);

	let speedKmph = train.speed() * 20 * 3.6;

	let speedCoef = speedKmph > 20.0 ? 1.0 : speedKmph / 40 + 0.5;

	let rockZ = (state.rockingZCoef +  0.2 * i) % 1.0;
	let rockX = (4 * state.rockingXCoef +  0.2 * i) % 1.0;

	let totalZrotation = 1.1 * speedCoef * chaoticRotate2(rockZ) * 3 + roll;
	let totalXrotation = 1.3 * speedCoef * chaoticRotate(rockX) * 0.5;

	let doorValue = train.doorValue();

	let renderFrontLights = !train.isReversed();


	if (state.isBreaking) state.reqpozctr = -3;
	else if (state.isAccelerating) state.reqpozctr = 3;
	else state.reqpozctr = 0;
	if (speedKmph == 0) state.reqpozcrn = 3;
	else state.reqpozcrn = 0;
	// BOGIES	
	matrices.pushPose();
	ctx.drawCarModel(head["TELEGI"], i, matrices);
	matrices.popPose();

	matrices.pushPose(); // pushing for rocking and roll
	matrices.rotateZ(totalZrotation);
	matrices.rotateX(totalXrotation);

	matrices.pushPose(); // pushing for rotating by 180 deg.

	ctx.drawCarModel(state.dh.model, i, matrices);
	// EXTERIOR AND INTERIOR
	ctx.drawCarModel(head["EXT"], i, matrices);
	ctx.drawCarModel(head["INT"], i, matrices);
	ctx.drawCarModel(head["INTLAMPS"], i, matrices);
	ctx.drawCarModel(head["LAMPS"], i, matrices);
	if (name == "base") {
		ctx.drawCarModel(head["MASK_141_MVM"], i, matrices);
	} else if (name == "141lvz") {
		ctx.drawCarModel(head["MASK_141_LVZ"], i, matrices);
	} else if (name == "222lvz") {
		ctx.drawCarModel(head["MASK_222_LVZ"], i, matrices);
	} else if (name == "222mvm") {
		ctx.drawCarModel(head["MASK_222_MVM"], i, matrices);
	} else if (name == "podstekol") {
		ctx.drawCarModel(head["MASK_222_podstekol"], i, matrices);
	} else if (name == "22mvm") {
		ctx.drawCarModel(head["MASK_22_MVM"], i, matrices);
	}
	//DOORS
	renderDoorHalf(ctx, train, state, i, matrices, head["D_LF_EXT"], head["D_LF_INT"], null, 0.63, false)

	renderDoorHalf(ctx, train, state, i, matrices, head["D_LB_EXT"], head["D_LB_INT"], null, -0.63, false)

	renderDoorHalf(ctx, train, state, i, matrices, head["D_RB_EXT"], head["D_RB_INT"], null, -0.63, true)

	renderDoorHalf(ctx, train, state, i, matrices, head["D_RF_EXT"], head["D_RF_INT"], null, 0.63, true)

	//FARY
	if (renderFrontLights) {
		if (name == "base" || name == "141lvz") ctx.drawCarModel(head["FARY_141"], i, matrices);
		else if (name == "222lvz" || name == "222mvm") ctx.drawCarModel(head["FARY_222"], i, matrices);
		else if (name == "podstekol") ctx.drawCarModel(head["FARY_222_podstekol"], i, matrices);
		else if (name == "22mvm") ctx.drawCarModel(head["FARY_22"], i, matrices);
	} else {
		ctx.drawCarModel(head["BUF"], i, matrices);
	}

	matrices.popPose();

}

function renderProm(ctx, state, train, i, matrices) {
	let name = parseVariant(train);

	let roll = state.rolls[i];
	roll = clamp(roll, -0.05, 0.05);

	let speedKmph = train.speed() * 20 * 3.6;

	let speedCoef = speedKmph > 20.0 ? 1.0 : speedKmph / 40 + 0.5;

	let rockZ = (state.rockingZCoef +  0.2 * i) % 1.0;
	let rockX = (4 * state.rockingXCoef +  0.2 * i) % 1.0;

	let totalZrotation = 1.1 * speedCoef * chaoticRotate2(rockZ) * 3 + roll;
	let totalXrotation = 1.3 * speedCoef * chaoticRotate(rockX) * 0.5;

	let doorValue = train.doorValue();

	// BOGIES	
	matrices.pushPose();
	ctx.drawCarModel(prom["TELEGI"], i, matrices);
	matrices.popPose();

	matrices.pushPose(); // pushing for rocking and roll
	matrices.rotateZ(totalZrotation);
	matrices.rotateX(totalXrotation);

	// EXTERIOR AND INTERIOR
	ctx.drawCarModel(prom["EXT"], i, matrices);
	ctx.drawCarModel(prom["INT"], i, matrices);
	ctx.drawCarModel(prom["INTLAMPS"], i, matrices);
	if (name == "base") {
		ctx.drawCarModel(prom["MAP_1"], i, matrices);
	} else if (name == "141lvz") {
		ctx.drawCarModel(prom["MAP_1"], i, matrices);
	} else if (name == "222lvz") {
		ctx.drawCarModel(prom["MAP_1"], i, matrices);
	} else if (name == "222mvm") {
		ctx.drawCarModel(prom["MAP_1"], i, matrices);
	} else if (name == "podstekol") {
		ctx.drawCarModel(prom["MAP_1"], i, matrices);
	} else if (name == "22mvm") {
		ctx.drawCarModel(prom["MAP_1"], i, matrices);
	}
	//DOORS
	renderDoorHalf(ctx, train, state, i, matrices, prom["D_LF_EXT"], prom["D_LF_INT"], null, 0.63, false)

	renderDoorHalf(ctx, train, state, i, matrices, prom["D_LB_EXT"], prom["D_LB_INT"], null, -0.63, false)

	renderDoorHalf(ctx, train, state, i, matrices, prom["D_RB_EXT"], prom["D_RB_INT"], null, -0.63, true)

	renderDoorHalf(ctx, train, state, i, matrices, prom["D_RF_EXT"], prom["D_RF_INT"], null, 0.63, true)

	matrices.popPose();

}

function renderTail(ctx, state, train, i, matrices) {
	let name = parseVariant(train);

	let roll = state.rolls[i];
	roll = clamp(roll, -0.05, 0.05);

	let speedKmph = train.speed() * 20 * 3.6;

	let speedCoef = speedKmph > 20.0 ? 1.0 : speedKmph / 40 + 0.5;

	let rockZ = (state.rockingZCoef +  0.2 * i) % 1.0;
	let rockX = (4 * state.rockingXCoef +  0.2 * i) % 1.0;

	let totalZrotation = 1.1 * speedCoef * chaoticRotate2(rockZ) * 3 + roll;
	let totalXrotation = 1.3 * speedCoef * chaoticRotate(rockX) * 0.5;

	let renderFrontLights = !train.isReversed();

	let doorValue = train.doorValue();

	matrices.pushPose();

	matrices.rotateX(0.0005);
	matrices.rotateZ(totalZrotation);
	matrices.rotateY(3.1415);

	// BOGIES	
	matrices.pushPose();
	ctx.drawCarModel(head["TELEGI"], i, matrices);
	matrices.popPose();

	matrices.pushPose(); // pushing for rocking and roll
	matrices.rotateZ(totalZrotation);
	matrices.rotateX(totalXrotation);

	matrices.pushPose(); // pushing for rotating by 180 deg.


	ctx.drawCarModel(state.dh.model, i, matrices);
	// EXTERIOR AND INTERIOR
	ctx.drawCarModel(head["EXT"], i, matrices);
	ctx.drawCarModel(head["INT"], i, matrices);
	ctx.drawCarModel(head["INTLAMPS"], i, matrices);
	ctx.drawCarModel(head["LAMPS"], i, matrices);
	if (name == "base") {
		ctx.drawCarModel(head["MASK_141_MVM"], i, matrices);
	} else if (name == "141lvz") {
		ctx.drawCarModel(head["MASK_141_LVZ"], i, matrices);
	} else if (name == "222lvz") {
		ctx.drawCarModel(head["MASK_222_LVZ"], i, matrices);
	} else if (name == "222mvm") {
		ctx.drawCarModel(head["MASK_222_MVM"], i, matrices);
	} else if (name == "podstekol") {
		ctx.drawCarModel(head["MASK_222_podstekol"], i, matrices);
	} else if (name == "22mvm") {
		ctx.drawCarModel(head["MASK_22_MVM"], i, matrices);
	}
	//DOORS
	renderDoorHalf(ctx, train, state, i, matrices, head["D_LF_EXT"], head["D_LF_INT"], null, 0.63, true)

	renderDoorHalf(ctx, train, state, i, matrices, head["D_LB_EXT"], head["D_LB_INT"], null, -0.63, true)

	renderDoorHalf(ctx, train, state, i, matrices, head["D_RB_EXT"], head["D_RB_INT"], null, -0.63, false)

	renderDoorHalf(ctx, train, state, i, matrices, head["D_RF_EXT"], head["D_RF_INT"], null, 0.63, false)

	//FARY
	if (!renderFrontLights) {
		if (name == "base" || name == "141lvz") ctx.drawCarModel(head["FARY_141"], i, matrices);
		else if (name == "222lvz" || name == "222mvm") ctx.drawCarModel(head["FARY_222"], i, matrices);
		else if (name == "podstekol") ctx.drawCarModel(head["FARY_222_podstekol"], i, matrices);
		else if (name == "22mvm") ctx.drawCarModel(head["FARY_22"], i, matrices);
	} else {
		ctx.drawCarModel(head["BUF"], i, matrices);
	}

	matrices.popPose();
	matrices.popPose();

}

function wipFunc(x) {
	return 0.5 * pseudoSin(x + 0.75) + 0.5;
}



function renderDoorHalf(ctx, train, state, i, matrices, ext, int, win, x, isRight) {
	let doorValue = 0.0;
	if (isRight) {
		doorValue = Math.min(train.doorValue() * 2, 1) * train.doorRightOpen[i];
	} else {
		doorValue = Math.min(train.doorValue() * 2, 1) * train.doorLeftOpen[i];
	}

	let doorValueClamped = smoothCubic(doorValue) * x;

	matrices.pushPose();
	matrices.translate(0, 0, doorValueClamped);
	ctx.drawCarModel(ext, i, matrices);
	ctx.drawCarModel(int, i, matrices);

	if (renderWindows) {
		ctx.drawCarModel(win, i, matrices);
	}
	matrices.popPose();
}

function chaoticRotate(x) {
	return (x)*(x-1)*(x-0.1)*(x-0.6)*(x-0.8);
}

function chaoticRotate2(x) {
	return (x)*(x-1)*(x-0.15)*(x-0.5)*(x-0.8);
}

function smoothCubic(x) { //кубическая функция - в отрезке 0-1 возвращает сглаженную функцию 0-1
	return x*x*(-2*x + 3);
}