import omit from 'lodash/omit.js';
import {createFindInTree} from '../lib/find-in-tree.js';

const findInTree = createFindInTree([
	'**.oprX',
	'**.icoX',
	'**.prodX',
	'**.pRefL',
	'**.locX',
	'**.ani.fLocX',
	'**.ani.tLocX',
	'**.fLocX',
	'**.tLocX',
	'**.remX',
	'**.himX',
	'**.polyG.polyXL',
	'**.rRefL',
	'**.msgL',
	'**.remL',
]);

// there are circular references (e.g. warning -> loc -> warning)
// todo: parse either on-the-fly or in a recursive/iterative process
const parseCommonData = (_ctx) => {
	const {profile, opt, res} = _ctx;
	const c = res.common || {};
	const matches = findInTree(res);

	const common = {};
	const ctx = {..._ctx, common};

	common.operators = [];
	if (Array.isArray(c.opL)) {
		common.operators = c.opL.map(op => profile.parseOperator(ctx, op));
		matches['**.oprX'].forEach(([idx, parents]) => {
			if ('number' === typeof idx) {
				parents[0].operator = common.operators[idx];
			}
		});
	}

	common.icons = [];
	if (Array.isArray(c.icoL)) {
		common.icons = c.icoL.map(icon => profile.parseIcon(ctx, icon));
		matches['**.icoX'].forEach(([idx, parents]) => {
			if ('number' === typeof idx) {
				parents[0].icon = common.icons[idx];
			}
		});
	}

	common.lines = [];
	if (Array.isArray(c.prodL)) {
		common.lines = c.prodL.map(l => profile.parseLine(ctx, l));

		matches['**.prodX'].forEach(([idx, parents]) => {
			if ('number' === typeof idx) {
				parents[0].line = common.lines[idx];
			}
		});
		matches['**.pRefL'].forEach(([idxs, parents]) => {
			parents[0].lines = idxs.filter(idx => Boolean(common.lines[idx]))
				.map(idx => common.lines[idx]);
		});
		// todo
		// **.dep.dProdX: departureLine -> common.lines[idx]
		// **.arr.aProdX: arrivalLine -> common.lines[idx]
	}

	common.hints = [];
	if (Array.isArray(c.remL)) {
		common.hints = c.remL.map(hint => profile.parseHint(ctx, hint));
		matches['**.remX'].forEach(([idx, parents]) => {
			if ('number' === typeof idx) {
				parents[0].hint = common.hints[idx];
			}
		});
		matches['**.remL'].forEach(([idxs, parents]) => {
			if (!Array.isArray(idxs)) {
				return;
			}
			parents[0].hints = idxs
				.filter(idx => Boolean(common.hints[idx]))
				.map(idx => common.hints[idx]);
		});
		matches['**.rRefL'].forEach(([idxs, parents]) => {
			parents[0].hints = idxs
				.filter(idx => Boolean(common.hints[idx]))
				.map(idx => common.hints[idx]);
		});
	}

	// resolve .msgL[] references
	// todo: `himMsgEdgeL[].msgRefL[]` look different, it seems they only reference
	// `common.himL[]` items?
	const parseRemarkRef = (ref) => {
		if (ref.type === 'REM' && ref.hint) {
			return omit(ref, ['type', 'remX']);
		}
		if (ref.type === 'HIM' && ref.warning) {
			return omit(ref, ['type', 'himX']);
		}
		return null;
	};
	matches['**.msgL'].forEach(([refs, parents]) => {
		// todo: store as parents[0].(hints|warnings)
		parents[0].remarkRefs = refs
			.map(parseRemarkRef)
			.filter(ref => ref !== null);
	});

	common.locations = [];
	if (Array.isArray(c.locL)) {
		common.locations = c.locL.map(loc => profile.parseLocation(ctx, loc));

		for (let i = 0; i < common.locations.length; i++) {
			const raw = c.locL[i];
			const loc = common.locations[i];
			if ('number' === typeof raw.mMastLocX) {
				loc.station = Object.assign({}, common.locations[raw.mMastLocX]);
				loc.station.type = 'station';
			} else if (raw.isMainMast) {
				loc.type = 'station';
			}
		}

		// todo: correct props?
		matches['**.locX'].forEach(([idx, parents]) => {
			if ('number' === typeof idx) {
				parents[0].location = common.locations[idx];
			}
		});
		matches['**.ani.fLocX'].forEach(([idxs, parents]) => {
			parents[0].fromLocations = idxs.map(idx => common.locations[idx]);
		});
		matches['**.ani.tLocX'].forEach(([idxs, parents]) => {
			parents[0].toLocations = idxs.map(idx => common.locations[idx]);
		});
		matches['**.fLocX'].forEach(([idx, parents]) => {
			if ('number' === typeof idx) {
				parents[0].fromLocation = common.locations[idx];
			}
		});
		matches['**.tLocX'].forEach(([idx, parents]) => {
			if ('number' === typeof idx) {
				parents[0].toLocation = common.locations[idx];
			}
		});
	}

	common.warnings = [];
	if (Array.isArray(c.himL)) {
		common.warnings = c.himL.map(w => profile.parseWarning(ctx, w));
		matches['**.himX'].forEach(([idx, parents]) => {
			if ('number' === typeof idx) {
				parents[0].warning = common.warnings[idx];
			}
		});
	}

	common.polylines = [];
	if ((opt.polylines || opt.polyline) && Array.isArray(c.polyL)) {
		common.polylines = c.polyL.map(p => profile.parsePolyline(ctx, p));
		// todo: **.ani.poly -> parsePolyline()

		matches['**.polyG.polyXL'].forEach(([idxs, parents]) => {
			const idx = idxs.find(idx => Boolean(common.polylines[idx])); // find any given polyline
			parents[1].polyline = common.polylines[idx];
		});
	}

	return common;
};

export {
	parseCommonData,
};
