package com.afi.estimator.common
{
    import flash.xml.XMLDocument;
    import flash.xml.XMLNode;
    
    import mx.collections.ArrayCollection;
    import mx.utils.ObjectUtil;
    
    public class XMLEncoder
    {
        
        
        public function XMLEncoder(myXML:XMLDocument)
        {
            super();
            
            this.myXMLDoc = myXML ? myXML : new XMLDocument();
        }
        
        private var myXMLDoc:XMLDocument;
        private var arryPropName:String;
        private static const CLASS_INFO_OPTIONS:Object = {includeReadOnly:false, includeTransient:false};
        
        public function encodeValue(obj:Object, qname:QName, parentNode:XMLNode):XMLNode
        {
            var myElement:XMLNode;
            
            if (obj == null)
                return null;
            
            // Skip properties that are functions
            var typeType:uint = getDataTypeFromObject(obj);
            if (typeType == XMLEncoder.FUNCTION_TYPE)
                return null;
            
            if (typeType == XMLEncoder.XML_TYPE)
            {
                myElement = obj.cloneNode(true);
                parentNode.appendChild(myElement);
                return myElement;
            }
            myElement = myXMLDoc.createElement("foo")
            myElement.nodeName = qname.localName;
            parentNode.appendChild(myElement);
            
            
            if (typeType == XMLEncoder.OBJECT_TYPE)
            {
                var classInfo:Object = ObjectUtil.getClassInfo(obj, null, CLASS_INFO_OPTIONS);
                var properties:Array = classInfo.properties;
                var pCount:uint = properties.length;
                loop:    for (var p:uint = 0; p < pCount; p++)
                {
                    var fieldName:String = properties[p];
                    
                    fieldName=changePropName(fieldName,obj);
                    var propQName:QName = new QName("", fieldName);
                    var objType:uint;
                    var tempFieldName:String='';
                    var st:String=parentNode.toString();
                    if(obj.hasOwnProperty(fieldName)){
                         tempFieldName=fieldName;
                    }else{
                    
                         tempFieldName=getLowerCase(fieldName);
                    }
                    
                    
                    if(obj.hasOwnProperty(tempFieldName)){
                        objType = getDataTypeFromObject(obj[tempFieldName]);
                    }
                    
                    if (objType == XMLEncoder.ARRAY_TYPE){
                        this.arryPropName=getQName(tempFieldName);
                        doArrayMap(obj[tempFieldName],myElement);
                    }else if(!(isComplexType(obj[tempFieldName]))){
                        if( obj[tempFieldName].toString()!=null && obj[tempFieldName].toString()!='' ){
                            encodeValue(obj[tempFieldName], propQName, myElement);
                        }else{
                            continue loop;
                        }
                    }
                    else{
                        encodeValue(obj[tempFieldName], propQName, myElement);
                    }
                }
            }
            else if (typeType == XMLEncoder.ARRAY_TYPE)
            {
                doArrayMap(obj,myElement);
            }
            else
            {
                // Simple types fall through to here
                var valueString:String;
                
                if (typeType == XMLEncoder.DATE_TYPE)
                {
                    //valueString = encodeDate(obj as Date, "dateTime");
                }
                else if (typeType == XMLEncoder.NUMBER_TYPE)
                {
                    if (obj == Number.POSITIVE_INFINITY)
                        valueString = "INF";
                    else if (obj == Number.NEGATIVE_INFINITY)
                        valueString = "-INF";
                    else
                    {
                        var rep:String = obj.toString();
                        // see if its hex
                        var start:String = rep.substr(0, 2);
                        if (start == "0X" || start == "0x")
                        {
                            valueString = parseInt(rep).toString();
                        }
                        else
                        {
                            valueString = rep;
                        }
                    }
                }
                else
                {
                    valueString = obj.toString();
                }
                
                var valueNode:XMLNode = myXMLDoc.createTextNode(valueString);
                myElement.appendChild(valueNode);
            }
            
            return myElement;
        }
        private function doArrayMap(obj:Object,myElement:XMLNode):void{
            
            var numMembers:uint = obj.length;
            var localePropName:String=getPropName(arryPropName,obj[i]);
            var itemQName:QName = new QName("", localePropName);
            for (var i:uint = 0; i < numMembers; i++)
            {
                encodeValue(obj[i], itemQName, myElement);
            }
        }
        
        
        private function getPropName(propQName:String,obj:*):String{
            
            if(!(isComplexType(obj))){
                return getLowerCase(propQName);
            }else if(obj is Object){
                return getUpperCase(propQName);
            }else {
                return propQName
            }
        }
        
        private function isComplexType(obj:*):Boolean{
            
            if(obj is String || obj is Number || obj is Boolean || 
                obj is Date || obj is Function ||obj is XMLDocument){
                return false;
            }
            else {
                return true;
            }
        }
        private function changePropName(propQName:String,obj:Object):String{
            
            if(obj[propQName] is String || obj[propQName] is Number || obj[propQName] is Boolean || 
                obj[propQName] is Date || obj[propQName] is Array || obj[propQName] is Function ||obj[propQName] is XMLDocument){
                return getLowerCase(propQName);
            }else if(obj[propQName] is Object){
                return getUpperCase(propQName);
            }else {
                return propQName;
            }
            
        }
        private function getLowerCase(childLocalName:String):String{
            
            var lowerCaseSt:String='';
            if(childLocalName!=null && childLocalName!=''){
                lowerCaseSt=childLocalName.toLocaleLowerCase().charAt(0)+
                    childLocalName.substr(1,childLocalName.length);
            }
            return lowerCaseSt;
        }
        private function getUpperCase(childLocalName:String):String{
            var upperCaseSt:String='';
            if(childLocalName!=null && childLocalName!=''){
                upperCaseSt=childLocalName.toUpperCase().charAt(0)+
                    childLocalName.substr(1,childLocalName.length);
            }
            return upperCaseSt;
        }
        private function  getQName(childLocalName:String):String{
            var qName:String='';
            if(childLocalName!=null && childLocalName!=''){
                qName=childLocalName.substr(0,childLocalName.indexOf('List'));
            }
            return getUpperCase(qName);
        }
        private function getDataTypeFromObject(obj:Object):uint
        {
            if (obj is Number)
                return XMLEncoder.NUMBER_TYPE;
            else if (obj is Boolean)
                return XMLEncoder.BOOLEAN_TYPE;
            else if (obj is String)
                return XMLEncoder.STRING_TYPE;
            else if (obj is XMLDocument)
                return XMLEncoder.XML_TYPE;
            else if (obj is Date)
                return XMLEncoder.DATE_TYPE;
            else if (obj is ArrayCollection)
                return XMLEncoder.ARRAY_TYPE;
            else if (obj is Function)
                return XMLEncoder.FUNCTION_TYPE;
            else if (obj is Object)
                return XMLEncoder.OBJECT_TYPE;
            else
                return XMLEncoder.STRING_TYPE;
        }
        
        private static const NUMBER_TYPE:uint   = 0;
        private static const STRING_TYPE:uint   = 1;
        private static const OBJECT_TYPE:uint   = 2;
        private static const DATE_TYPE:uint     = 3;
        private static const BOOLEAN_TYPE:uint  = 4;
        private static const XML_TYPE:uint      = 5;
        private static const ARRAY_TYPE:uint    = 6;  // An array with a wrapper element
        private static const MAP_TYPE:uint      = 7;
        private static const ANY_TYPE:uint      = 8;
        private static const ROWSET_TYPE:uint   = 11;// CF QueryBean
        private static const DOC_TYPE:uint      = 13;
        private static const SCHEMA_TYPE:uint   = 14;
        private static const FUNCTION_TYPE:uint = 15; // We currently do not serialize properties of type function
        private static const ELEMENT_TYPE:uint  = 16;
        private static const BASE64_BINARY_TYPE:uint = 17;
        private static const HEX_BINARY_TYPE:uint = 18;
        
        
    }
}