/*!
 * ${copyright}
 */


sap.ui.define([
	'sap/ui/base/EventProvider',
	'sap/ui/core/routing/Target',
	'sap/ui/core/routing/async/Route',
	'sap/ui/core/routing/sync/Route',
	'sap/ui/core/Component',
	"sap/base/Log",
	"sap/base/assert",
	"sap/base/util/deepExtend"
],
	function(EventProvider, Target, asyncRoute, syncRoute, Component, Log, assert, deepExtend) {
	"use strict";

		/**
		 * Instantiates a route
		 *
		 * @class
		 * @param {sap.ui.core.routing.Router} oRouter
		 *   Router instance to which the route will be added
		 * @param {object} oConfig
		 *   Configuration object for the route
		 * @param {string} oConfig.name
		 *   Name of the route, it will be used to retrieve the route from the router, it needs to be unique
		 *   per router instance
		 * @param {string} [oConfig.pattern]
		 *   URL pattern where it needs to match again. A pattern may consist of the following:
		 * <ul>
		 * <li>
		 * hardcoded parts: "pattern" : "product/settings" - this pattern will only match if the hash of the browser is product/settings and no arguments will be passed to the events of the route.</br>
		 * </li>
		 * <li>
		 * mandatory parameters: "pattern" : "product/{id}" - {id} is a mandatory parameter, e. g. the following hashes would match: product/5, product/3. The pattenMatched event will get 5 or 3 passed as id in its arguments.The hash product/ will not match.</br>
		 * </li>
		 * <li>
		 * optional parameters: "pattern" : "product/{id}/detail/:detailId:" - :detailId: is an optional parameter, e. g. the following hashes would match: product/5/detail, product/3/detail/2</br>
		 * </li>
		 * <li>
		 * query parameters: "pattern" : "product{?query}" // {?query} allows you to pass queries with any parameters, e. g. the following hashes would match: product?first=firstValue, product?first=firstValue&second=secondValue</br>
		 * </li>
		 * <li>
		 * rest as string parameters: "pattern" : ":all*:" - this pattern will define an optional variable that will pass the whole hash as string to the routing events. It may be used to define a catchall route, e. g. the following hashes would match: foo, product/5/3, product/5/detail/3/foo. You can also combine it with the other variables but make sure a variable with a * is the last one.</br>
		 * </ul>
		 * @param {boolean} [oConfig.greedy=false] Since 1.27. By default only the first route matching the hash, will fire events. If greedy is turned on
		 *   for a route, its events will be fired even if another route has already matched.
		 * @param {string} [oConfig.parent] Since 1.32. This property contains the information about the route which nests this route in the form:
		 *   "[componentName:]routeName". The nesting routes pattern will be prefixed to this routes pattern and hence
		 *   the nesting route also matches if this one matches.
		 * @param {string|string[]} [oConfig.target]
		 *   One or multiple name of targets {@link sap.ui.core.routing.Targets}. As soon as the route matches, the
		 *   target(s) will be displayed. All the deprecated parameters are ignored, if a target is used.
		 * @param {string} [oConfig.view]
		 *   <b>Deprecated since 1.28, use <code>target.viewName</code> instead.</b></br> The name of a view that will be created, the first time this
		 *   route will be matched. To place the view into a Control use the targetAggregation and targetControl.
		 *   Views will only be created once per Router
		 * @param {string} [oConfig.viewType]
		 *   <b>Deprecated since 1.28, use <code>target.viewType</code> instead.</b></br> The type of the view that is going to be created. eg: "XML", "JS"
		 * @param {string} [oConfig.viewPath]
		 *   <b>Deprecated since 1.28, use <code>target.viewPath</code> instead.</b></br> A prefix that will be prepended in front of the view eg: view is
		 *   set to "myView" and viewPath is set to "myApp" - the created view will be "myApp.myView"
		 * @param {string} [oConfig.targetParent]
		 *   <b>Deprecated since 1.28, use <code>config.rootView</code> (only available in the router config) instead.</b></br> The id of the parent of the
		 *   targetControl - This should be the id view your targetControl is located in. By default, this will be
		 *   the view created by a component, or if the Route is a subroute the view of the parent route is taken.
		 *   You only need to specify this, if you are not using a router created by a component on your top level routes
		 * @param {string} [oConfig.targetControl]
		 *   <b>Deprecated since 1.28, use <code>target.controlId</code> instead.</b></br> Views will be put into a container Control, this might be an
		 *   {@link sap.ui.ux3.Shell} control or an {@link sap.m.NavContainer} if working with mobile, or any other container.
		 *   The id of this control has to be put in here
		 * @param {string} [oConfig.targetAggregation]
		 *   <b>Deprecated since 1.28, use <code>target.controlAggregation</code> instead.</b></br> The name of an aggregation of the targetControl,
		 *   that contains views. Eg: an {@link sap.m.NavContainer} has an aggregation "pages", another Example is the
		 *   {@link sap.ui.ux3.Shell} it has "content".
		 * @param {boolean} [oConfig.clearTarget=false]
		 *   <b>Deprecated since 1.28, use <code>target.clearControlAggregation</code> instead.</b></br> Defines a boolean that
		 *   can be passed to specify if the aggregation should be cleared before adding the View to it. When using an
		 *   {@link sap.ui.ux3.Shell} this should be true. For an {@link sap.m.NavContainer} it should be false
		 * @param {object} [oConfig.subroutes]
		 *   <b>Deprecated since 1.28, use <code>targets.parent</code> instead.</b> one or multiple route configs taking all of these parameters again.
		 *   If a subroute is hit, it will fire the routeMatched event for all its parents. The routePatternMatched event
		 *   will only be fired for the subroute not the parents. The routing will also display all the targets of the
		 *   subroutes and its parents.
		 * @param {sap.ui.core.routing.Route} [oParent]
		 *   The parent route - if a parent route is given, the routeMatched event of this route will also trigger the
		 *   route matched of the parent and it will also create the view of the parent(if provided).
		 *
		 * @public
		 * @alias sap.ui.core.routing.Route
		 * @extends sap.ui.base.EventProvider
		 */
		var Route = EventProvider.extend("sap.ui.core.routing.Route", /** @lends sap.ui.core.routing.Route.prototype */ {

			metadata : {
				publicMethods: ["getURL", "getPattern"]
			},

			constructor : function(oRouter, oConfig, oParent) {
				EventProvider.apply(this, arguments);

				this._validateConfig(oConfig);

				this._aPattern = [];
				this._aRoutes = [];
				this._oParent = oParent;
				this._oConfig = oConfig;
				this._oRouter = oRouter;

				var that = this,
					vRoute = oConfig.pattern,
					aSubRoutes,
					sRouteName,
					oSubRouteConfig,
					RouteStub,
					async = oRouter._isAsync();

				RouteStub = async ? asyncRoute : syncRoute;
				for (var fn in RouteStub) {
					this[fn] = RouteStub[fn];
				}

				if (!Array.isArray(vRoute)) {
					vRoute = [vRoute];
				}

				if (oConfig.parent) {
					var oRoute = this._getParentRoute(oConfig.parent);
					if (!oRoute) {
						Log.error("No parent route with '" + oConfig.parent + "' could be found", this);
					} else if (oRoute._aPattern.length > 1) {
						Log.error("Routes with multiple patterns cannot be used as parent for nested routes", this);
						return;
					} else {
						this._oNestingParent = oRoute;
						vRoute.forEach(function(sRoute, i) {
							var sNestingRoute = oRoute._aPattern[0];
							sNestingRoute = sNestingRoute.charAt(sNestingRoute.length) === "/" ? sNestingRoute : sNestingRoute + "/";
							vRoute[i] = sNestingRoute + sRoute;
						});
					}
				}

				if (Array.isArray(oConfig.subroutes)) {
					//Convert subroutes
					aSubRoutes = oConfig.subroutes;
					oConfig.subroutes = {};
					aSubRoutes.forEach(function(oSubRoute) {
						oConfig.subroutes[oSubRoute.name] = oSubRoute;
					});
				}


				if (!oConfig.target) {
					// make a copy of the config object because Target changes
					// the object internally
					var oTargetConfig = this._convertToTargetOptions(oConfig);
					oTargetConfig._async = async;
					// create a new target for this route
					this._oTarget = new Target(oTargetConfig, oRouter._oViews, oParent && oParent._oTarget);
					this._oTarget._bUseRawViewId = true;
				}

				// recursively add the subroutes to this route
				if (oConfig.subroutes) {
					for (sRouteName in oConfig.subroutes) {
						oSubRouteConfig = oConfig.subroutes[sRouteName];
						if (oSubRouteConfig.name === undefined) {
							oSubRouteConfig.name = sRouteName;
						}
						oRouter.addRoute(oSubRouteConfig, that);
					}
				}

				if (oConfig.pattern === undefined) {
					//this route has no pattern - it will not get a matched handler. Or a crossroads route
					return;
				}

				vRoute.forEach(function(sRoute, iIndex) {

					that._aPattern[iIndex] = sRoute;

					that._aRoutes[iIndex] = oRouter._oRouter.addRoute(sRoute);
					that._checkRoute(that._aRoutes[iIndex]);
					that._aRoutes[iIndex].greedy = oConfig.greedy;

					that._aRoutes[iIndex].matched.add(function() {
						var oArguments = {};
						Array.from(arguments).forEach(function(sArgument, iArgumentIndex) {
							oArguments[that._aRoutes[iIndex]._paramsIds[iArgumentIndex]] = sArgument;
						});
						that._routeMatched(oArguments, true);
					});

					that._aRoutes[iIndex].switched.add(function() {
						that._routeSwitched();
					});
				});
			},

			_checkRoute: function(oRoute){
				var aParams = oRoute._paramsIds;
				if (Array.isArray(aParams)){
					var aDuplicateParams = aParams.filter( function(sParam){
						return sParam.charAt(0) === "?";
					}).filter( function(sParam){
						return aParams.indexOf(sParam.substring(1)) > -1;
					}).map(function(sParam){
						return sParam.substring(1);
					});
					if (aDuplicateParams.length > 0) {
						throw Error("The config of route '" + this._oConfig.name + "' contains standard parameter and query parameter with the same name: '" + aDuplicateParams + "'. The name of the routing parameters and query parameter have to differentiate.");
					}
				}
			},

			_routeSwitched: function() {
				this._suspend();
				this.fireEvent("switched", {
					name: this._oConfig.name
				});
			},

			_suspend: function() {
				if (this._oRouter._oTargets) {
					// suspend the targets which were displayed when the route was matched
					this._oRouter._oTargets.suspend(this._oConfig.target);

					// suspend the dynamic targets
					if (this._oConfig.dynamicTarget) {
						this._oRouter._oTargets.suspend(this._oConfig.dynamicTarget);
					}
				}
			},

			_resume: function() {
				if (this._oRouter._oTargets) {
					this._oRouter._oTargets.resume(this._oConfig.target);

					if (this._oConfig.dynamicTarget) {
						this._oRouter._oTargets.resume(this._oConfig.dynamicTarget);
					}
				}
			},

			/**
			 * Destroys a route
			 *
			 * @public
			 * @returns { sap.ui.core.routing.Route } this for chaining.
			 */
			destroy : function () {
				EventProvider.prototype.destroy.apply(this);

				this._aPattern = null;
				this._aRoutes = null;
				this._oParent = null;
				this._oConfig = null;

				this.bIsDestroyed = true;

				return this;
			},

			/**
			 * Returns the URL for the route and replaces the placeholders with the values in oParameters
			 *
			 * @param {object} oParameters Parameters for the route
			 * @return {string} the unencoded pattern with interpolated arguments
			 * @public
			 */
			getURL : function (oParameters) {
				return this._aRoutes[0].interpolate(oParameters || {});
			},

			/**
			 * Converts the different format of targets info into the object format
			 * which has the key of a target saved under the "name" property
			 *
			 * @param {string|string[]|object|object[]} vTarget The key of the target or
			 *  an object which has the key of the target under property 'name' as specified
			 *  in the {@link #constructor} or an array of keys or objects
			 * @return {object[]} Array of objects and each of the objects contains at least
			 *  the key of the target under the "name" property
			 * @private
			 */
			_alignTargetsConfig: function(vTarget) {
				if (!vTarget) {
					return [];
				}

				if (!Array.isArray(vTarget)) {
					return (typeof vTarget === "string") ?
						[{ name: vTarget }] : [vTarget];
				}

				// vTarget is an array
				return vTarget.map(function(vTargetConfig) {
					if (typeof vTargetConfig === "string") {
						vTargetConfig = {
							name: vTargetConfig
						};
					}
					return vTargetConfig;
				});
			},

			/**
			 * Loads targets with type "Component" recursively and set the new hash based on the given
			 * <code>oComponentTargetInfo</code>.
			 *
			 * @param {object} oComponentTargetInfo See the documentation {@link sap.ui.core.routing.Router#navTo}
			 * @param {boolean} [bParentRouteSwitched=false] Whether a new route in the Parent router is matched
			 * @returns {Promise} A promise which resolves after all targets with type "Component" are loaded and
			 *  the new hash based on the given component target info is set.
			 *
			 * @private
			 */
			_changeHashWithComponentTargets: function(oComponentTargetInfo, bParentRouteSwitched) {
				var aTargetsConfig = this._alignTargetsConfig(this._oConfig.target),
					oTargets = this._oRouter._oTargets,
					aTargets,
					aLoadedPromises;

				if (aTargetsConfig && aTargetsConfig.length > 0 && oTargets) {
					aTargets = oTargets.getTarget(aTargetsConfig);

					if (!Array.isArray(aTargets)) {
						aTargets = [aTargets];
					}
				} else {
					aTargets = [];
				}

				var that = this;
				aLoadedPromises = aTargets.map(function(oTarget, index) {
					if (oTarget._oOptions.type === "Component") {
						var pLoaded = oTarget._load({
							prefix: aTargetsConfig[index].prefix,
							propagateTitle: aTargetsConfig[index].hasOwnProperty("propagateTitle") ? aTargetsConfig[index].propagateTitle : that._oRouter._oConfig.propagateTitle
						});

						return pLoaded
							.then(function(oComponent) {
								var oRouter = oComponent.getRouter(),
									oHashChanger = oRouter && oRouter.getHashChanger(),
									oRouteInfo = oComponentTargetInfo && oComponentTargetInfo[aTargetsConfig[index].name],
									sRouteName = oRouteInfo && oRouteInfo.route,
									oRoute = oRouter && oRouter.getRoute(sRouteName),
									bRouteSwitched;

								if (oRouteInfo) {
									if (oRoute) {
										bRouteSwitched = oRouter._getLastMatchedRouteName() !== sRouteName;
										oHashChanger.setHash(oRoute.getURL(oRouteInfo.parameters), bParentRouteSwitched || !bRouteSwitched);
										return oRoute._changeHashWithComponentTargets(oRouteInfo.componentTargetInfo, bParentRouteSwitched || bRouteSwitched);
									} else {
										Log.error("Can not navigate to route with name '" + sRouteName + "' because the route does not exist in component with id '" + oComponent.getId() + "'");
									}
								}
							});
					}
				});

				return Promise.all(aLoadedPromises);
			},

			/**
			 * Returns the pattern of the route. If there are multiple patterns, the first pattern is returned
			 *
			 * @return {string} the routes pattern
			 * @public
			 */
			getPattern : function() {
				return this._aPattern[0];
			},

			/**
			 * Returns whether the given hash can be matched by the Route
			 *
			 * @param {string} sHash which will be tested by the Route
			 * @return {boolean} whether the hash can be matched
			 * @public
			 * @since 1.58.0
			 */
			match : function(sHash) {
				return this._aRoutes.some(function(oRoute) {
					return oRoute.match(sHash);
				});
			},

			/**
			 * The <code>matched</code> event is fired, when the current URL hash matches:
			 * <pre>
			 *  a. the pattern of the route.
			 *  b. the pattern of its sub-route.
			 *  c. the pattern of its nested route. When this occurs, the 'nestedRoute' parameter is set with the instance of nested route.
			 * </pre>
			 *
			 * Please refer to event {@link sap.ui.core.routing.Route#event:patternMatched patternMatched} for getting notified only when its own pattern is matched with the URL hash not its sub-routes or nested route.
			 *
			 * @name sap.ui.core.routing.Route#matched
			 * @event
			 * @param {sap.ui.base.Event} oEvent
			 * @param {sap.ui.base.EventProvider} oEvent.getSource
			 * @param {object} oEvent.getParameters
			 * @param {string} oEvent.getParameters.name The name of the route
			 * @param {object} oEvent.getParameters.arguments A key-value pair object which contains the arguments defined in the route
			 *  resolved with the corresponding information from the current URL hash
			 * @param {object} oEvent.getParameters.config The configuration object of the route
			 * @param {sap.ui.core.routing.Route} [oEvent.getParameters.nestedRoute] The nested route instance of this route. The event
			 *  is fired on this route because the pattern in the nested route is matched with the current URL hash. This parameter can be
			 *  used to decide whether the current route is matched because of its nested child route. For more information about nested
			 *  child route please refer to the documentation of oConfig.parent in {@link sap.ui.core.routing.Route#constructor}
			 * @param {sap.ui.core.mvc.View|sap.ui.core.ComponentContainer} oEvent.getParameters.view The first View or ComponentContainer instance
			 *  which is created out of the first target. If multiple targets are displayed, use oEvent.getParameters.views to get all instances
			 * @param {Array<sap.ui.core.mvc.View|sap.ui.core.ComponentContainer>} oEvent.getParameters.views All View or ComponentContainer
			 *  instances which are created out of the targets.
			 * @param {sap.ui.core.Control} oEvent.getParameters.targetControl The container control to which the first View or ComponentContainer
			 *  is added. If multiple targets are displayed, use oEvent.getParameters.targetControls to get all container controls
			 * @param {Array<sap.ui.core.Control>} oEvent.getParameters.targetControls The container controls to which the View or
			 *  ComponentContainer instances are added.
			 * @public
			 */

			/**
			 * Attaches event handler <code>fnFunction</code> to the {@link #event:matched matched} event of this
			 * <code>sap.ui.core.routing.Route</code>.
			 *
			 * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code>
			 * if specified, otherwise it will be bound to this <code>sap.ui.core.routing.Route</code> itself.
			 *
			 * @param {object}
			 *            [oData] An application-specific payload object that will be passed to the event handler
			 *            along with the event object when firing the event
			 * @param {function}
			 *            fnFunction The function to be called, when the event occurs
			 * @param {object}
			 *            [oListener] Context object to call the event handler with. Defaults to this
			 *            <code>sap.ui.core.routing.Route</code> itself
			 *
			 * @returns {this} Reference to <code>this</code> in order to allow method chaining
			 * @public
			 * @since 1.25.1
			 */
			attachMatched : function(oData, fnFunction, oListener) {
				return this.attachEvent("matched", oData, fnFunction, oListener);
			},

			/**
			 * Detaches event handler <code>fnFunction</code> from the {@link #event:matched matched} event of this
			 * <code>sap.ui.core.routing.Route</code>.
			 *
			 * The passed function and listener object must match the ones used for event registration.
			 *
			 * @param {function} fnFunction The function to be called, when the event occurs
			 * @param {object} [oListener] Context object on which the given function had to be called
			 * @returns {this} Reference to <code>this</code> in order to allow method chaining
			 * @public
			 * @since 1.25.1
			 */
			detachMatched : function(fnFunction, oListener) {
				return this.detachEvent("matched", fnFunction, oListener);
			},

			/**
			 * The <code>beforeMatched</code> event is fired before the corresponding target is loaded and placed, when the current URL hash matches:
			 * <pre>
			 *  a. the pattern of the route.
			 *  b. the pattern of its sub-route.
			 *  c. the pattern of its nested route. When this occurs, the 'nestedRoute' parameter is set with the instance of nested route.
			 * </pre>
			 *
			 * @name sap.ui.core.routing.Route#beforeMatched
			 * @event
			 * @param {sap.ui.base.Event} oEvent
			 * @param {sap.ui.base.EventProvider} oEvent.getSource
			 * @param {object} oEvent.getParameters
			 * @param {string} oEvent.getParameters.name The name of the route
			 * @param {object} oEvent.getParameters.arguments A key-value pair object which contains the arguments defined in the route
			 *  resolved with the corresponding information from the current URL hash
			 * @param {object} oEvent.getParameters.config The configuration object of the route
			 * @param {sap.ui.core.routing.Route} [oEvent.getParameters.nestedRoute] The nested route instance of this route. The event
			 *  is fired on this route because the pattern in the nested route is matched with the current URL hash. This parameter can be
			 *  used to decide whether the current route is matched because of its nested child route. For more information about nested
			 *  child route please refer to the documentation of oConfig.parent in {@link sap.ui.core.routing.Route#constructor}
			 * @public
			 * @since 1.46.1
			 */

			/**
			 * Attaches event handler <code>fnFunction</code> to the {@link #event:beforeMatched beforeMatched} event of this
			 * <code>sap.ui.core.routing.Route</code>.
			 *
			 * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code>
			 * if specified, otherwise it will be bound to this <code>sap.ui.core.routing.Route</code> itself.
			 *
			 * @param {object}
			 *            [oData] An application-specific payload object that will be passed to the event handler
			 *            along with the event object when firing the event
			 * @param {function}
			 *            fnFunction The function to be called, when the event occurs
			 * @param {object}
			 *            [oListener] Context object to call the event handler with. Defaults to this
			 *            <code>Route</code> itself
			 *
			 * @returns {this} Reference to <code>this</code> in order to allow method chaining
			 * @public
			 * @since 1.46.1
			 */
			attachBeforeMatched : function(oData, fnFunction, oListener) {
				return this.attachEvent("beforeMatched", oData, fnFunction, oListener);
			},

			/**
			 * Detaches event handler <code>fnFunction</code> from the {@link #event:beforeMatched beforeMatched} event of this
			 * <code>sap.ui.core.routing.Route</code>.
			 *
			 * The passed function and listener object must match the ones used for event registration.
			 *
			 * @param {function} fnFunction The function to be called, when the event occurs
			 * @param {object} [oListener] Context object on which the given function had to be called
			 * @returns {this} Reference to <code>this</code> in order to allow method chaining
			 * @public
			 * @since 1.46.1
			 */
			detachBeforeMatched : function(fnFunction, oListener) {
				return this.detachEvent("beforeMatched", fnFunction, oListener);
			},

			/**
			 * Fires event {@link #event:beforeMatched beforeMatched} to attached listeners.
			 *
			 * @param {object} [oParameters] Parameters to pass along with the event
			 *
			 * @returns {sap.ui.core.routing.Router} Reference to <code>this</code> in order to allow method chaining
			 * @protected
			 * @since 1.46.1
			 */
			fireBeforeMatched : function(oParameters) {
				this.fireEvent("beforeMatched", oParameters);
				return this;
			},

			/**
			 * The <code>patternMatched</code> event is fired, only when the current URL hash matches the pattern of the route.
			 *
			 * @name sap.ui.core.routing.Route#patternMatched
			 * @event
			 * @param {sap.ui.base.Event} oEvent
			 * @param {sap.ui.base.EventProvider} oEvent.getSource
			 * @param {object} oEvent.getParameters
			 * @param {string} oEvent.getParameters.name The name of the route
			 * @param {object} oEvent.getParameters.arguments A key-value pair object which contains the arguments defined in the route
			 *  resolved with the corresponding information from the current URL hash
			 * @param {object} oEvent.getParameters.config The configuration object of the route
			 * @param {sap.ui.core.mvc.View|sap.ui.core.ComponentContainer} oEvent.getParameters.view The first View or ComponentContainer instance
			 *  which is created out of the first target. If multiple targets are displayed, use oEvent.getParameters.views to get all instances
			 * @param {Array<sap.ui.core.mvc.View|sap.ui.core.ComponentContainer>} oEvent.getParameters.views All View or ComponentContainer
			 *  instances which are created out of the targets.
			 * @param {sap.ui.core.Control} oEvent.getParameters.targetControl The container control to which the first View or ComponentContainer
			 *  is added. If multiple targets are displayed, use oEvent.getParameters.targetControls to get all container controls
			 * @param {Array<sap.ui.core.Control>} oEvent.getParameters.targetControls The container controls to which the View or
			 *  ComponentContainer instances are added.
			 * @public
			 */

			/**
			 * Attaches event handler <code>fnFunction</code> to the {@link #event:patternMatched patternMatched} event of this
			 * <code>sap.ui.core.routing.Route</code>.
			 *
			 * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code>
			 * if specified, otherwise it will be bound to this <code>sap.ui.core.routing.Route</code> itself.
			 *
			 * @param {object}
			 *            [oData] An application-specific payload object that will be passed to the event handler
			 *            along with the event object when firing the event
			 * @param {function}
			 *            fnFunction The function to be called, when the event occurs
			 * @param {object}
			 *            [oListener] Context object to call the event handler with. Defaults to this
			 *            <code>Route</code> itself
			 *
			 * @returns {this} Reference to <code>this</code> in order to allow method chaining
			 * @public
			 * @since 1.25.1
			 */
			attachPatternMatched : function(oData, fnFunction, oListener) {
				return this.attachEvent("patternMatched", oData, fnFunction, oListener);
			},

			/**
			 * Detaches event handler <code>fnFunction</code> from the {@link #event:patternMatched patternMatched} event of this
			 * <code>sap.ui.core.routing.Route</code>.
			 *
			 * The passed function and listener object must match the ones used for event registration.
			 *
			 * @param {function} fnFunction The function to be called, when the event occurs
			 * @param {object} [oListener] Context object on which the given function had to be called
			 * @returns {this} Reference to <code>this</code> in order to allow method chaining
			 * @public
			 * @since 1.25.1
			 */
			detachPatternMatched : function(fnFunction, oListener) {
				return this.detachEvent("patternMatched", fnFunction, oListener);
			},

			/**
			 * The <code>switched</code> event is only fired, when the previously matched route has been left
			 * and another route is matched.
			 *
			 * @name sap.ui.core.routing.Route#switched
			 * @event
			 * @param {sap.ui.base.Event} oEvent
			 * @param {sap.ui.base.EventProvider} oEvent.getSource
			 * @param {object} oEvent.getParameters
			 * @param {string} oEvent.getParameters.name The name of the route
			 * @param {object} oEvent.getParameters.arguments A key-value pair object which contains the arguments defined in the route
			 *  resolved with the corresponding information from the current URL hash
			 * @param {object} oEvent.getParameters.config The configuration object of the route
			 * @private
			 * @ui5-restricted sap.ui.core.routing
			 * @since 1.62
			 */

			_validateConfig: function(oConfig) {
				if (!oConfig.name) {
					Log.error("A name has to be specified for every route", this);
				}

				if (oConfig.viewName) {
					Log.error("The 'viewName' option shouldn't be used in Route. please use 'view' instead", this);
				}
			},

			_convertToTargetOptions: function (oOptions) {
				return deepExtend(
					{},
					oOptions,
					{
						rootView: oOptions.targetParent,
						controlId: oOptions.targetControl,
						controlAggregation: oOptions.targetAggregation,
						clearControlAggregation: oOptions.clearTarget,
						viewName: oOptions.view,
						// no rename here
						viewType: oOptions.viewType,
						viewId: oOptions.viewId
					});
			},

			_getParentRoute: function (sParent) {
				var aParts = sParent.split(":");
				if (aParts.length === 1 || (aParts.length === 2 && !aParts[0]))  {
					return this._oRouter.getRoute(aParts[aParts.length - 1]);
				} else {
					assert(this._oRouter._oOwner, "No owner component for " + this._oRouter._oOwner.getId());
					var oOwnerComponent = Component.getOwnerComponentFor(this._oRouter._oOwner);
					while (oOwnerComponent) {
						if (oOwnerComponent.getMetadata().getName() === aParts[0]) {
							var oRouter = oOwnerComponent.getRouter();
							return oRouter.getRoute(aParts[1]);
						}
						oOwnerComponent = Component.getOwnerComponentFor(oOwnerComponent);
					}
					return null;
				}
			},

			/**
			 * Returns the arguments of the route which matches the given hash
			 *
			 * @param {string} sHash The hash
			 * @returns {object} An object containing the route arguments
			 * @private
			 * @ui5-restricted sap.ui.core
			 */
			getPatternArguments : function(sHash) {
				return this._aRoutes[0].extrapolate(sHash);
			}
		});


		Route.M_EVENTS = {
			BeforeMatched : "beforeMatched",
			Matched : "matched",
			PatternMatched : "patternMatched"
		};

		return Route;
});
