include("display.js");

var shouldRenderWindows = true;

var rawModels = ModelManager.loadPartedRawModel(Resources.manager(), Resources.idRelative("5265.obj"), null);
var models = uploadPartedModels(rawModels, true, true);

function create(ctx, state, train) {

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

	state.brakeRateLimit = new RateLimit(0.25);
	state.brakeOldSpeed = train.speed();
	state.shouldRenderStopLights = false;

	state.uahMark = false;
	state.uahCoef = 0.0;

	displCreate(ctx, state, train)
}

function dispose(ctx, state, train) {
	displDispose(ctx, state, train)
}

function render(ctx, state, train) {
	updateLogic(ctx, state, train);
	renderLogic(ctx, state, train);

	displRender(ctx, state, train)
}

function updateLogic(ctx, state, train) {
	updateWheelAngle(train, state);
	updateSteeringAngle(train, state, 3.473, 0);

	updateStopSignals(train, state);

	updateUah(ctx, train, state);

}

function renderLogic(ctx, state, train) {
	renderHead(ctx, state, train) 
}

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



	ctx.drawCarModel(models["SHELL"], carIndex, matrices);
	//ctx.drawCarModel(models["KAB"], carIndex, matrices);
	//ctx.drawCarModel(models["DFV"], carIndex, matrices);
	ctx.drawCarModel(models["SALON"], carIndex, matrices);
	//ctx.drawCarModel(models["SALON_DETAIL"], carIndex, matrices);
	ctx.drawCarModel(models["GABARIT"], carIndex, matrices);
	//ctx.drawCarModel(models["SALON_LIGHT"], carIndex, matrices);


	let coef = train.lastCarRotation[0].y() % (3.1415/2)
	if (coef > 3.1415 / 4) coef = 3.1415/2 - coef;
	coef = coef / (3.1415/4);

	{
		let uahCoef = state.uahCoef;
		matrices.pushPose();
		matrices.translate(-0.2553, 0.0, 0.2249);
		matrices.rotateY(0.04 * uahCoef);
		matrices.translate(0.2553, 0.0, -0.2249);

		matrices.pushPose();
		matrices.translate(0, 2.25, 0.2249);
		matrices.rotateX(-0.39 * smoothCubic(uahCoef));
		matrices.translate(0, -2.25, -0.2249);

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

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

		matrices.pushPose();
		matrices.translate(0.2553, 0.0, 0.2249);
		matrices.rotateY((1.0 - uahCoef) * coef * 0.0475);
		matrices.rotateY(0.105 * uahCoef);
		matrices.translate(-0.2553, 0.0, -0.2249);

		matrices.pushPose();
		matrices.translate(0, 2.25, 0.2249);
		matrices.rotateX(-0.39 * smoothCubic(uahCoef));
		matrices.translate(0, -2.25, -0.2249);
	
		ctx.drawCarModel(models["POLE_L"], carIndex, matrices);

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


	

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

	
	{
	// WHEELS
		// front right
		const radius = 1.8;
		let steeringAngle = state.steeringAngle;
		matrices.pushPose();
		matrices.translate(-0.8484, -0.5300,3.201);
		matrices.rotateY(steeringAngle);
		matrices.rotateX(state.wheelAngle * radius);
		matrices.translate(0.8484, 0.5300, -3.201);
	
		ctx.drawCarModel(models["W_P"], carIndex, matrices);
	
		matrices.popPose();
	
		matrices.pushPose();
		matrices.translate(0.8484, -0.5300, 3.201);
		matrices.rotateY(steeringAngle);
		matrices.rotateX(state.wheelAngle * radius);
		matrices.translate(-0.8484, 0.5300, -3.201);
	
		ctx.drawCarModel(models["W_L"], carIndex, matrices);
	
		matrices.popPose();
	
		// BACK WHEELS
		matrices.pushPose();
		matrices.translate(-0.8484, -0.5300, -2.380);
		matrices.rotateX(state.wheelAngle * radius);
		matrices.translate(0.8484, 0.5300, 2.380);
	
		ctx.drawCarModel(models["W_Z"], carIndex, matrices);
		
		matrices.popPose();
	}

	{
		// FARY
		let shouldRenderLights = (MinecraftClient.worldDayTime() % 24000) > 12000;

		if (shouldRenderLights) {
			ctx.drawCarModel(models["LIGHT"], carIndex, matrices);
		}

		if (state.shouldRenderStopLights) {
			ctx.drawCarModel(models["STOP"], carIndex, matrices);
		}
	}
	

	{
	// DOORS	
		// front
				let doorVal = 0.0;
		if (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["D1L_EXT"], 
			null, models["D1L_INT"], null, models["D1L_WIN"], 
			doorVal * 70, doorVal * -155, -1.130, 4.161, -1.130, 4.161 + 0.25, carIndex)
	
		// middle
		renderDoorWithChild(matrices, ctx, null, models["D2R_EXT"], 
			null, models["D2R_INT"], null, models["D2R_WIN"], 
			doorVal * -70, doorVal * 155, -1.130, 1.010, -1.130, 1.010 - 0.25, carIndex)

		renderDoorWithChild(matrices, ctx, null, models["D2L_EXT_doors.007"], 
			null, models["D2L_INT"], null, models["D2L_WIN"], 
			doorVal * 70, doorVal * -155, -1.130, -0.1616, -1.130, -0.1616 + 0.25, carIndex)

		// back
		renderDoorWithChild(matrices, ctx, null, models["D3R_EXT"], 
			null, models["D3R_INT"], null, models["D3R_WIN"], 
			doorVal * -70, doorVal * 155, -1.130, -3.419, -1.130, -3.47 - 0.25, carIndex)

		renderDoorWithChild(matrices, ctx, null, models["D3L_EXT"], 
			null, models["D3L_INT"], null, models["D3L_WIN"], 
			doorVal * 70, doorVal * -155, -1.130, -4.726, -1.130, -4.7 + 0.25, 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();
}




// ВРАЩЕНИ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);
}

function updateStopSignals(train, state) {
	if (state.brakeRateLimit.shouldUpdate()) {
		let oldSpeed = state.brakeOldSpeed;
		let speed	= train.speed();

		state.shouldRenderStopLights = speed < oldSpeed;

		state.brakeOldSpeed	= speed;
	}
}

function updateUah(ctx, train, state) {
  let stationList = train.getThisRoutePlatforms();
  if (stationList[0] == undefined) {
  	state.uahMark = false;
  	state.uahCoef == 0.0;
  	return;
  }
  let routeName = stationList[0].route.name + "";

  if (!routeName.includes('~')) {
  	state.uahMark = false;
  	state.uahCoef = 0.0;
  	return;
  }
	//if (!routeName.contains('~'))
	let stationsCount = stationList.length;
	let nextStInd = train.getThisRoutePlatformsNextIndex();
	let isDoorOpening = train.isDoorOpening();


	if (isDoorOpening && nextStInd == 0) { // Если троллейбус пытается открыть дверь на первой станции
		state.uahMark = true;
	}

	// Если троллейбус находится медлу 1 и последней станцией. Нужно для перестраховки, если, допустим, троллейбус инициализировался не сначала а по пути
	if (nextStInd <= stationsCount - 1 && nextStInd > 0) { 
		let railIndex = train.getRailIndex(train.railProgress(), true);
		let railType = train.path()[railIndex].rail == null ? RailType.SIDING : train.path()[railIndex].rail.railType;



		if (!(railType.toString() == "PLATFORM" && nextStInd == stationsCount - 1)) {
			state.uahMark = true;
			state.uahCoef = 1.0;
		}
	}

	if (isDoorOpening && nextStInd == stationsCount - 1) { // Если троллейбус пытается открыть дверь на последней станции
		state.uahMark = false;
	}

	const poleRotateDuration = 3.0;

	if (state.uahMark) {
		if (state.uahCoef < 1.0) {
			state.uahCoef += Timing.delta() / poleRotateDuration;
		} else {
			state.uahCoef = 1.0
		}
	} else {
		if (state.uahCoef > 0.0) {
			state.uahCoef -= Timing.delta() / poleRotateDuration;
		} else {
			state.uahCoef = 0.0
		}
	}

	
}

