include("display.js");

var shouldRenderWindows = true;

var rawModels = ModelManager.loadPartedRawModel(Resources.manager(), Resources.idRelative("mtr:trolza/6205_gol.obj"), null);
var models = uploadPartedModels(rawModels, true, true);

var rawModels2 = ModelManager.loadPartedRawModel(Resources.manager(), Resources.idRelative("mtr:trolza/6205_uzel.obj"), null);
var models2 = uploadPartedModels(rawModels2, true, true);

var rawModels3 = ModelManager.loadPartedRawModel(Resources.manager(), Resources.idRelative("mtr:trolza/6205_prip.obj"), null);
var models3 = uploadPartedModels(rawModels3, true, true);

function create(ctx, state, train) {
	if (train.trainCars < 4) {
		print("Вагон не рендерится из-за недостатка длины")
		return;
	}

	state.wheelAngle = 0;
	state.steeringAngle = 0.0;
	state.oldFirstCarTurnForSteering = train.lastCarRotation[1].y();
	state.jointOffset = 0;

	displCreate(ctx, state, train)
}

function dispose(ctx, state, train) {
	if (train.trainCars < 4) return
	displDispose(ctx, state, train)
}

function render(ctx, state, train) {
	if (train.trainCars < 4) return
	updateLogic(state, train);
	renderLogic(ctx, state, train);
	


	displRender(ctx, state, train)
}

function updateLogic(state, train) {
	updateWheelAngle(train, state);
	updateSteeringAngle(train, state, 3.949, 1);

	updateJointScript(state, train) // КОСТЫЛИЩЕЕЕЕ
}

function renderLogic(ctx, state, train) {
	if (train.trainCars < 4) {
		return;
	}

	renderHead(ctx, state, train) 
	renderMid(ctx, state, train) 
	renderTail(ctx, state, train) 
}

function renderHead(ctx, state, train) {
	let matrices = new Matrices();
	let carIndex = 1;

	ctx.drawCarModel(models["SHELL"], carIndex, matrices);
	ctx.drawCarModel(models["SALON"], carIndex, matrices);
	ctx.drawCarModel(models["LIGHT"], carIndex, matrices);
	ctx.drawCarModel(models["SALON_LIGHT"], carIndex, matrices);
	ctx.drawCarModel(models["DOOR_LIGHT"], carIndex, matrices);

	if (shouldRenderWindows) ctx.drawCarModel(models["WINDOWS"], carIndex, matrices);

	
	{
	// WHEELS
		// front right
		const radius = 1.8;
		let steeringAngle = state.steeringAngle;
		matrices.pushPose();
		matrices.translate(-0.8484, -0.4343, 3.949);
		matrices.rotateY(steeringAngle);
		matrices.rotateX(state.wheelAngle * radius);
		matrices.translate(0.8484, 0.4343, -3.949);
	
		ctx.drawCarModel(models["WHEEL_R"], carIndex, matrices);
	
		matrices.popPose();
	
		matrices.pushPose();
		matrices.translate(0.8484, -0.4343, 3.949);
		matrices.rotateY(steeringAngle);
		matrices.rotateX(state.wheelAngle * radius);
		matrices.translate(-0.8484, 0.4343, -3.949);
	
		ctx.drawCarModel(models["WHEEL_L"], carIndex, matrices);
	
		matrices.popPose();
	
		// BACK WHEELS
		matrices.pushPose();
		matrices.translate(-0.8484, -0.4343, -2.270);
		matrices.rotateX(state.wheelAngle * radius);
		matrices.translate(0.8484, 0.4343, 2.270);
	
		ctx.drawCarModel(models["WHEEL2"], carIndex, matrices);
		
		matrices.popPose();
	}
	

	{
	// DOORS	
		// front
				let doorVal = 0.0;
		if (train.doorRightOpen[1] || train.doorRightOpen[0]) {
			if (train.isDoorOpening()) {
				doorVal = smoothCubic(clamp(train.doorValue() * 1.3, 0.0, 1.0));
			} else {
				doorVal = smoothCubic(clamp(train.doorValue() * 1.8 - 0.7, 0.0, 1.0));
			}
		}


		renderDoorWithChild(matrices, ctx, null, models["DLF_EXT"], 
			null, models["DLF_INT"], null, models["DLF_WIN"], 
			doorVal * -70, doorVal * 155, -1.166, 5.55, -1.166, 5.23, carIndex)
	
		// back
		renderDoorWithChild(matrices, ctx, null, models["DL_EXT"], 
			null, models["DL_INT"], null, models["DL_WIN"], 
			doorVal * 75, doorVal * -155, -1.166, 0.1815, -1.166, 0.492, carIndex)
	
	
		renderDoorWithChild(matrices, ctx, null, models["DR_EXT"], 
			null, models["DR_INT"], null, models["DR_WIN"], 
			doorVal * -75, doorVal * 155, -1.166, 1.578, -1.166, 1.272, carIndex)
	}
}

