public AvatarAppearance(WearableData wearableData) : base() // Note: indra has a protected constructor since it subclasses this in VOAvatar
        {
            IsDummy             = false;
            TexSkinColor        = null; //TODO: Not implemented
            TexHairColor        = null; //TODO: Not implemented
            TexEyeColor         = null; //TODO: Not implemented
            PelvisToFoot        = 0f;
            HeadOffset          = Vector3.zero;
            Root                = null;
            WearableData        = wearableData;
            NumBones            = 0;
            NumCollisionVolumes = 0;
            IsBuilt             = false;
            InitFlags           = 0;

            if (WearableData == null)
            {
                throw new Exception("AvatarAppearance.constructor: Can't create an avatar with a null wearableData.");
            }

            for (int i = 0; i < (int)AvatarAppearanceDictionary.BakedTextureIndex.NumIndices; i++)
            {
                BakedTextureDatas.Add(new BakedTextureData()
                {
                    LastTextureID = IndraConstants.IMG_DEFAULT_AVATAR,
                    TexLayerSet   = null,
                    IsLoaded      = false,
                    IsUsed        = false,
                    MaskTexName   = 0,
                    TextureIndex  = AvatarAppearanceDictionary.BakedToLocalTextureIndex((AvatarAppearanceDictionary.BakedTextureIndex)i)
                });
            }
        }
        /// <summary>
        /// Replaces the Wearables collection with a list of new wearable items
        /// </summary>
        /// <param name="wearableItems">Wearable items to replace the Wearables collection with</param>
        private void ReplaceOutfit(List<InventoryWearable> wearableItems)
        {
            Dictionary<WearableType, WearableData> newWearables = new Dictionary<WearableType, WearableData>();

            lock (Wearables)
            {
                // Preserve body parts from the previous set of wearables. They may be overwritten,
                // but cannot be missing in the new set
                foreach (KeyValuePair<WearableType, WearableData> entry in Wearables)
                {
                    if (entry.Value.AssetType == AssetType.Bodypart)
                        newWearables[entry.Key] = entry.Value;
                }

                // Add the given wearables to the new wearables collection
                for (int i = 0; i < wearableItems.Count; i++)
                {
                    InventoryWearable wearableItem = wearableItems[i];

                    WearableData wd = new WearableData();
                    wd.AssetID = wearableItem.AssetUUID;
                    wd.AssetType = wearableItem.AssetType;
                    wd.ItemID = wearableItem.UUID;
                    wd.WearableType = wearableItem.WearableType;

                    newWearables[wearableItem.WearableType] = wd;
                }

                // Replace the Wearables collection
                Wearables = newWearables;
            }
        }
        /// <summary>
        /// Add a list of wearables to the current outfit and set appearance
        /// </summary>
        /// <param name="wearableItems">List of wearable inventory items to
        /// be added to the outfit</param>
        public void AddToOutfit(List<InventoryItem> wearableItems)
        {
            List<InventoryWearable> wearables = new List<InventoryWearable>();
            List<InventoryItem> attachments = new List<InventoryItem>();

            for (int i = 0; i < wearableItems.Count; i++)
            {
                InventoryItem item = wearableItems[i];

                if (item is InventoryWearable)
                    wearables.Add((InventoryWearable)item);
                else if (item is InventoryAttachment || item is InventoryObject)
                    attachments.Add(item);
            }

            lock (Wearables)
            {
                // Add the given wearables to the wearables collection
                for (int i = 0; i < wearables.Count; i++)
                {
                    InventoryWearable wearableItem = wearables[i];

                    WearableData wd = new WearableData();
                    wd.AssetID = wearableItem.AssetUUID;
                    wd.AssetType = wearableItem.AssetType;
                    wd.ItemID = wearableItem.UUID;
                    wd.WearableType = wearableItem.WearableType;

                    Wearables[wearableItem.WearableType] = wd;
                }
            }

            if (attachments.Count > 0)
            {
                AddAttachments(attachments, false, false);
            }

            if (wearables.Count > 0)
            {
                SendAgentIsNowWearing();
                DelayedRequestSetAppearance();
            }
        }
        /// <summary>
        /// Populates textures and visual params from a decoded asset
        /// </summary>
        /// <param name="wearable">Wearable to decode</param>
        private void DecodeWearableParams(WearableData wearable)
        {
            Dictionary<VisualAlphaParam, float> alphaMasks = new Dictionary<VisualAlphaParam, float>();
            List<ColorParamInfo> colorParams = new List<ColorParamInfo>();

            // Populate collection of alpha masks from visual params
            // also add color tinting information
            foreach (KeyValuePair<int, float> kvp in wearable.Asset.Params)
            {
                if (!VisualParams.Params.ContainsKey(kvp.Key)) continue;

                VisualParam p = VisualParams.Params[kvp.Key];

                ColorParamInfo colorInfo = new ColorParamInfo();
                colorInfo.WearableType = wearable.WearableType;
                colorInfo.VisualParam = p;
                colorInfo.Value = kvp.Value;

                // Color params
                if (p.ColorParams.HasValue)
                {
                    colorInfo.VisualColorParam = p.ColorParams.Value;

                    // If this is not skin, just add params directly
                    if (wearable.WearableType != WearableType.Skin)
                    {
                        colorParams.Add(colorInfo);
                    }
                    else
                    {
                        // For skin we skip makeup params for now and use only the 3
                        // that are used to determine base skin tone
                        // Param 108 - Rainbow Color
                        // Param 110 - Red Skin (Ruddiness)
                        // Param 111 - Pigment
                        if (kvp.Key == 108 || kvp.Key == 110 || kvp.Key == 111)
                        {
                            colorParams.Add(colorInfo);
                        }
                    }
                }

                // Add alpha mask
                if (p.AlphaParams.HasValue && p.AlphaParams.Value.TGAFile != string.Empty && !p.IsBumpAttribute && !alphaMasks.ContainsKey(p.AlphaParams.Value))
                {
                    alphaMasks.Add(p.AlphaParams.Value, kvp.Value);
                }

                // Alhpa masks can also be specified in sub "driver" params
                if (p.Drivers != null)
                {
                    for (int i = 0; i < p.Drivers.Length; i++)
                    {
                        if (VisualParams.Params.ContainsKey(p.Drivers[i]))
                        {
                            VisualParam driver = VisualParams.Params[p.Drivers[i]];
                            if (driver.AlphaParams.HasValue && driver.AlphaParams.Value.TGAFile != string.Empty && !driver.IsBumpAttribute && !alphaMasks.ContainsKey(driver.AlphaParams.Value))
                            {
                                alphaMasks.Add(driver.AlphaParams.Value, kvp.Value);
                            }
                        }
                    }
                }
            }

            Color4 wearableColor = Color4.White; // Never actually used
            if (colorParams.Count > 0)
            {
                wearableColor = GetColorFromParams(colorParams);
                Logger.DebugLog("Setting tint " + wearableColor + " for " + wearable.WearableType);
            }

            // Loop through all of the texture IDs in this decoded asset and put them in our cache of worn textures
            foreach (KeyValuePair<AvatarTextureIndex, UUID> entry in wearable.Asset.Textures)
            {
                int i = (int)entry.Key;

                // Update information about color and alpha masks for this texture
                Textures[i].AlphaMasks = alphaMasks;
                Textures[i].Color = wearableColor;

                // If this texture changed, update the TextureID and clear out the old cached texture asset
                if (Textures[i].TextureID != entry.Value)
                {
                    // Treat DEFAULT_AVATAR_TEXTURE as null
                    if (entry.Value != DEFAULT_AVATAR_TEXTURE)
                        Textures[i].TextureID = entry.Value;
                    else
                        Textures[i].TextureID = UUID.Zero;
                    Logger.DebugLog("Set " + entry.Key + " to " + Textures[i].TextureID, Client);

                    Textures[i].Texture = null;
                }
            }
        }
        protected void AgentWearablesUpdateHandler(object sender, PacketReceivedEventArgs e)
        {
            bool changed = false;
            AgentWearablesUpdatePacket update = (AgentWearablesUpdatePacket)e.Packet;

            lock (Wearables)
            {
                #region Test if anything changed in this update

                for (int i = 0; i < update.WearableData.Length; i++)
                {
                    AgentWearablesUpdatePacket.WearableDataBlock block = update.WearableData[i];

                    if (block.AssetID != UUID.Zero)
                    {
                        WearableData wearable;
                        if (Wearables.TryGetValue((WearableType)block.WearableType, out wearable))
                        {
                            if (wearable.AssetID != block.AssetID || wearable.ItemID != block.ItemID)
                            {
                                // A different wearable is now set for this index
                                changed = true;
                                break;
                            }
                        }
                        else
                        {
                            // A wearable is now set for this index
                            changed = true;
                            break;
                        }
                    }
                    else if (Wearables.ContainsKey((WearableType)block.WearableType))
                    {
                        // This index is now empty
                        changed = true;
                        break;
                    }
                }

                #endregion Test if anything changed in this update

                if (changed)
                {
                    Logger.DebugLog("New wearables received in AgentWearablesUpdate");
                    Wearables.Clear();

                    for (int i = 0; i < update.WearableData.Length; i++)
                    {
                        AgentWearablesUpdatePacket.WearableDataBlock block = update.WearableData[i];

                        if (block.AssetID != UUID.Zero)
                        {
                            WearableType type = (WearableType)block.WearableType;

                            WearableData data = new WearableData();
                            data.Asset = null;
                            data.AssetID = block.AssetID;
                            data.AssetType = WearableTypeToAssetType(type);
                            data.ItemID = block.ItemID;
                            data.WearableType = type;

                            // Add this wearable to our collection
                            Wearables[type] = data;
                        }
                    }
                }
                else
                {
                    Logger.DebugLog("Duplicate AgentWearablesUpdate received, discarding");
                }
            }

            if (changed)
            {
                // Fire the callback
                OnAgentWearables(new AgentWearablesReplyEventArgs());
            }
        }
        private void AgentWearablesUpdateHandler(Packet packet, Simulator simulator)
        {
            // Lock to prevent a race condition with multiple AgentWearables packets
            lock (WearablesRequestEvent)
            {
                AgentWearablesUpdatePacket update = (AgentWearablesUpdatePacket)packet;

                // Reset the Wearables collection
                lock (Wearables.Dictionary) Wearables.Dictionary.Clear();

                for (int i = 0; i < update.WearableData.Length; i++)
                {
                    if (update.WearableData[i].AssetID != UUID.Zero)
                    {
                        WearableType type = (WearableType)update.WearableData[i].WearableType;
                        WearableData data = new WearableData();
                        data.Item = new InventoryWearable(update.WearableData[i].ItemID);
                        data.Item.WearableType = type;
                        data.Item.AssetType = WearableTypeToAssetType(type);
                        data.Item.AssetUUID = update.WearableData[i].AssetID;

                        // Add this wearable to our collection
                        lock (Wearables.Dictionary) Wearables.Dictionary[type] = data;
                    }
                }
            }

            WearablesRequestEvent.Set();
        }
        // this method will download the assets for all inventory items in iws
        private void ReplaceOutfitWearables(List<InventoryWearable> iws)
        {
            lock (Wearables.Dictionary)
            {
                Dictionary<WearableType, WearableData> preserve = new Dictionary<WearableType,WearableData>();
                
                foreach (KeyValuePair<WearableType,WearableData> kvp in Wearables.Dictionary)
                {
                    if (kvp.Value.Item.AssetType == AssetType.Bodypart)
                            preserve.Add(kvp.Key, kvp.Value);
                }

                Wearables.Dictionary = preserve;
            
                foreach (InventoryWearable iw in iws)
                {
                    WearableData wd = new WearableData();
                    wd.Item = iw; 
                    Wearables.Dictionary[wd.Item.WearableType] = wd;
                }
            }
        }
        private void AgentWearablesUpdateHandler(Packet packet, Simulator simulator)
        {
            bool changed = false;
            AgentWearablesUpdatePacket update = (AgentWearablesUpdatePacket)packet;

            lock (Wearables)
            {
                #region Test if anything changed in this update

                for (int i = 0; i < update.WearableData.Length; i++)
                {
                    AgentWearablesUpdatePacket.WearableDataBlock block = update.WearableData[i];

                    if (block.AssetID != UUID.Zero)
                    {
                        WearableData wearable;
                        if (Wearables.TryGetValue((WearableType)block.WearableType, out wearable))
                        {
                            if (wearable.AssetID != block.AssetID || wearable.ItemID != block.ItemID)
                            {
                                // A different wearable is now set for this index
                                changed = true;
                                break;
                            }
                        }
                        else
                        {
                            // A wearable is now set for this index
                            changed = true;
                            break;
                        }
                    }
                    else if (Wearables.ContainsKey((WearableType)block.WearableType))
                    {
                        // This index is now empty
                        changed = true;
                        break;
                    }
                }

                #endregion Test if anything changed in this update

                if (changed)
                {
                    Logger.DebugLog("New wearables received in AgentWearablesUpdate");
                    Wearables.Clear();

                    for (int i = 0; i < update.WearableData.Length; i++)
                    {
                        AgentWearablesUpdatePacket.WearableDataBlock block = update.WearableData[i];

                        if (block.AssetID != UUID.Zero)
                        {
                            WearableType type = (WearableType)block.WearableType;

                            WearableData data = new WearableData();
                            data.Asset = null;
                            data.AssetID = block.AssetID;
                            data.AssetType = WearableTypeToAssetType(type);
                            data.ItemID = block.ItemID;
                            data.WearableType = type;

                            // Add this wearable to our collection
                            Wearables[type] = data;
                        }
                    }
                }
                else
                {
                    Logger.DebugLog("Duplicate AgentWearablesUpdate received, discarding");
                }
            }

            if (changed)
            {
                // Fire the callback
                AgentWearablesCallback callback = OnAgentWearables;
                if (callback != null)
                {
                    try { callback(); }
                    catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); }
                }
            }
        }