package flare.data
{    
    /**
     * Utility class for parsing and representing data field values.
     */
    public class DataUtil
    {
        /** Constant indicating a numeric data type. */
        public static const NUMBER:int  = 0;
        /** Constant indicating an integer data type. */
        public static const INT:int     = 1;
        /** Constant indicating a Date data type. */
        public static const DATE:int    = 2;
        /** Constant indicating a String data type. */
        public static const STRING:int  = 3;
        /** Constant indicating an arbitrary Object data type. */
        public static const OBJECT:int  = 4;
        /** Constant indicating a boolean data type. */
        public static const BOOLEAN:int = 5;
        
        /**
         * Parse an input value given its data type.
         * @param val the value to parse
         * @param type the data type to parse as
         * @return the parsed data value
         */
        public static function parseValue(val:Object, type:int):Object
        {
            switch (type) {
                case NUMBER:
                    return Number(val);
                case INT:    
                    return int(val);
                case BOOLEAN:
                    return Boolean(val);
                case DATE:
                    var t:Number = val is Number ? Number(val) 
                                                 : Date.parse(String(val));
                    return isNaN(t) ? null : new Date(t);
                case STRING:
                    return String(val);
                default:        return val;
            }
        }
        
        /**
         * Returns the data type for the input string value. This method
         * attempts to parse the value as a number of different data types.
         * If successful, the matching data type is returned. If no parse
         * succeeds, this method returns the <code>STRING</code> constant.
         * @param s the string to parse
         * @return the inferred data type of the string contents
         */
        public static function type(s:String):int
        {
            if (!isNaN(Number(s))) return NUMBER;
            if (!isNaN(Date.parse(s))) return DATE;
            return STRING;
        }
        
        /**
         * Infers the data schema by checking values of the input data.
         * @param tuples an array of data tuples
         * @return the inferred schema
         */
        public static function inferSchema(tuples:Array):DataSchema
        {
            if (tuples==null || tuples.length==0) return null;
            
            var header:Array = [];
            for (var name:String in tuples[0]) {
                header.push(name);
            }
            var types:Array = new Array(header.length);
            
            // initialize data types
            for (var col:int=0; col<header.length; ++col) {
                types[col] = DataUtil.type(tuples[0][header[col]]);
            }
            
            // now process data to infer types
            for (var i:int = 2; i<tuples.length; ++i) {
                var tuple:Object = tuples[i];
                for (col=0; col<header.length; ++col) {
                    name = header[col];
                    var value:Object = tuple[name];
                    if (types[col] == -1 || value==null) continue;
                    
                    var type:int = 
                        value is Boolean ? BOOLEAN :
                        value is Date ? DATE :
                        value is int ? INT :
                        value is Number ? NUMBER :
                        value is String ? STRING : OBJECT;

                    if (types[col] != type) {
                        types[col] = -1;
                    }
                }
            }
            
            // finally, we create the schema
            var schema:DataSchema = new DataSchema();
            for (col=0; col<header.length; ++col) {
                schema.addField(new DataField(header[col],
                    types[col]==-1 ? DataUtil.STRING : types[col]));
            }
            return schema;
        }
        
    } // end of class DataUtil
}