const DocumentedClass = require('./types/class');
const DocumentedInterface = require('./types/interface');
const DocumentedTypeDef = require('./types/typedef');
const DocumentedConstructor = require('./types/constructor');
const DocumentedMember = require('./types/member');
const DocumentedFunction = require('./types/function');
const DocumentedEvent = require('./types/event');
const DocumentedExternal = require('./types/external');
const version = require('../package').version;

/**
 * Class that does stuff
 */
class Documentation {
	constructor(items, custom) {
		this.rootTypes = {
			class: [DocumentedClass, 'classes'],
			interface: [DocumentedInterface, 'interfaces'],
			typedef: [DocumentedTypeDef, 'typedefs'],
			external: [DocumentedExternal, 'externals']
		};

		this.childTypes = {
			'constructor': DocumentedConstructor, // eslint-disable-line quote-props
			member: DocumentedMember,
			function: DocumentedFunction,
			event: DocumentedEvent
		};

		for(const type in this.rootTypes) this[this.rootTypes[type][1]] = new Map();
		this.custom = custom;
		this.parse(items);
	}

	registerRoots(items) {
		let i = 0;
		while(i < items.length) {
			const item = items[i];
			if(this.rootTypes[item.kind] instanceof Array) {
				const [Type, key] = this.rootTypes[item.kind];
				this[key].set(item.name, new Type(this, item));
				items.splice(i, 1);
			} else {
				++i;
			}
		}
	}

	findParent(item) {
		if(this.childTypes[item.kind]) {
			for(const type in this.rootTypes) {
				const parent = this[this.rootTypes[type][1]].get(item.memberof);
				if(parent) return parent;
			}
		}

		return null;
	}

	parse(items) {
		this.registerRoots(items);

		for(const member of items) {
			let item;
			if(this.childTypes[member.kind]) item = new this.childTypes[member.kind](this, member);
			else console.warn(`- Unknown documentation kind "${item.kind}" - \n${JSON.stringify(item)}\n`);

			const parent = this.findParent(member);
			if(parent) {
				parent.add(item);
				continue;
			}

			let memberof = member.memberof || (member.directData ? member.directData.memberof : null);
			if(memberof) memberof = ` (member of "${memberof}")`;
			else memberof = '';
			const name = member.name || (member.directData ? member.directData.name : 'UNKNOWN');
			console.warn(`- "${name}"${memberof} has no accessible parent.`);
			if(!name && !memberof) console.warn('Raw object:', member);
		}
	}

	serialize() {
		const meta = {
			generator: version,
			format: this.constructor.FORMAT_VERSION,
			date: Date.now()
		};

		const serialized = {
			meta,
			custom: this.custom
		};

		for(const type in this.rootTypes) {
			const key = this.rootTypes[type][1];
			serialized[key] = Array.from(this[key].values()).map(i => i.serialize());
		}

		return serialized;
	}

	static get FORMAT_VERSION() {
		return 19;
	}
}

module.exports = Documentation;

@@ -1,24 +1,54 @@
+const path = require('path');%0A
 const DocumentedClass = 
@@ -2032,16 +2032,124 @@
 ;%0A%09%09%09%7D%0A%0A
+%09%09%09const name = member.name %7C%7C (member.directData ? member.directData.name : 'UNKNOWN');%0A%09%09%09let info = %5B%5D;%0A%0A
 %09%09%09let m
@@ -2241,19 +2241,16 @@
 %0A%09%09%09
-if(
 memberof
 ) me
@@ -2245,17 +2245,18 @@
 memberof
-)
+ =
  membero
@@ -2261,13 +2261,11 @@
 rof 
-=
+?
  %60
- (
 memb
@@ -2287,28 +2287,37 @@
 of%7D%22
-)%60;%0A%09%09%09else 
+%60 : '';%0A%09%09%09info.push(
 memberof
  = '
@@ -2316,14 +2316,11 @@
 erof
- = '';
+);%0A
 %0A%09%09%09
@@ -2325,39 +2325,190 @@
 %09%09const 
-name = member.nam
+meta = member.meta ? member.meta.directData : member.directData && member.directData.meta ? %7B%0A%09%09%09%09path: member.directData.meta.path,%0A%09%09%09%09file: member.directData.meta.fil
 e %7C%7C 
-(
 member.d
@@ -2520,46 +2520,264 @@
 Data
- ? member.directData.name : 'UNKNOWN')
+.meta.filename,%0A%09%09%09%09line: member.directData.meta.line %7C%7C member.directData.meta.lineno%0A%09%09%09%7D : null;%0A%09%09%09const file = meta ? %60$%7Bpath.join(meta.path, meta.file)%7D:$%7Bmeta.line%7D%60 : '';%0A%09%09%09info.push(file);%0A%0A%09%09%09info = info.length %3E 0 ? %60 ($%7Binfo.join(', ')%7D)%60 : ''
 ;%0A%09%09
@@ -2804,24 +2804,20 @@
 name%7D%22$%7B
-memberof
+info
 %7D has no
@@ -2863,16 +2863,25 @@
 memberof
+ && !file
 ) consol

Add file info to parentless warning