﻿module wizzi-mtree.loader.mTreeBrickProvider
	kind jsfile

	import path
	import url
	+
	var errors = require('../errors')
	var LoadHistory = require('./loadHistory').LoadHistory
	var MTreeBrick = require('./mTreeBrick').MTreeBrick
	var IttfDocumentFinder = require('./ittfDocumentFinder')

	#
		# Each mTree loading requires one instance of the MTreeBrickProvider.
		# TODO To date there is no global cache for mTrees, but only a cache of
		# mTreeBricks used during a single loading. 
	class MTreeBrickProvider
		ctor
			set this.__type = 'MTreeBrickProvider'
			set this.bricksCache = {}
			set this.store = null
			set this.storeKind = null
			set this.uri = null
			set this.schema = null
			set this.userId = null
			set this.documentFinder = null
			set this.primaryMTreeBrickCloned = null
			set this.loadHistory = null
            set this.frontMatter = null
			set this.productionContext = null
			set this.mTreeBuildUpContext = null
			set this.sourcePreprocessor = null

		# The primary mTreeBrick of an mTree loading
		m getPrimaryMTreeBrick
			return this.primaryMTreeBrickCloned

		# The starting point of an mTree loading
		# Creates the mTree loadHistory
		# Load the primary mTreeBrick from the primary IttfDocument
		m init
			string primaryIttfDocumentUri
			{ loadContext
				{ __ittfDocumentStore
				{ productionContext
				{ mTreeBuildUpContext
				func sourcePreprocessor
					optional
			callback

			set this.store = loadContext.__ittfDocumentStore

			var that = this
			# parse the primary IttfDocument Uri and collect
			# infos from the uri
			_ this.store.parseUri
				@ primaryIttfDocumentUri
				f_cb( parsedUri )
					# loog 'wizzi-mtree.mTreeBrickProvider.parsedUri', parsedUri
					
					set that.storeKind = parsedUri.storeKind
					set that.uri = parsedUri.uri || parsedUri.originalUri
					set that.userId = parsedUri.userId
					set that.projectId = parsedUri.projectId
					set that.schema = parsedUri.schema
					
					# load the document text content
					_ that.store.getModelContent
						@ that.uri
						f_cb_no_err( ittfContent )
							if err
								set err.documentUri = that.uri
								r_cb_err()
							# init helper objects
							# loog 'ittfContent', ittfContent
							if !ittfContent || ittfContent.trim().length == 0
								# TODO document this
								return
									_ callback
										new errors.IttfLoadError
											@ "Empty document"
											@ that.uri
							set that.documentFinder
								new IttfDocumentFinder
									@ that.store
									@ that.schema
							set that.loadHistory = new LoadHistory()
                            set that.frontMatter = {}
							set that.productionContext = loadContext.productionContext
							set that.mTreeBuildUpContext = loadContext.mTreeBuildUpContext
							set that.sourcePreprocessor = loadContext.sourcePreprocessor
							# loog 'wizzi-mtree.mTreeBrickProvider.ittfContent', ittfContent
							# load the primary mTreeBrick from the ittf content
							_ that.loadMTreeBrickFromSource
								@ that.uri
								{
								@ ittfContent
								f_cb( primaryMTreeBrickCloned )
									# save the cloned primary mTreeBrick on the mTreeBrickProvider instance
									set that.primaryMTreeBrickCloned = primaryMTreeBrickCloned
									# callbacks returning the mTreeBrickProvider instance
									_ callback(null, that)

		#
			# Clones, or loads from source, an included or mixed ittf document
			# params
			#	{ options
			#		string ittfDocumentUri
			#		boolean include
			#         // If true the mTreeBrick loaded from the IttfDocument will be included in the includer mTreeBrick
			#         // and its scope will become that of the includer (its brickKey will be that of the includer).
			#         // A $include command must not have any argument.
			#         // An included ittf document must not have params (must not have the $params command).
			#		string basedir
			#		string relpath
            #       string from
            #		string includerBrickKey
            #       { includerMTreeBrick
            #         api-ref wizzi-mtree.mTreeBrick
            #
			#
			# called from
			#	./mtree.load
			#	./includer
			#	./mixer
			#

		m get
			{ options
			callback
			
			var loadHistory = this.loadHistory
			var productionContext = this.productionContext
			var that = this
			_ this.documentFinder.resolvePath
				@ options
				f_cb( uri )
					# loog 'wizzi-mtree.mTreeBrickProvider.path resolved', uri
					var mTreeBrickCloned = null
					# check cache
					# loog 'searching in cache', that.bricksCache, uri
					var cachedMTreeBrick = that.bricksCache[uri]
					if cachedMTreeBrick
						# found in cache, clone it
						set mTreeBrickCloned = cachedMTreeBrick.clone()
						
						# this is not superflous
						# the productionContext counts the cached used
						_ productionContext.addIttfDocument
							@ uri
							@ cachedMTreeBrick.inputContent
						
						# adding the mTreeBrickCloned to the loadHistory
						# generates the sourceKey and brickKey 
						var mTreeBrickData
							_ loadHistory.addMTreeBrick
								@ uri
								@ that.schema
								@ mTreeBrickCloned
								@ options
						set mTreeBrickCloned.sourceKey = mTreeBrickData.sourceKey
						set mTreeBrickCloned.brickKey = mTreeBrickData.brickKey
						set mTreeBrickCloned.$schema = that.schema
						return callback(null, mTreeBrickCloned)
					
					else 
						
						# not found in cache, check if it is a documentFragment
                        if uri.substr(-11, 11) === '__$fragment'
                            return
                                _ that.loadMTreeBrickFromDocumentFragment
                                    @ uri
                                    @ options
                                    @ callback

                        # not found in cache, get the content from the store
						# than load the mTreeBrick from source
						_ that.store.getModelContent
							@ uri
							f_cb( ittfContent )
								if !ittfContent || ittfContent.trim().length == 0
									# TODO document this
									return
										_ callback
											new errors.IttfLoadError
												@ "Empty document"
												@ uri
								else
									_ that.loadMTreeBrickFromSource
										@ uri
										@ options
										@ ittfContent
										@ callback

		m loadMTreeBrickFromSource
			param uri
			param options
			param ittfContent
			param callback
			
			_ this.productionContext.addIttfDocument
				@ uri
				@ ittfContent
			
			_ this.loadHistory.addIttfDocument
				@ uri
				@ ittfContent

			if this.sourcePreprocessor
				# TODO save the preprocessed in productionContext and loadHistory
				#      or save the sourcePreprocessor function
				set ittfContent = this.sourcePreprocessor(ittfContent)
			
			# Creates the mTreeBrickData object passing
			# null in the mTreeBrick parameter
			# it will be added later if the parameter 
			# `options.include` is false. 
			# This because if the mTreeBrick
			# is included it does not have its own evalContext, its nodes become part
			# of the includer, and a cloned mTreeBrick is not needed.

			var mTreeBrickData
				_ this.loadHistory.addMTreeBrick
					@ uri
					@ this.schema
					@ null
					@ options
			
			var newMTreeBrick = new MTreeBrick(uri, this.loadHistory, this.frontMatter)
			
			# parses the ittf document content and loads the mTreeBrick
			# frome the ittf node tree.
			checked_call( notUsed )
				_ newMTreeBrick.load(ittfContent, mTreeBrickData)
			# loog 'wizzi-mtree.mTreeBrickProvider.parsed newMTreeBrick', newMTreeBrick
			
			# caches the newMTreeBrick
			set this.bricksCache[uri] = newMTreeBrick
			
			# clone it
			var mTreeBrickCloned = newMTreeBrick.clone()
			# set keys
			set mTreeBrickCloned.sourceKey = mTreeBrickData.sourceKey
			set mTreeBrickCloned.brickKey = mTreeBrickData.brickKey
			set mTreeBrickCloned.$schema = this.schema
			+
			if !(options.include)
				set mTreeBrickData.mTreeBrick = mTreeBrickCloned
			+
			# loog 'cloned newMTreeBrick', mTreeBrickCloned
			return callback(null, mTreeBrickCloned)

		m loadMTreeBrickFromDocumentFragment
			param uri
			param options
			param callback
            
            var parentMtreeBrick = options.includerMTreeBrick
            var newMTreeBrick = new MTreeBrick(uri, this.loadHistory, this.frontMatter)

            # load it
            foreach item in options.includerMTreeBrick.documentFragments
                if item.value === options.relpath
                    # loog 'wizzi-mtree.mTreeBrickProvider.loadMTreeBrickFromDocumentFragment', item.name, item.value, item.$params
                    set newMTreeBrick.nodes = [item]
                    if !(options.include)
                        set newMTreeBrick.$params = item.$params
			
            # caches the newMTreeBrick
			set this.bricksCache[uri] = newMTreeBrick

			var mTreeBrickData
                iif options.include
                    then null
                    else
                        _ this.loadHistory.addMTreeBrick
                            @ uri
                            @ this.schema
                            @ null
                            @ options
			
			# clone it
			var mTreeBrickCloned = newMTreeBrick.clone()
            set mTreeBrickCloned.nodes[0].name = '$group'
            set mTreeBrickCloned.nodes[0].value = ''
            $$ VAI set mTreeBrickCloned.$params = ''
			
            # set keys

			if !(options.include)
                set mTreeBrickCloned.sourceKey = mTreeBrickData.sourceKey
                set mTreeBrickCloned.brickKey = mTreeBrickData.brickKey
                set mTreeBrickCloned.$schema = parentMtreeBrick.$schema
				set mTreeBrickData.mTreeBrick = mTreeBrickCloned
            else
                set mTreeBrickCloned.sourceKey = parentMtreeBrick.sourceKey
                set mTreeBrickCloned.brickKey = parentMtreeBrick.brickKey
                set mTreeBrickCloned.$schema = parentMtreeBrick.$schema
			
            # loog 'cloned newMTreeBrick', mTreeBrickCloned
			return callback(null, mTreeBrickCloned)

		m enterFragmentCall
			param mixerUri
			param mixedUri
			_ this.loadHistory.enterFragmentCall
				@ mixerUri
				@ mixedUri
		
		m exitFragmentCall
			_ this.loadHistory.exitFragmentCall()
		
		m checkForRecursion
			return
				_ this.loadHistory.checkForRecursion()

	#
		# Creates an MTreeBrickProvider for loading
		# an IttfDocucument
	set MTreeBrickProvider.createFromUri
		function
			string primaryIttfDocumentUri
			{ loadContext
			callback
			# loog 'wizzi-mtree.mTreeBrickProvider.createFromUri', primaryIttfDocumentUri
			var provider = new MTreeBrickProvider()
			try
				_ provider.init
					@ primaryIttfDocumentUri
					@ loadContext
					f_cb( notUsed )
						_ callback(null, provider)
			catch ex
                # TODO create new IttfError and call callback
				try
                    set ex.message += '\n creating from uri: ' + primaryIttfDocumentUri
                catch ex2
				throw ex

	set module.exports = MTreeBrickProvider