<cfcomponent name="baseInterface">

    <!--- this is the inner CfC --->    
    <cfset variables.innerCfC = "">

    <!--- pseudo-constructor --->
    <cffunction name="init" 
                output="true" 
                returntype="void" 
                access="public">

        <cfargument name="wrappedCfC" 
                    type="WEB-INF.cftags.component">

        <cfset variables.innerCfC = arguments.wrappedCfC>
        
        <cfset checkInterface()>
        
    </cffunction>

    <cffunction name="getFuncs" 
                access="private" 
                returntype="struct">

        <cfargument name="object" 
                    required="true" 
                    type="struct">
		<!--- /**
		 * Returns a struct of all public functions in a cfc , inherited or not,
         * excepting constructors (init). Overridden functions are returned once
         * from the lowest possible point in the inheritance tree.
		 * Based (ripped off, really) on the function getComponentProps()
		 * by Robby Lansaw (robby@ohsogooey.com) at cflib.org
		 * @param object 	 The metadata from a component. (Required)
		 * @return Returns a struct of structs. 
		 */ --->
        <cfscript>
    	var myFuncs = StructNew();
    	var innerFuncs = StructNew();
		var i = ''; 
		var j = '';
    	
    	if (structKeyExists(object,'functions')) {
			for (i=1; i lte arraylen(object.functions);i = i+1) {
                if ((not StructKeyExists(myFuncs,object.functions[i].NAME)) and 
                    (object.functions[i].ACCESS is "PUBLIC") and 
                    (object.functions[i].NAME is not "INIT")){
                	StructInsert(myFuncs,object.functions[i].NAME,object.functions[i],false);
                }
            }
    	}
		if (listlast(object.extends.NAME,'.' ) neq "COMPONENT"){
            innerFuncs = getFuncs(object.EXTENDS);
            StructAppend(myFuncs,innerFuncs,false);
		}
        </cfscript>
        <cfreturn myFuncs>
    </cffunction>

    <cffunction name="arrayCompare" 
                access="private" 
                returntype="boolean">

        <cfargument name="LeftArray" 
                    required="true" 
                    type="array">

        <cfargument name="RightArray" 
                    required="true" 
                    type="array">

		<!--- /**
		 * Recursive functions to compare arrays and nested structures.
		 * Adaptation of the function arrayCompare()
		 * by Ja Carter (ja@nuorbit.com) at cflib.org
		 * @param LeftArray 	 The first array. (Required)
		 * @param RightArray 	 The second array. (Required)
		 * @return Returns a boolean. 
		 */ --->
        <cfscript>
		var result = true;
		var i = "";
		
		// Loop through the elements and compare them one at a time
		for (i=1;i lte arrayLen(LeftArray); i = i+1) {
			//elements is a structure, call structCompare()
			if (isStruct(LeftArray[i])){
				result = structCompare(LeftArray[i],RightArray[i]);
				if (NOT result) return false;
			//elements is an array, call arrayCompare()
			} else if (isArray(LeftArray[i])){
				result = arrayCompare(LeftArray[i],RightArray[i]);
				if (NOT result) return false;
			//A simple type comparison here
			} else {
				if(LeftArray[i] IS NOT RightArray[i]) return false;
			}
		}        
        </cfscript>
        <cfreturn true>
    
    </cffunction>

    <cffunction name="structCompare"
                access="private"
                returntype="boolean"
                output="false">

        <cfargument name="LeftStruct" 
                    required="true" 
                    type="struct">

        <cfargument name="RightStruct" 
                    required="true" 
                    type="struct">

		<!--- /**
		 * Recursive functions to compare structures and arrays.
		 * Adaptation the function structCompare()
		 * by Ja Carter (ja@nuorbit.com) at cflib.org
		 * @param LeftStruct 	 The first struct. (Required)
		 * @param RightStruct 	 The second structure. (Required)
		 * @return Returns a boolean. 
		 */ --->
		 <cfscript>
			var result = true;
			var LeftStructKeys = "";
			var RightStructKeys = "";
			var key = "";
			
			//Make sure both structures have the same keys
			LeftStructKeys = StructKeyList(LeftStruct);
			RightStructKeys = StructKeyList(RightStruct);
			if(LeftStructKeys neq RightStructKeys) return false;	
			
			// Loop through the keys and compare them one at a time
			for (key in LeftStruct) {
				//Key is a structure, call structCompare()
				if (isStruct(LeftStruct[key])){
					result = structCompare(LeftStruct[key],RightStruct[key]);
					if (NOT result) return false;
				//Key is an array, call arrayCompare()
				} else if (isArray(LeftStruct[key])){
					result = arrayCompare(LeftStruct[key],RightStruct[key]);
					if (NOT result) return false;
				// A simple type comparison here, but DON'T compare the value of the FOUNDIN key!
				} else {
					if((key IS NOT "FOUNDIN") and (LeftStruct[key] IS NOT RightStruct[key])) return false;
				}
			}
		 </cfscript>

		 <cfreturn true>


    </cffunction>


    <cffunction name="checkInterface" 
                access="private" 
                returntype="void"
                output="true">


        <cfset InterfaceMetaData = GetMetaData(this)>
        <cfset MemberMetaData = GetMetaData(variables.innerCfC)>

        <cfset InterfaceFuncs = getFuncs(InterfaceMetaData)>
        <cfset MemberFuncs = getFuncs(MemberMetaData)>
        
        <cfset MissingFunctions = "">

        <cfloop collection="#InterfaceFuncs#" item="function">
            <cfif not StructKeyExists(MemberFuncs,#function#) or
                  not structCompare(InterfaceFuncs["#function#"],MemberFuncs["#function#"])>
                <cfset MissingFunctions = ListAppend(MissingFunctions,'#function#')>
            </cfif>
        </cfloop>

        <cfif ListLen(MissingFunctions)>

	        <cfthrow type="interfaceException"
	                 message="interface implementation error"
	                 detail="the CfC #MemberMetaData.name# (#MemberMetaData.path#) 
	                         does not correctly implement the interface #InterfaceMetaData.name# (#InterfaceMetaData.path#) 
	                         - missing #MissingFunctions#">
        </cfif>

 

    </cffunction>

</cfcomponent>