const EARTH_RADIUS = 6371008.8;
const DEGREES_TO_RADIANS = Math.PI / 180;

export default class Projects {

	constructor(asProjects = {}) {
		Object.assign(this, asProjects);
	}

	getDefaultCodeName() {
		for(const [sCodeName, asProject] of Object.entries(this)) {
			if(asProject.default) return sCodeName;
		}
	}

	getDefaultProject() {
		return this[this.getDefaultCodeName()];
	}

	getTrackInfo(oFeature, oTrack, oLang) {
		const iTrackId = Number(oFeature.properties.track_id);
		const oTrackFeature = oTrack.features[iTrackId] || oFeature;
		const aoCoords = oTrackFeature.geometry.coordinates;

		if(oTrackFeature.properties.type == 'hitchhiking') {
			return {
				type: 'track',
				subtype: oTrackFeature.properties.type,
				name: oTrackFeature.properties.name
			};
		}

		const asStats = this.getTrackStats(aoCoords);
		return {
			type: 'track',
			subtype: oTrackFeature.properties.type,
			name: oTrackFeature.properties.name,
			description: oTrackFeature.properties.description,
			leg: oTrackFeature.properties.leg,
			distance: Math.round(asStats.distance / 100) / 10,
			elev_gain: Math.round(asStats.elevGain),
			elev_drop: Math.abs(Math.round(asStats.elevDrop)),
			duration: this.getNaturalDuration(asStats.time, oLang)
		};
	}

	getNaturalDuration(iHours, oLang) {
		let iTimeMinutes = 0, iTimeHours = 0, iTimeDays = Math.floor(iHours/8); //8 hours a day
		if(iTimeDays > 1) iTimeDays = Math.round(iTimeDays * 2) / 2; //Round down to the closest half day
		else {
			iTimeDays = 0;
			iTimeHours = Math.floor(iHours);
			iHours -= iTimeHours;

			iTimeMinutes = Math.floor(iHours * 4) * 15; //Round down to the closest 15 minutes
		}
		return '~'
			+(iTimeDays>0?(iTimeDays+(iTimeDays%2==0?'':'\u00bd')+' '+oLang.get(iTimeDays>1?'unit.days':'unit.day')):'')	//Days
			+((iTimeHours>0 || iTimeDays==0)?iTimeHours+oLang.get('unit.hour'):'')											//Hours
			+((iTimeDays>0 || iTimeMinutes==0)?'':iTimeMinutes);															//Minutes

	}

	getTrackStats(aoCoords) {
		let iDistance = 0, iElevDrop = 0, iElevGain = 0, iTime = 0;

		for(let i = 1; i < aoCoords.length; i++) {
			const iCurrElev = Number(aoCoords[i][2]);
			const iPrevElev = Number(aoCoords[i - 1][2]);
			const iElevDelta = (Number.isFinite(iCurrElev) && Number.isFinite(iPrevElev))?(iCurrElev - iPrevElev):0;
			const iSegDistance = this.getDistance(aoCoords[i], aoCoords[i - 1]);
			if(iSegDistance <= 0) continue;

			iDistance += iSegDistance;
			iElevDrop += Math.min(iElevDelta, 0);
			iElevGain += Math.max(iElevDelta, 0);

			let iSpeedCorrecRatio = 0;
			const iAngle = iElevDelta / iSegDistance;
			if(iAngle < -1)			iSpeedCorrecRatio = 0.5;
			else if(iAngle < -0.2)	iSpeedCorrecRatio = 1.25;
			else if(iAngle < 0.1)	iSpeedCorrecRatio = 1;
			else if(iAngle < 0.25)	iSpeedCorrecRatio = 0.85;
			else if(iAngle < 0.5)	iSpeedCorrecRatio = 0.6;
			else if(iAngle < 1)		iSpeedCorrecRatio = 0.5;
			else					iSpeedCorrecRatio = 0.25;
			iTime += iSegDistance / 1000 * iSpeedCorrecRatio / 3.5;
		}

		return {
			distance: iDistance,
			elevDrop: iElevDrop,
			elevGain: iElevGain,
			time: iTime
		};
	}

	getDistance(aoPointA, aoPointB) {
		const iLatA = aoPointA[1] * DEGREES_TO_RADIANS;
		const iLatB = aoPointB[1] * DEGREES_TO_RADIANS;
		const iAngle = 
			  Math.sin(iLatA) * Math.sin(iLatB)
			+ Math.cos(iLatA) * Math.cos(iLatB) * Math.cos((aoPointB[0] - aoPointA[0]) * DEGREES_TO_RADIANS);

		return EARTH_RADIUS * Math.acos(Math.min(iAngle, 1));
	}
}