function renderMid(ctx, state, train) {
	let matrices = new Matrices() 
	let carIndex = 2;

	matrices.pushPose();
	matrices.translate(-state.jointOffset, 0, 0)
	ctx.drawCarModel(models2["ext"], carIndex, matrices);
	ctx.drawCarModel(models2["int"], carIndex, matrices);
	matrices.popPose();
}

function renderTail(ctx, state, train) {
	let matrices = new Matrices() 
	let carIndex = 3;
	let coef = train.lastCarRotation[3].y() % (3.1415/2)
	if (coef > 3.1415 / 4) coef = 3.1415/2 - coef;
	coef = coef / (3.1415/4);

	ctx.drawCarModel(models3["SHELL"], carIndex, matrices);
	ctx.drawCarModel(models3["SALON"], carIndex, matrices);
	ctx.drawCarModel(models3["SALON_LIGHT"], carIndex, matrices);

	ctx.drawCarModel(models3["POLE_R"], carIndex, matrices);

	matrices.pushPose();
	matrices.translate(0.2661, 0.0, 2.311);
	matrices.rotateY(coef * 0.0475);
	matrices.translate(-0.2661, 0.0, -2.311);
	
	ctx.drawCarModel(models3["POLE_L"], carIndex, matrices);

	matrices.popPose();

	ctx.drawCarModel(models3["DOOR_LIGHT"], carIndex, matrices);


	if (shouldRenderWindows ) ctx.drawCarModel(models3["WINDOWS"], carIndex, matrices);

	{
	// WHEEL
		const radius = 1.8;

		matrices.pushPose();
		matrices.translate(0, -0.4343, -0.6349);
		matrices.rotateX(state.wheelAngle * radius);
		matrices.translate(0, 0.4343, 0.6349);
	
		ctx.drawCarModel(models3["WHEEL"], carIndex, matrices);
		
		matrices.popPose();
	}	

	{
	// DOORS
		let doorVal = 0.0;
		if (train.doorRightOpen[3] || train.doorRightOpen[2]) {
			if (train.isDoorOpening()) {
				doorVal = smoothCubic(clamp(train.doorValue() * 1.3, 0.0, 1.0));
			} else {
				doorVal = smoothCubic(clamp(train.doorValue() * 1.8 - 0.7, 0.0, 1.0));
			}
		}
		
		// front
		renderDoorWithChild(matrices, ctx, null, models3["FDR"], 
			null, models3["FDRI"], null, models3["FDRW"], 
			doorVal * -75, doorVal * 155, -1.166, 3.209, -1.166, 3.209 - 0.3, carIndex)
	
	
		renderDoorWithChild(matrices, ctx, null, models3["FDL"], 
			null, models3["FDLI"], null, models3["FDLW"], 
			doorVal * 75, doorVal * -155, -1.166, 1.818, -1.166, 1.818 + 0.3, carIndex)
		// back

		renderDoorWithChild(matrices, ctx, null, models3["BDR"], 
			null, models3["BDRI"], null, models3["BDRW"], 
			doorVal * -75, doorVal * 155, -1.166, -1.395, -1.166, -1.395 - 0.3, carIndex)
	
	
		renderDoorWithChild(matrices, ctx, null, models3["BDL"], 
			null, models3["BDLI"], null, models3["BDLW"], 
			doorVal * 75, doorVal * -155, -1.166, -2.794, -1.166, -2.794 + 0.3, carIndex)
	
	}
}

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

