import RoutingSystem from './RoutingSystem.js';
import ViewSystem, { View } from './ViewSystem.js';
import ModuleSystem from './ModuleSystem.js';
import Modules from '../../modules';
import EventSystem from './EventSystem';
import ActionSystem from './ActionSystem';
import RulingSystem from './RulingSystem';
import InjectionSystem from './InjectionSystem';
import * as Font from 'expo-font';


import Data from '!!raw-loader!../../rules/Universal.css';
import BaseRules from '!!raw-loader!../../rules/BaseRules.css';

export default class CoreSystem {
	constructor(dev = false) {
		// TODO -- Make correct Initialization
		InjectionSystem.depend("Core", this);

		this.Routing = new RoutingSystem();
		InjectionSystem.depend("Routing", this.Routing);
		this.ModuleSystem = new ModuleSystem();
		InjectionSystem.depend("Modules", this.ModuleSystem);
		this.ActionSystem = new ActionSystem(this);
		InjectionSystem.depend("Actions", this.ActionSystem);
		this.ActionSystem.onUpdate(() => this.forceUpdate());

		this.RulingSystem = new RulingSystem();
		InjectionSystem.depend("Ruling", this.RulingSystem);
		this.RulingSystem.addRuleFile(Data);
		this.RulingSystem.addRuleFile(BaseRules);
		this.RulingSystem.onUpdate(() => this.forceUpdate())

		this.ViewSystem    = new ViewSystem(this);
		InjectionSystem.depend("Views", this.ViewSystem);
		this.loadFonts()
		this.__loadModules(Modules);
		this._devMode = dev;

	}
	onUpdate(fn) {this._onUpdate = fn;}
	forceUpdate() {
		this._onUpdate && this._onUpdate();
	}

	fresh() {
		this.ActionSystem = new ActionSystem(this);
		this.ActionSystem.onUpdate(() => this.forceUpdate());
		this.Routing = new RoutingSystem();
		this.ModuleSystem = new ModuleSystem();
		
		this.ViewSystem    = new ViewSystem(this);
		let HomeView = new View();
		this.addPage('Home', HomeView, 'VSHome').setHome('Home');
		this.__loadModules(Modules);
	}

	loadFonts(){
		Font.loadAsync({
	      'black': require('../assets/fonts/SFCompactDisplay-Black_0.otf'),
	      'bold': require('../assets/fonts/SFCompactDisplay-Bold_0.otf'),
	      'heavy': require('../assets/fonts/SFCompactDisplay-Heavy_0.otf'),
	      'light': require('../assets/fonts/SFCompactDisplay-Light_0.otf'),
	      'medium': require('../assets/fonts/SFCompactDisplay-Medium_0.otf'),
	      'regular': require('../assets/fonts/SFCompactDisplay-Regular_0.otf'),
	      'semibold': require('../assets/fonts/SFCompactDisplay-Semibold_0.otf'),
	      'thin': require('../assets/fonts/SFCompactDisplay-Thin_0.otf'),
	      'ultralight': require('../assets/fonts/SFCompactDisplay-Ultralight_0.otf'),
	      'roboto-black':require('../assets/fonts/Roboto-Black.ttf'),
	      'roboto-black-italic':require('../assets/fonts/Roboto-BlackItalic.ttf'),
	      'roboto-bold':require('../assets/fonts/Roboto-Bold.ttf'),
	      'roboto-bold-italic':require('../assets/fonts/Roboto-BoldItalic.ttf'),
	      'roboto-light':require('../assets/fonts/Roboto-Light.ttf'),
	      'roboto-light-italic':require('../assets/fonts/Roboto-LightItalic.ttf'),
	      'roboto-medium':require('../assets/fonts/Roboto-Medium.ttf'),
	      'roboto-medium-italic':require('../assets/fonts/Roboto-MediumItalic.ttf'),
	      'roboto-regular':require('../assets/fonts/Roboto-Regular.ttf'),
	      'roboto-regular-italic':require('../assets/fonts/Roboto-RegularItalic.ttf'),
	      'roboto-thin':require('../assets/fonts/Roboto-Thin.ttf'),
	      'roboto-thin-italic':require('../assets/fonts/Roboto-ThinItalic.ttf')

	    });
	}

	getCurrentView() {
		return this.ViewSystem.getView(this.Routing.getCurrentView());
	}

	setHome( route , View ) {
		this.Routing.setHome(route);
		return this;
	}

	goto( route ) {
		let res = this.Routing.goTo(route);
		this.forceUpdate();
		return res;
	}

	addPage( route , View, ViewID = Math.random()) {
		this.Routing.addRoute(route,route);
		this.Routing.setView(route,ViewID);
		this.ViewSystem.addView(ViewID, View);
		return this;
	}
	removePage(route) {
		this.Routing.removeRoute(route);
		return this;
	}
	goBack() {

	}

	__loadModules() {
		this.ModuleSystem.load(Modules);
	}

	addModule(mod, namespace) {
		this.ModuleSystem.loadModule(mod.name, mod, namespace);
		return this;
	}

	render() {
		let ViewID = this.Routing.getCurrentView();
		return this.ViewSystem.render(ViewID);
	}

	import(data) {

		// Perform dependency injection here! 
		// must Convert Data to Structures before importing 
		// Import Systems Seperately from leaf to root
		let {
			Routing,
			Views
		} = data;

		try {
			this.Routing.import(Routing);
			this.ViewSystem.import(Views);
		} catch(e) {
			throw new Error("CoreSystem cannot import data : ", e);
		}
		console.log("Successful import");
	}

	export() {
		let RS = this.Routing.export();
		let VS = this.ViewSystem.export();
		return {
			Routing: RS,
			Views: VS
		};
	}




	ray(data) {
		let {
			event
		} = data;
		let targets = event.path.map(item => findReactElement(item));
		let resViewNode = null;
		for (var i in targets) {
			if(targets[i] && targets[i].ID) {
				resViewNode = targets[i].ID;
				break;
			}		
		}
		let ViewID = this.Routing.getCurrentView();
		return this.ViewSystem.getNode(ViewID, resViewNode);
	}






}

const findReactElement = (node) => {
    for (var key in node) {
        if (key.startsWith("__reactInternalInstance$")) {
            return (node[key]._debugOwner && node[key]._debugOwner.stateNode.props) || (node[key].return.memoizedProps);
        }
    }
    return null;
};