/**
 * This class generates UUID's according to RFC 4122. This class has a default id property.
 * This means that a single instance is shared unless the id property is overridden. Thus,
 * two {@link Ext.data.Model} instances configured like the following share one generator:
 *
 *     Ext.define('MyApp.data.MyModelX', {
 *         extend: 'Ext.data.Model',
 *         identifier: 'uuid'
 *     });
 *
 *     Ext.define('MyApp.data.MyModelY', {
 *         extend: 'Ext.data.Model',
 *         identifier: 'uuid'
 *     });
 *
 * This allows all models using this class to share a commonly configured instance.
 *
 * # Using Version 1 ("Sequential") UUID's
 *
 * If a server can provide a proper timestamp and a "cryptographic quality random number"
 * (as described in RFC 4122), the shared instance can be configured as follows:
 *
 *     Ext.data.identifier.Uuid.Global.reconfigure({
 *         version: 1,
 *         clockSeq: clock, // 14 random bits
 *         salt: salt,      // 48 secure random bits (the Node field)
 *         timestamp: ts    // timestamp per Section 4.1.4
 *     });
 *
 *     // or these values can be split into 32-bit chunks:
 *
 *     Ext.data.identifier.Uuid.Global.reconfigure({
 *         version: 1,
 *         clockSeq: clock,
 *         salt: { lo: saltLow32, hi: saltHigh32 },
 *         timestamp: { lo: timestampLow32, hi: timestamptHigh32 }
 *     });
 *
 * This approach improves the generator's uniqueness by providing a valid timestamp and
 * higher quality random data. Version 1 UUID's should not be used unless this information
 * can be provided by a server and care should be taken to avoid caching of this data.
 *
 * See [http://www.ietf.org/rfc/rfc4122.txt](http://www.ietf.org/rfc/rfc4122.txt) for details.
 */
Ext.define('Ext.data.identifier.Uuid', {
    extend: 'Ext.data.identifier.Generator',
    alias: 'data.identifier.uuid',
 
    /**
     * Provides a way to determine if this identifier supports creating unique IDs. Proxies like
     * {@link Ext.data.proxy.LocalStorage} need the identifier to create unique IDs and will check
     * this property.
     * @property isUnique
     * @type Boolean
     * @private
     */
    isUnique: true,
 
    config: {
        /**
         * @cfg {String} id
         * The id for this generator instance. By default all model instances share the same
         * UUID generator instance. By specifying an id other than 'uuid', a unique generator
         * instance will be created for the Model.
         */
        id: null
    },
 
    /**
     * @cfg {Number} [version=4]
     * The Version of UUID. Supported values are:
     *
     *  * 1 : Time-based, "sequential" UUID. To use this type of generator, you must also
     *  specify the `salt`, `timestamp` and `clock` properties. For details on the values
     *  and how a server should produce them, see RFC 4122. Use of this type of generator
     *  produces values that are easier to read since they are sequential, but requires
     *  some care to initialize properly and still ensure their uniqueness.
     *
     *  * 4 : Pseudo-random UUID. This is the simplest form and requires no configuration
     *  and hence is the default type.
     */
 
    /**
     * @cfg {Number/Object} [salt]
     * This value is a 48-bit number. This can be a number or an object with `hi` and `lo`
     * properties where `lo` is the low 32-bits and `hi` is the upper 16 bits.
     *
     * Only applicable when `version` is set to `1`.
     */
 
    /**
     * @cfg {Number/Object} [timestamp]
     * When created, this value is a 60-bit number. This can be a number or an object with
     * `hi` and `lo` properties where `lo` is the low 32-bits and `hi` is the upper 28 bits.
     *
     * Only applicable when `version` is set to `1`.
     */
 
    /**
     * @cfg {Number} [clockSeq]
     * A clock value to help avoid duplicates.
     *
     * Only applicable when `version` is set to `1`.
     */
 
    constructor: function(config) {
        this.callParent([ config ]);
 
        this.reconfigure(config);
    },
 
    /**
     * Reconfigures this generator given new config properties. The only values that this
     * changes are `version` and, if `version` is 1, its related config properties.
     */
    reconfigure: function(config) {
        var cls = this.self;
 
        this.generate = (config && config.version === 1)
            ? cls.createSequential(config.salt, config.timestamp, config.clockSeq)
            : cls.createRandom();
    },
 
    clone: null,
 
    statics: {
        createRandom: function() {
            var pattern = 'xxxxxxxx-xxxx-4xxx-Rxxx-xMxxxxxxxxxx'.split(''),
                hex = '0123456789abcdef'.split(''),
                length = pattern.length,
                parts = [];
 
            return function() {
                var r, c, i;
                
                for (= 0; i < length; ++i) {
                    c = pattern[i];
 
                    if (!== '-' && c !== '4') {
                        r = Math.random() * 16;
                        r = (=== 'R') ? (& 3 | 8) : (| ((=== 'M') ? 1 : 0));
                        c = hex[r]; // above floors r so always 0-15
                    }
 
                    parts[i] = c;
                }
 
                return parts.join('');
            };
        },
 
        createSequential: function(salt, time, clockSeq) {
            var parts = [],
                twoPow32 = Math.pow(2, 32),
                saltLo = salt.lo,
                saltHi = salt.hi,
                timeLo = time.lo,
                timeHi = time.hi,
                toHex;
                
            toHex = function(value, length) {
                var ret = value.toString(16).toLowerCase();
                
                if (ret.length > length) {
                    ret = ret.substring(ret.length - length); // right-most digits
                }
                else if (ret.length < length) {
                    ret = Ext.String.leftPad(ret, length, '0');
                }
                
                return ret;
            };
 
            if (typeof salt === 'number') {
                saltHi = Math.floor(salt / twoPow32);
                saltLo = Math.floor(salt - saltHi * twoPow32);
            }
            
            if (typeof time === 'number') {
                timeHi = Math.floor(time / twoPow32);
                timeLo = Math.floor(time - timeHi * twoPow32);
            }
 
            // Set multicast bit: "the least significant bit of the first octet of the
            // node ID" (nodeId = salt for this implementation):
            saltHi |= 0x100;
 
            parts[3] = toHex(0x80 | ((clockSeq >>> 8) & 0x3F), 2) +
                       toHex(clockSeq & 0xFF, 2);
            parts[4] = toHex(saltHi, 4) + toHex(saltLo, 8);
 
            /*
               The magic decoder ring (derived from RFC 4122 Section 4.2.2):
 
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |                          time_low                             |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |           time_mid            |  ver  |        time_hi        |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |res|  clock_hi |   clock_low   |    salt 0   |M|     salt 1    |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |                         salt (2-5)                            |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
                         time_mid      clock_hi (low 6 bits)
                time_low     | time_hi |clock_lo
                    |        |     |   || salt[0]
                    |        |     |   ||   | salt[1..5]
                    v        v     v   vv   v v
                    0badf00d-aced-1def-b123-dfad0badbeef
                                  ^    ^     ^
                            version    |     multicast (low bit)
                                       |
                                    reserved (upper 2 bits)
            */
            return function() {
                parts[0] = toHex(timeLo, 8);
                parts[1] = toHex(timeHi & 0xFFFF, 4);
                parts[2] = toHex(((timeHi >>> 16) & 0xFFF) | (1 << 12), 4);
 
                // sequentially increment the timestamp...
                ++timeLo;
                
                if (timeLo >= twoPow32) { // if (overflow)
                    timeLo = 0;
                    ++timeHi;
                }
 
                return parts.join('-');
            };
        }
    }
}, function() {
    this.Global = new this({
        id: 'uuid'
    });
});