function renderDoorWithChild(matrices, ctx, parentDoor, childDoor, parentInt, childInt, parentWindow, childIWindow, firstAngle, secondAngle, x1, z1, x2, z2, i) {
	firstAngle = firstAngle / 180.0 * 3.1415;
	secondAngle = secondAngle / 180.0 * 3.1415;

	matrices.pushPose(); // пушим родительскую матрицу в стек
	matrices.translate(x1, 0.0, z1);
	matrices.rotate(0.0, 1.0, 0.0, firstAngle); // поворот двери. Смотри самый нижний комментарий для пояснения 
	matrices.translate(-x1, 0.0, -z1);	
	{
		matrices.pushPose();	// пуш матрицы дочерней
		matrices.translate(x2, 0.0, z2);
		matrices.rotate(0.0, 1.0, 0.0, secondAngle); // поворот
		matrices.translate(-x2, 0.0, -z2);		
		if (childDoor != null) ctx.drawCarModel(childDoor, i, matrices); // рендер внутренней ширмы либо двери
		if (childInt != null) ctx.drawCarModel(childInt, i, matrices);
		if (childIWindow != null && shouldRenderWindows) ctx.drawCarModel(childIWindow, i, matrices);
		matrices.popPose(); // дочерняя матрица лопается
	}
	if (parentDoor != null) ctx.drawCarModel(parentDoor, i, matrices); // рендер крайней ширмы либо механизма двери
	if (parentInt != null) ctx.drawCarModel(parentInt, i, matrices);
	if (parentWindow != null && shouldRenderWindows) ctx.drawCarModel(parentWindow, i, matrices);
	matrices.popPose(); // родительская матрица лопается
}

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

// ПОВОРОТ ВЕДУЩИХ КОЛЕС
function updateSteeringAngle(train, state, l, i) {
	if (Timing.delta() == 0.0) return;

	let delta = getCorrectDelta(train.lastCarRotation[i].y(), state.oldFirstCarTurnForSteering)
	if (delta == 0.0) {
		state.steeringAngle = 0.0;
		state.oldFirstCarTurnForSteering = train.lastCarRotation[i].y();
		return;
	}
	const coef = 0.0625;

	let radius = train.speed() * Timing.delta() / delta;
	let sin = clamp(coef * l / radius, -1.0, 1.0);

	if (train.isReversed()) sin *= -1;

	state.steeringAngle = Math.asin(sin);
	state.oldFirstCarTurnForSteering = train.lastCarRotation[i].y();
}

function updateJointScript(state, train) {
// тут пока пусто, но возможно тут будет костыльный скрипт
	

	let delta = getCorrectDelta(train.lastCarRotation[1].y(), train.lastCarRotation[3].y())

	delta = delta/2;
	state.jointOffset = Math.sin(delta) * 2;
}


// ВРАЩЕНИE КОЛЕС
function updateWheelAngle(train, state) { 
	const angleLoop = 10000;

	// if (!train.shouldRenderDetail()) return 0;
	let speedMps = 20 * train.speed();
	let delta = Timing.delta();
	if (train.isReversed()) {
		state.wheelAngle -= speedMps * delta; 
	} else {
		state.wheelAngle += speedMps * delta; 
	}
	
	if (state.wheelAngle > angleLoop) state["wheelAngle"] = 0;
	if (state.wheelAngle < 0) state["wheelAngle"] = angleLoop;
}

function uploadPartedModels(rawModels, removeRenderType, invertUV) { 
  result = {};
  for (it = rawModels.entrySet().iterator(); it.hasNext(); ) {
    entry = it.next();

    let jsStringKey = "" + entry.getKey(); 
    if (jsStringKey.includes("__int__")) {
    	entry.getValue().setAllRenderType("interior"); //включение интерьера
    	if (removeRenderType) jsStringKey = jsStringKey.replace("__int__", '');

    } else if (jsStringKey.includes("__light__")) {
    	entry.getValue().setAllRenderType("light"); //включение света
    	if (removeRenderType) jsStringKey = jsStringKey.replace("__light__", '');

    } else if (jsStringKey.includes("__window__")) {
    	entry.getValue().setAllRenderType("exteriortranslucent"); //
    	if (removeRenderType) jsStringKey = jsStringKey.replace("__window__", '');

    } else if (jsStringKey.includes("__windowint__")) {
    	entry.getValue().setAllRenderType("interiortranslucent"); //
    	if (removeRenderType) jsStringKey = jsStringKey.replace("__windowint__", '');

    } 

    print("registering " + jsStringKey + "...");

    entry.getValue().applyUVMirror(false, invertUV);
    result[jsStringKey] = ModelManager.uploadVertArrays(entry.getValue());
  }
  return result;
}

function getCorrectDelta(angle1, angle2) {
  let delta = angle1 - angle2;
  if (Math.abs(delta) > Math.PI) {
    if(delta < 0) {
    	return Math.PI * 2 + delta
    } else {
    	return -Math.PI * 2 + delta
    }
  }
  return delta;
}

function clamp(num, min, max) {
	if (num > max) return max;
	if (num < min) return min;
	return num;
}

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