Package com.ngt.jopenmetaverse.shared.sim

Source Code of com.ngt.jopenmetaverse.shared.sim.AssetManager

/**
* A library to interact with Virtual Worlds such as OpenSim
* Copyright (C) 2012  Jitendra Chauhan, Email: jitendra.chauhan@gmail.com
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.ngt.jopenmetaverse.shared.sim;

import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Observable;
import com.ngt.jopenmetaverse.shared.cap.http.CapsHttpClient;
import com.ngt.jopenmetaverse.shared.cap.http.CapsHttpRequestCompletedArg;
import com.ngt.jopenmetaverse.shared.cap.http.DownloadManager;
import com.ngt.jopenmetaverse.shared.cap.http.HttpBaseDownloadProgressArg;
import com.ngt.jopenmetaverse.shared.cap.http.HttpBaseRequestCompletedArg;
import com.ngt.jopenmetaverse.shared.cap.http.DownloadManager.DownloadRequest;
import com.ngt.jopenmetaverse.shared.protocol.AbortXferPacket;
import com.ngt.jopenmetaverse.shared.protocol.AssetUploadCompletePacket;
import com.ngt.jopenmetaverse.shared.protocol.AssetUploadRequestPacket;
import com.ngt.jopenmetaverse.shared.protocol.ConfirmXferPacketPacket;
import com.ngt.jopenmetaverse.shared.protocol.InitiateDownloadPacket;
import com.ngt.jopenmetaverse.shared.protocol.PacketType;
import com.ngt.jopenmetaverse.shared.protocol.RequestXferPacket;
import com.ngt.jopenmetaverse.shared.protocol.SendXferPacketPacket;
import com.ngt.jopenmetaverse.shared.protocol.TransferAbortPacket;
import com.ngt.jopenmetaverse.shared.protocol.TransferInfoPacket;
import com.ngt.jopenmetaverse.shared.protocol.TransferPacketPacket;
import com.ngt.jopenmetaverse.shared.protocol.TransferRequestPacket;
import com.ngt.jopenmetaverse.shared.sim.asset.Asset;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetAnimation;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetBodypart;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetClothing;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetGesture;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetLandmark;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetMesh;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetNotecard;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetPrim;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetScriptBinary;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetScriptText;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetSound;
import com.ngt.jopenmetaverse.shared.sim.asset.AssetTexture;
import com.ngt.jopenmetaverse.shared.sim.asset.pipeline.TexturePipeline;
import com.ngt.jopenmetaverse.shared.sim.asset.pipeline.TexturePipeline.TextureRequestState;
import com.ngt.jopenmetaverse.shared.sim.cache.AssetCache;
import com.ngt.jopenmetaverse.shared.sim.events.AutoResetEvent;
import com.ngt.jopenmetaverse.shared.sim.events.Caps;
import com.ngt.jopenmetaverse.shared.sim.events.EventObservable;
import com.ngt.jopenmetaverse.shared.sim.events.EventObserver;
import com.ngt.jopenmetaverse.shared.sim.events.MethodDelegate;
import com.ngt.jopenmetaverse.shared.sim.events.PacketReceivedEventArgs;
import com.ngt.jopenmetaverse.shared.sim.events.ThreadPool;
import com.ngt.jopenmetaverse.shared.sim.events.ThreadPoolFactory;
import com.ngt.jopenmetaverse.shared.sim.events.asm.AssetDownload;
import com.ngt.jopenmetaverse.shared.sim.events.asm.AssetReceivedCallbackArgs;
import com.ngt.jopenmetaverse.shared.sim.events.asm.AssetUpload;
import com.ngt.jopenmetaverse.shared.sim.events.asm.AssetUploadEventArgs;
import com.ngt.jopenmetaverse.shared.sim.events.asm.BakedTextureUploadedCallbackArgs;
import com.ngt.jopenmetaverse.shared.sim.events.asm.ImageDownload;
import com.ngt.jopenmetaverse.shared.sim.events.asm.ImageReceiveProgressEventArgs;
import com.ngt.jopenmetaverse.shared.sim.events.asm.InitiateDownloadEventArgs;
import com.ngt.jopenmetaverse.shared.sim.events.asm.MeshDownloadCallbackArgs;
import com.ngt.jopenmetaverse.shared.sim.events.asm.TextureDownloadCallbackArgs;
import com.ngt.jopenmetaverse.shared.sim.events.asm.Transfer;
import com.ngt.jopenmetaverse.shared.sim.events.asm.XferDownload;
import com.ngt.jopenmetaverse.shared.sim.events.asm.XferReceivedEventArgs;
import com.ngt.jopenmetaverse.shared.sim.inventory.InventoryItem;
import com.ngt.jopenmetaverse.shared.sim.message.LindenMessages.UploadBakedTextureMessage;
import com.ngt.jopenmetaverse.shared.sim.message.LindenMessages.UploaderRequestComplete;
import com.ngt.jopenmetaverse.shared.sim.message.LindenMessages.UploaderRequestUpload;
import com.ngt.jopenmetaverse.shared.structureddata.OSD;
import com.ngt.jopenmetaverse.shared.structureddata.OSDFormat;
import com.ngt.jopenmetaverse.shared.structureddata.OSDMap;
import com.ngt.jopenmetaverse.shared.types.Enums.AssetType;
import com.ngt.jopenmetaverse.shared.types.UUID;
import com.ngt.jopenmetaverse.shared.util.JLogger;
import com.ngt.jopenmetaverse.shared.util.Utils;

public class AssetManager {
   
    //region Enums

    public enum EstateAssetTypeint
    {
        None (-1),
        Covenant (0);
        private int index;
        EstateAssetTypeint(int index)
    {
      this.index = index;
    }    

    public int getIndex()
    {
      return index;
    }

    private static final Map<Integer,EstateAssetTypeint> lookup 
    = new HashMap<Integer,EstateAssetTypeint>();

    static {
      for(EstateAssetTypeint s : EnumSet.allOf(EstateAssetTypeint.class))
        lookup.put(s.getIndex(), s);
    }

    public static EstateAssetTypeint get(Integer index)
    {
      return lookup.get(index);
    }  
    }

    /// <summary>
    ///
    /// </summary>
    public enum StatusCode
    {
        /// <summary>OK</summary>
        OK(0),
        /// <summary>Transfer completed</summary>
        Done(1),
        /// <summary></summary>
        Skip(2),
        /// <summary></summary>
        Abort(3),
        /// <summary>Unknown error occurred</summary>
        Error(-1),
        /// <summary>Equivalent to a 404 error</summary>
        UnknownSource(-2),
        /// <summary>Client does not have permission for that resource</summary>
        InsufficientPermissions(-3),
        /// <summary>Unknown status</summary>
        Unknown(-4);
        private int index;
        StatusCode(int index)
    {
      this.index = index;
    }    

    public int getIndex()
    {
      return index;
    }

    private static final Map<Integer,StatusCode> lookup 
    = new HashMap<Integer,StatusCode>();

    static {
      for(StatusCode s : EnumSet.allOf(StatusCode.class))
        lookup.put(s.getIndex(), s);
    }

    public static StatusCode get(Integer index)
    {
      return lookup.get(index);
    }  
    }

    /// <summary>
    ///
    /// </summary>
    public enum ChannelType
    {
        /// <summary></summary>
        Unknown(0),
        /// <summary>Unknown</summary>
        Misc (1),
        /// <summary>Virtually all asset transfers use this channel</summary>
        Asset (2);
       
        private int index;
        ChannelType(int index)
    {
      this.index = index;
    }    

    public int getIndex()
    {
      return index;
    }

    private static final Map<Integer,ChannelType> lookup 
    = new HashMap<Integer,ChannelType>();

    static {
      for(ChannelType s : EnumSet.allOf(ChannelType.class))
        lookup.put(s.getIndex(), s);
    }

    public static ChannelType get(Integer index)
    {
      return lookup.get(index);
    }  
    }

    /// <summary>
    ///
    /// </summary>
    public enum SourceType
    {
        /// <summary></summary>
        Unknown(0),
        /// <summary>Asset from the asset server</summary>
        Asset (2),
        /// <summary>Inventory item</summary>
        SimInventoryItem (3),
        /// <summary>Estate asset, such as an estate covenant</summary>
        SimEstate (4);
       
        private int index;
        SourceType(int index)
    {
      this.index = index;
    }    

    public int getIndex()
    {
      return index;
    }

    private static final Map<Integer,SourceType> lookup 
    = new HashMap<Integer,SourceType>();

    static {
      for(SourceType s : EnumSet.allOf(SourceType.class))
        lookup.put(s.getIndex(), s);
    }

    public static SourceType get(Integer index)
    {
      return lookup.get(index);
    }  
    }

    /// <summary>
    ///
    /// </summary>
    public enum TargetType
    {
        /// <summary></summary>
        Unknown (0),
        /// <summary></summary>
        File (1),
        /// <summary></summary>
        VFile (2);
       
        private int index;
        TargetType(int index)
    {
      this.index = index;
    }    

    public int getIndex()
    {
      return index;
    }

    private static final Map<Integer,TargetType> lookup 
    = new HashMap<Integer,TargetType>();

    static {
      for(TargetType s : EnumSet.allOf(TargetType.class))
        lookup.put(s.getIndex(), s);
    }

    public static TargetType get(Integer index)
    {
      return lookup.get(index);
    }  
    }

    /// <summary>
    ///
    /// </summary>
    public static enum ImageType
    {
        /// <summary></summary>
        Normal((byte)0),
        /// <summary></summary>
        Baked ((byte)1);
    private byte index;
    ImageType(byte index)
    {
      this.index = index;
    }    

    public byte getIndex()
    {
      return index;
    }

    private static final Map<Byte,ImageType> lookup  = new HashMap<Byte,ImageType>();

    static {
      for(ImageType s : EnumSet.allOf(ImageType.class))
        lookup.put(s.getIndex(), s);
    }

    public static ImageType get(Byte index)
    {
      return lookup.get(index);
    }
    }

    /// <summary>
    /// Image file format
    /// </summary>
    public enum ImageCodec
    {
        Invalid((byte)0),
        RGB((byte)1),
        J2C((byte)2),
        BMP((byte)3),
        TGA((byte)4),
        JPEG((byte)5),
        DXT ((byte)6),
        PNG ((byte)7);
       
      private byte index;
      ImageCodec(byte index)
    {
      this.index = index;
    }    

    public byte getIndex()
    {
      return index;
    }

    private static final Map<Byte,ImageCodec> lookup  = new HashMap<Byte,ImageCodec>();

    static {
      for(ImageCodec s : EnumSet.allOf(ImageCodec.class))
        lookup.put(s.getIndex(), s);
    }

    public static ImageCodec get(Byte index)
    {
      return lookup.get(index);
    }
    }

    public static enum TransferError
    {
        None(0),
        Failed(-1),
        AssetNotFound(-3),
        AssetNotFoundInDatabase(-4),
        InsufficientPermissions(-5),
        EOF(-39),
        CannotOpenFile(-42),
        FileNotFound(-43),
        FileIsEmpty(-44),
        TCPTimeout(-23016),
        CircuitGone(-23017);
        private int index;
        TransferError(int index)
    {
      this.index = index;
    }    

    public int getIndex()
    {
      return index;
    }

    private static final Map<Integer,TransferError> lookup 
    = new HashMap<Integer,TransferError>();

    static {
      for(TransferError s : EnumSet.allOf(TransferError.class))
        lookup.put(s.getIndex(), s);
    }

    public static TransferError get(Integer index)
    {
      return lookup.get(index);
    }
    }

    public static class UploadRequestResult
    {
      UUID transactionID;
      UUID agentID;
     
    public UploadRequestResult() {
      super();
      transactionID = new UUID();
      agentID = new UUID();
    }
   
    public UploadRequestResult(UUID transactionID, UUID agentID) {
      super();
      this.transactionID = transactionID;
      this.agentID = agentID;
    }
    public UUID getTransactionID() {
      return transactionID;
    }
    public void setTransactionID(UUID transactionID) {
      this.transactionID = transactionID;
    }
    public UUID getAgentID() {
      return agentID;
    }
    public void setAgentID(UUID agentID) {
      this.agentID = agentID;
    }
     
    }
   
    //endregion Enums  
   
 
    /// <summary>Number of milliseconds to wait for a transfer header packet if out of order data was received</summary>
    final int TRANSFER_HEADER_TIMEOUT = 1000 * 15;

    //region Delegates
    /// <summary>
    /// Callback used for various asset download requests
    /// </summary>
    /// <param name="transfer">Transfer information</param>
    /// <param name="asset">Downloaded asset, null on fail</param>
   
    public MethodDelegate<Void, AssetReceivedCallbackArgs> assetReceivedCallback;
//    public delegate void AssetReceivedCallback(AssetDownload transfer, Asset asset);
    /// <summary>
    /// Callback used upon competition of baked texture upload
    /// </summary>
    /// <param name="newAssetID">Asset UUID of the newly uploaded baked texture</param>
    public MethodDelegate<Void, BakedTextureUploadedCallbackArgs> bakedTextureUploadedCallback;
   
//    public delegate void BakedTextureUploadedCallback(UUID newAssetID);
    /// <summary>
    /// A callback that fires upon the completition of the RequestMesh call
    /// </summary>
    /// <param name="success">Was the download successfull</param>
    /// <param name="assetMesh">Resulting mesh or null on problems</param>

    public MethodDelegate<Void, MeshDownloadCallbackArgs> meshDownloadCallback;
//    public delegate void MeshDownloadCallback(boolean success, AssetMesh assetMesh);

    //endregion Delegates

    //region Events
    private EventObservable<XferReceivedEventArgs> onXferReceived = new EventObservable<XferReceivedEventArgs>();
    public void registerOnXferReceived(EventObserver<XferReceivedEventArgs> o)
    {
      onXferReceived.addObserver(o);
    }
    public void unregisterOnXferReceived(EventObserver<XferReceivedEventArgs> o)
    {
      onXferReceived.deleteObserver(o);
    }
    private EventObservable<AssetUploadEventArgs> onAssetUploaded = new EventObservable<AssetUploadEventArgs>();
    public void registerOnAssetUploaded(EventObserver<AssetUploadEventArgs> o)
    {
      onAssetUploaded.addObserver(o);
    }
    public void unregisterOnAssetUploaded(EventObserver<AssetUploadEventArgs> o)
    {
      onAssetUploaded.deleteObserver(o);
    }
    private EventObservable<AssetUploadEventArgs> onUploadProgress = new EventObservable<AssetUploadEventArgs>();
    public void registerOnUploadProgress(EventObserver<AssetUploadEventArgs> o)
    {
      onUploadProgress.addObserver(o);
    }
    public void unregisterOnUploadProgress(EventObserver<AssetUploadEventArgs> o)
    {
      onUploadProgress.deleteObserver(o);
    }
    private EventObservable<InitiateDownloadEventArgs> onInitiateDownload = new EventObservable<InitiateDownloadEventArgs>();
    public void registerOnInitiateDownload(EventObserver<InitiateDownloadEventArgs> o)
    {
      onInitiateDownload.addObserver(o);
    }
    public void unregisterOnInitiateDownload(EventObserver<InitiateDownloadEventArgs> o)
    {
      onInitiateDownload.deleteObserver(o);
    }
    private EventObservable<ImageReceiveProgressEventArgs> onImageReceiveProgress = new EventObservable<ImageReceiveProgressEventArgs>();
    public void registerOnImageReceiveProgress(EventObserver<ImageReceiveProgressEventArgs> o)
    {
      onImageReceiveProgress.addObserver(o);
    }
    public void unregisterOnImageReceiveProgress(EventObserver<ImageReceiveProgressEventArgs> o)
    {
      onImageReceiveProgress.deleteObserver(o);
    }
   
//    //region XferReceived
//    /// <summary>The event subscribers. null if no subcribers</summary>
//    private EventHandler<XferReceivedEventArgs> m_XferReceivedEvent;
//
//    /// <summary>Raises the XferReceived event</summary>
//    /// <param name="e">A XferReceivedEventArgs object containing the
//    /// data returned from the simulator</param>
//    protected virtual void OnXferReceived(XferReceivedEventArgs e)
//    {
//        EventHandler<XferReceivedEventArgs> handler = m_XferReceivedEvent;
//        if (handler != null)
//            handler(this, e);
//    }
//
//    /// <summary>Thread sync lock object</summary>
//    private readonly object m_XferReceivedLock = new object();
//
//    /// <summary>Raised when the simulator responds sends </summary>
//    public event EventHandler<XferReceivedEventArgs> XferReceived
//    {
//        add { lock (m_XferReceivedLock) { m_XferReceivedEvent += value; } }
//        remove { lock (m_XferReceivedLock) { m_XferReceivedEvent -= value; } }
//    }
//    //endregion
//
//    //region AssetUploaded
//    /// <summary>The event subscribers. null if no subcribers</summary>
//    private EventHandler<AssetUploadEventArgs> m_AssetUploadedEvent;
//
//    /// <summary>Raises the AssetUploaded event</summary>
//    /// <param name="e">A AssetUploadedEventArgs object containing the
//    /// data returned from the simulator</param>
//    protected virtual void OnAssetUploaded(AssetUploadEventArgs e)
//    {
//        EventHandler<AssetUploadEventArgs> handler = m_AssetUploadedEvent;
//        if (handler != null)
//            handler(this, e);
//    }
//
//    /// <summary>Thread sync lock object</summary>
//    private readonly object m_AssetUploadedLock = new object();
//
//    /// <summary>Raised during upload completes</summary>
//    public event EventHandler<AssetUploadEventArgs> AssetUploaded
//    {
//        add { lock (m_AssetUploadedLock) { m_AssetUploadedEvent += value; } }
//        remove { lock (m_AssetUploadedLock) { m_AssetUploadedEvent -= value; } }
//    }
//    //endregion
//
//    //region UploadProgress
//    /// <summary>The event subscribers. null if no subcribers</summary>
//    private EventHandler<AssetUploadEventArgs> m_UploadProgressEvent;
//
//    /// <summary>Raises the UploadProgress event</summary>
//    /// <param name="e">A UploadProgressEventArgs object containing the
//    /// data returned from the simulator</param>
//    protected virtual void OnUploadProgress(AssetUploadEventArgs e)
//    {
//        EventHandler<AssetUploadEventArgs> handler = m_UploadProgressEvent;
//        if (handler != null)
//            handler(this, e);
//    }
//
//    /// <summary>Thread sync lock object</summary>
//    private readonly object m_UploadProgressLock = new object();
//
//    /// <summary>Raised during upload with progres update</summary>
//    public event EventHandler<AssetUploadEventArgs> UploadProgress
//    {
//        add { lock (m_UploadProgressLock) { m_UploadProgressEvent += value; } }
//        remove { lock (m_UploadProgressLock) { m_UploadProgressEvent -= value; } }
//    }
//    //endregion UploadProgress
//
//    //region InitiateDownload
//    /// <summary>The event subscribers. null if no subcribers</summary>
//    private EventHandler<InitiateDownloadEventArgs> m_InitiateDownloadEvent;
//
//    /// <summary>Raises the InitiateDownload event</summary>
//    /// <param name="e">A InitiateDownloadEventArgs object containing the
//    /// data returned from the simulator</param>
//    protected virtual void OnInitiateDownload(InitiateDownloadEventArgs e)
//    {
//        EventHandler<InitiateDownloadEventArgs> handler = m_InitiateDownloadEvent;
//        if (handler != null)
//            handler(this, e);
//    }
//
//    /// <summary>Thread sync lock object</summary>
//    private readonly object m_InitiateDownloadLock = new object();
//
//    /// <summary>Fired when the simulator sends an InitiateDownloadPacket, used to download terrain .raw files</summary>
//    public event EventHandler<InitiateDownloadEventArgs> InitiateDownload
//    {
//        add { lock (m_InitiateDownloadLock) { m_InitiateDownloadEvent += value; } }
//        remove { lock (m_InitiateDownloadLock) { m_InitiateDownloadEvent -= value; } }
//    }
//    //endregion InitiateDownload
//
//    //region ImageReceiveProgress
//    /// <summary>The event subscribers. null if no subcribers</summary>
//    private EventHandler<ImageReceiveProgressEventArgs> m_ImageReceiveProgressEvent;
//
//    /// <summary>Raises the ImageReceiveProgress event</summary>
//    /// <param name="e">A ImageReceiveProgressEventArgs object containing the
//    /// data returned from the simulator</param>
//    protected virtual void OnImageReceiveProgress(ImageReceiveProgressEventArgs e)
//    {
//        EventHandler<ImageReceiveProgressEventArgs> handler = m_ImageReceiveProgressEvent;
//        if (handler != null)
//            handler(this, e);
//    }
//
//    /// <summary>Thread sync lock object</summary>
//    private readonly object m_ImageReceiveProgressLock = new object();
//
//    /// <summary>Fired when a texture is in the process of being downloaded by the TexturePipeline class</summary>
//    public event EventHandler<ImageReceiveProgressEventArgs> ImageReceiveProgress
//    {
//        add { lock (m_ImageReceiveProgressLock) { m_ImageReceiveProgressEvent += value; } }
//        remove { lock (m_ImageReceiveProgressLock) { m_ImageReceiveProgressEvent -= value; } }
//    }
//    //endregion ImageReceiveProgress
//
//    //endregion Events

   
   
   
  private static ThreadPool threadPool = ThreadPoolFactory.getThreadPool();
   
    /// <summary>Texture download cache</summary>
    public AssetCache Cache;

    private TexturePipeline Texture;

    private DownloadManager HttpDownloads;

    private GridClient Client;

    private Map<UUID, Transfer> Transfers = new HashMap<UUID, Transfer>();

    private AssetUpload PendingUpload;
    private Object PendingUploadLock = new Object();
    private volatile boolean WaitingForUploadConfirm = false;

    /// <summary>
    /// Default constructor
    /// </summary>
    /// <param name="client">A reference to the GridClient object</param>
    public AssetManager(GridClient client)
    {
        Client = client;
        Cache = new AssetCache(client);
        Texture = new TexturePipeline(client);
        HttpDownloads = new DownloadManager();

        //        // Transfer packets for downloading large assets
        //        // Client.network.RegisterCallback(PacketType.TransferInfo, TransferInfoHandler);
        Client.network.RegisterCallback(PacketType.TransferInfo, new EventObserver<PacketReceivedEventArgs>()
            {
          @Override
          public void handleEvent(Observable o,PacketReceivedEventArgs arg) {
            try{ TransferInfoHandler(o, arg);}
            catch(Exception e) {JLogger.warn(Utils.getExceptionStackTraceAsString(e));}
          }}
            );
       
        //        // Client.network.RegisterCallback(PacketType.TransferPacket, TransferPacketHandler);
        Client.network.RegisterCallback(PacketType.TransferPacket, new EventObserver<PacketReceivedEventArgs>()
            {
          @Override
          public void handleEvent(Observable o,PacketReceivedEventArgs arg) {
            try{ TransferPacketHandler(o, arg);}
            catch(Exception e) {JLogger.warn(Utils.getExceptionStackTraceAsString(e));}
          }}
            );
       
        //        // Xfer packets for uploading large assets
        //        // Client.network.RegisterCallback(PacketType.RequestXfer, RequestXferHandler);
        Client.network.RegisterCallback(PacketType.RequestXfer, new EventObserver<PacketReceivedEventArgs>()
            {
          @Override
          public void handleEvent(Observable o,PacketReceivedEventArgs arg) {
            try{ RequestXferHandler(o, arg);}
            catch(Exception e) {JLogger.warn(Utils.getExceptionStackTraceAsString(e));}
          }}
            );
       
        //        // Client.network.RegisterCallback(PacketType.ConfirmXferPacket, ConfirmXferPacketHandler);
        Client.network.RegisterCallback(PacketType.ConfirmXferPacket, new EventObserver<PacketReceivedEventArgs>()
            {
          @Override
          public void handleEvent(Observable o,PacketReceivedEventArgs arg) {
            try{ ConfirmXferPacketHandler(o, arg);}
            catch(Exception e) {JLogger.warn(Utils.getExceptionStackTraceAsString(e));}
          }}
            );
        //        // Client.network.RegisterCallback(PacketType.AssetUploadComplete, AssetUploadCompleteHandler);

        Client.network.RegisterCallback(PacketType.AssetUploadComplete, new EventObserver<PacketReceivedEventArgs>()
            {
          @Override
          public void handleEvent(Observable o,PacketReceivedEventArgs arg) {
            try{ AssetUploadCompleteHandler(o, arg);}
            catch(Exception e) {JLogger.warn(Utils.getExceptionStackTraceAsString(e));}
          }}
            );
       
                // Xfer packets for downloading misc assets
        //        // Client.network.RegisterCallback(PacketType.SendXferPacket, SendXferPacketHandler);
        Client.network.RegisterCallback(PacketType.SendXferPacket, new EventObserver<PacketReceivedEventArgs>()
            {
          @Override
          public void handleEvent(Observable o,PacketReceivedEventArgs arg) {
            try{ SendXferPacketHandler(o, arg);}
            catch(Exception e) {JLogger.warn(Utils.getExceptionStackTraceAsString(e));}
          }}
            );
       
        //        // Client.network.RegisterCallback(PacketType.AbortXfer, AbortXferHandler);
        Client.network.RegisterCallback(PacketType.AbortXfer, new EventObserver<PacketReceivedEventArgs>()
            {
          @Override
          public void handleEvent(Observable o,PacketReceivedEventArgs arg) {
            try{ AbortXferHandler(o, arg);}
            catch(Exception e) {JLogger.warn(Utils.getExceptionStackTraceAsString(e));}
          }}
            );
       
        //        // Simulator is responding to a request to download a file
        //        // Client.network.RegisterCallback(PacketType.InitiateDownload, InitiateDownloadPacketHandler);
        Client.network.RegisterCallback(PacketType.InitiateDownload, new EventObserver<PacketReceivedEventArgs>()
            {
          @Override
          public void handleEvent(Observable o,PacketReceivedEventArgs arg) {
            try{ InitiateDownloadPacketHandler(o, arg);}
            catch(Exception e) {JLogger.warn(Utils.getExceptionStackTraceAsString(e));}
          }}
            );
    }
   
    /// <summary>
    /// Request an asset download
    /// </summary>
    /// <param name="assetID">Asset UUID</param>
    /// <param name="type">Asset type, must be correct for the transfer to succeed</param>
    /// <param name="priority">Whether to give this transfer an elevated priority</param>
    /// <param name="callback">The callback to fire when the simulator responds with the asset data</param>
    public void RequestAsset(UUID assetID, AssetType type, boolean priority,
        MethodDelegate<Void, AssetReceivedCallbackArgs> callback)
    {
        RequestAsset(assetID, type, priority, SourceType.Asset, UUID.Random(), callback);
    }

    /// <summary>
    /// Request an asset download
    /// </summary>
    /// <param name="assetID">Asset UUID</param>
    /// <param name="type">Asset type, must be correct for the transfer to succeed</param>
    /// <param name="priority">Whether to give this transfer an elevated priority</param>
    /// <param name="sourceType">Source location of the requested asset</param>
    /// <param name="callback">The callback to fire when the simulator responds with the asset data</param>
    public void RequestAsset(UUID assetID, AssetType type, boolean priority, SourceType sourceType, MethodDelegate<Void, AssetReceivedCallbackArgs> callback)
    {
        RequestAsset(assetID, type, priority, sourceType, UUID.Random(), callback);
    }

    /// <summary>
    /// Request an asset download
    /// </summary>
    /// <param name="assetID">Asset UUID</param>
    /// <param name="type">Asset type, must be correct for the transfer to succeed</param>
    /// <param name="priority">Whether to give this transfer an elevated priority</param>
    /// <param name="sourceType">Source location of the requested asset</param>
    /// <param name="transactionID">UUID of the transaction</param>
    /// <param name="callback">The callback to fire when the simulator responds with the asset data</param>
    public void RequestAsset(UUID assetID, AssetType type, boolean priority,
        SourceType sourceType, UUID transactionID,
        MethodDelegate<Void, AssetReceivedCallbackArgs> callback)
    {
        AssetDownload transfer = new AssetDownload();
        transfer.ID = transactionID;
        transfer.AssetID = assetID;
        //transfer.AssetType = type; // Set in TransferInfoHandler.
        transfer.Priority = 100.0f + (priority ? 1.0f : 0.0f);
        transfer.Channel = ChannelType.Asset;
        transfer.Source = sourceType;
        transfer.Simulator = Client.network.getCurrentSim();
        transfer.Callback = callback;

        // Check asset cache first
        if (callback != null && Cache.hasAsset(assetID))
        {
            byte[] data = Cache.getCachedAssetBytes(assetID);
            transfer.AssetData = data;
            transfer.Success = true;
            transfer.Status = StatusCode.OK;

            Asset asset = CreateAssetWrapper(type);
            asset.AssetData = data;
            asset.setAssetID(assetID);

            try { callback.execute(new AssetReceivedCallbackArgs(transfer, asset)); }
            catch (Exception e) { JLogger.error(e); }

            return;
        }

        // Add this transfer to the dictionary
        synchronized (Transfers)
        {
          Transfers.put(transfer.ID, transfer);
        }

        // Build the request packet and send it
        TransferRequestPacket request = new TransferRequestPacket();
        request.TransferInfo.ChannelType = (int)transfer.Channel.getIndex();
        request.TransferInfo.Priority = transfer.Priority;
        request.TransferInfo.SourceType = (int)transfer.Source.getIndex();
        request.TransferInfo.TransferID = transfer.ID;

        byte[] paramField = new byte[20];
        Utils.arraycopy(assetID.GetBytes(), 0, paramField, 0, 16);
        Utils.arraycopy(Utils.intToBytes(Utils.ubyteToInt(type.getIndex())), 0, paramField, 16, 4);
        request.TransferInfo.Params = paramField;

        Client.network.SendPacket(request, transfer.Simulator);
    }

    /// <summary>
    /// Request an asset download through the almost deprecated Xfer system
    /// </summary>
    /// <param name="filename">Filename of the asset to request</param>
    /// <param name="deleteOnCompletion">Whether or not to delete the asset
    /// off the server after it is retrieved</param>
    /// <param name="useBigPackets">Use large transfer packets or not</param>
    /// <param name="vFileID">UUID of the file to request, if filename is
    /// left empty</param>
    /// <param name="vFileType">Asset type of <code>vFileID</code>, or
    /// <code>AssetType.Unknown</code> if filename is not empty</param>
    /// <param name="fromCache">Sets the FilePath in the request to Cache
    /// (4) if true, otherwise Unknown (0) is used</param>
    /// <returns></returns>
    public BigInteger RequestAssetXfer(String filename, boolean deleteOnCompletion, boolean useBigPackets, UUID vFileID, AssetType vFileType,
        boolean fromCache)
    {
        UUID uuid = UUID.Random();
        BigInteger id = uuid.GetULong();

        XferDownload transfer = new XferDownload();
        transfer.XferID = id;
        transfer.ID = new UUID(id.longValue()); // Our dictionary tracks transfers with UUIDs, so convert the ulong back
        transfer.Filename = filename;
        transfer.VFileID = vFileID;
        transfer.AssetType = vFileType;

        // Add this transfer to the dictionary
        synchronized(Transfers)
        {
          Transfers.put(transfer.ID, transfer);
        }

        RequestXferPacket request = new RequestXferPacket();
        request.XferID.ID = id;
        request.XferID.Filename = Utils.stringToBytesWithTrailingNullByte(filename);
        request.XferID.FilePath = fromCache ? (byte)4 : (byte)0;
        request.XferID.DeleteOnCompletion = deleteOnCompletion;
        request.XferID.UseBigPackets = useBigPackets;
        request.XferID.VFileID = vFileID;
        request.XferID.VFileType = (short)Utils.ubyteToInt(vFileType.getIndex());

        Client.network.SendPacket(request);

        return id;
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="assetID">Use UUID.Zero if you do not have the
    /// asset ID but have all the necessary permissions</param>
    /// <param name="itemID">The item ID of this asset in the inventory</param>
    /// <param name="taskID">Use UUID.Zero if you are not requesting an
    /// asset from an object inventory</param>
    /// <param name="ownerID">The owner of this asset</param>
    /// <param name="type">Asset type</param>
    /// <param name="priority">Whether to prioritize this asset download or not</param>
    /// <param name="callback"></param>
    public void RequestInventoryAsset(UUID assetID, UUID itemID, UUID taskID,
        UUID ownerID, AssetType type, boolean priority,
        MethodDelegate<Void, AssetReceivedCallbackArgs> callback)
    {
        AssetDownload transfer = new AssetDownload();
        transfer.ID = UUID.Random();
        transfer.AssetID = assetID;
        //transfer.AssetType = type; // Set in TransferInfoHandler.
        transfer.Priority = 100.0f + (priority ? 1.0f : 0.0f);
        transfer.Channel = ChannelType.Asset;
        transfer.Source = SourceType.SimInventoryItem;
        transfer.Simulator = Client.network.getCurrentSim();
        transfer.Callback = callback;

        // Check asset cache first
        if (callback != null && Cache.hasAsset(assetID))
        {
            byte[] data = Cache.getCachedAssetBytes(assetID);
            transfer.AssetData = data;
            transfer.Success = true;
            transfer.Status = StatusCode.OK;

            Asset asset = CreateAssetWrapper(type);
            asset.AssetData = data;
            asset.setAssetID(assetID);

            try { callback.execute(new AssetReceivedCallbackArgs(transfer, asset)); }
            catch (Exception e) { JLogger.error(e); }

            return;
        }

        // Add this transfer to the dictionary
        synchronized(Transfers)
        {
          Transfers.put(transfer.ID, transfer);
        }

        // Build the request packet and send it
        TransferRequestPacket request = new TransferRequestPacket();
        request.TransferInfo.ChannelType = (int)transfer.Channel.getIndex();
        request.TransferInfo.Priority = transfer.Priority;
        request.TransferInfo.SourceType = (int)transfer.Source.getIndex();
        request.TransferInfo.TransferID = transfer.ID;

        byte[] paramField = new byte[100];
        Utils.arraycopy(Client.self.getAgentID().GetBytes(), 0, paramField, 0, 16);
        Utils.arraycopy(Client.self.getSessionID().GetBytes(), 0, paramField, 16, 16);
        Utils.arraycopy(ownerID.GetBytes(), 0, paramField, 32, 16);
        Utils.arraycopy(taskID.GetBytes(), 0, paramField, 48, 16);
        Utils.arraycopy(itemID.GetBytes(), 0, paramField, 64, 16);
        Utils.arraycopy(assetID.GetBytes(), 0, paramField, 80, 16);
        Utils.arraycopy(Utils.intToBytes(Utils.ubyteToInt(type.getIndex())), 0, paramField, 96, 4);
        request.TransferInfo.Params = paramField;

        Client.network.SendPacket(request, transfer.Simulator);
    }

    public void RequestInventoryAsset(InventoryItem item, boolean priority, MethodDelegate<Void, AssetReceivedCallbackArgs> callback)
    {
        RequestInventoryAsset(item.AssetUUID, item.UUID, UUID.Zero, item.OwnerID, item.AssetType, priority, callback);
    }

    public void RequestEstateAsset() throws Exception
    {
        throw new Exception("This function is not implemented yet!");
    }

    /// <summary>
    /// Used to force asset data into the PendingUpload property, ie: for raw terrain uploads
    /// </summary>
    /// <param name="assetData">An AssetUpload object containing the data to upload to the simulator</param>
    public void SetPendingAssetUploadData(AssetUpload assetData)
    {
        synchronized(PendingUploadLock)
        {
            PendingUpload = assetData;
        }
    }

    /// <summary>
    /// Request an asset be uploaded to the simulator
    /// </summary>
    /// <param name="asset">The <seealso cref="Asset"/> Object containing the asset data</param>
    /// <param name="storeLocal">If True, the asset once uploaded will be stored on the simulator
    /// in which the client was connected in addition to being stored on the asset server</param>
    /// <returns>The <seealso cref="UUID"/> of the transfer, can be used to correlate the upload with
    /// events being fired</returns>
    public UUID RequestUpload(Asset asset, boolean storeLocal)
    {
        if (asset.AssetData == null)
            throw new IllegalArgumentException("Can't upload an asset with no data (did you forget to call Encode?)");
       
        UploadRequestResult uploadResult = RequestUpload(asset.getAssetType(), asset.AssetData, storeLocal);
        asset.setAssetID(uploadResult.getAgentID());
        return uploadResult.getTransactionID();
    }

    /// <summary>
    /// Request an asset be uploaded to the simulator
    /// </summary>
    /// <param name="type">The <seealso cref="AssetType"/> of the asset being uploaded</param>
    /// <param name="data">A byte array containing the encoded asset data</param>
    /// <param name="storeLocal">If True, the asset once uploaded will be stored on the simulator
    /// in which the client was connected in addition to being stored on the asset server</param>
    /// <returns>The <seealso cref="UUID"/> of the transfer, can be used to correlate the upload with
    /// events being fired</returns>
    public UploadRequestResult RequestUpload(AssetType type, byte[] data, boolean storeLocal)
    {
        return RequestUpload(type, data, storeLocal);
    }

//    /// <summary>
//    /// Request an asset be uploaded to the simulator
//    /// </summary>
//    /// <param name="assetID"></param>
//    /// <param name="type">Asset type to upload this data as</param>
//    /// <param name="data">A byte array containing the encoded asset data</param>
//    /// <param name="storeLocal">If True, the asset once uploaded will be stored on the simulator
//    /// in which the client was connected in addition to being stored on the asset server</param>
//    /// <returns>The <seealso cref="UUID"/> of the transfer, can be used to correlate the upload with
//    /// events being fired</returns>
//    public UploadRequestResult RequestUpload(UUID assetID, AssetType type, byte[] data, boolean storeLocal)
//    {
//        return RequestUpload(assetID, type, data, storeLocal, UUID.Random());
//    }
   
    /// <summary>
    /// Initiate an asset upload
    /// </summary>
    /// <param name="assetID">The ID this asset will have if the
    /// upload succeeds</param>
    /// <param name="type">Asset type to upload this data as</param>
    /// <param name="data">Raw asset data to upload</param>
    /// <param name="storeLocal">Whether to store this asset on the local
    /// simulator or the grid-wide asset server</param>
    /// <param name="transactionID">The tranaction id for the upload <see cref="RequestCreateItem"/></param>
    /// <returns>The transaction ID of this transfer</returns>
    public UploadRequestResult RequestUpload(AssetType type, byte[] data, boolean storeLocal,
        UUID transactionID) throws Exception
    {
      UploadRequestResult result = new UploadRequestResult();
        AssetUpload upload = new AssetUpload();
        upload.AssetData = data;
        upload.AssetType = type;
        result.setAgentID(UUID.Combine(transactionID, Client.self.getSecureSessionID()));
        upload.AssetID = result.getAgentID();
        upload.Size = data.length;
        upload.XferID = new BigInteger("0");
        upload.ID = transactionID;

        // Build and send the upload packet
        AssetUploadRequestPacket request = new AssetUploadRequestPacket();
        request.AssetBlock.StoreLocal = storeLocal;
        request.AssetBlock.Tempfile = false; // This field is deprecated
        request.AssetBlock.TransactionID = transactionID;
        request.AssetBlock.Type = type.getIndex();

        boolean isMultiPacketUpload;
        if (data.length + 100 < Settings.MAX_PACKET_SIZE)
        {
            isMultiPacketUpload = false;
            JLogger.info(
                String.format("Beginning asset upload [Single Packet], ID: %s, AssetID: %s, Size: %d",
                upload.ID.toString(), upload.AssetID.toString(), upload.Size));

            Transfers.put(upload.ID, upload);

            // The whole asset will fit in this packet, makes things easy
            request.AssetBlock.AssetData = data;
            upload.Transferred = data.length;
        }
        else
        {
            isMultiPacketUpload = true;
            JLogger.info(
                String.format("Beginning asset upload [Multiple Packets], ID: %s, AssetID: %s, Size: %d",
                upload.ID.toString(), upload.AssetID.toString(), upload.Size));

            // Asset is too big, send in multiple packets
            request.AssetBlock.AssetData = Utils.EmptyBytes;
        }

        // Wait for the previous upload to receive a RequestXferPacket
        synchronized(PendingUploadLock)
        {
            final int UPLOAD_CONFIRM_TIMEOUT = 20 * 1000;
            final int SLEEP_INTERVAL = 50;
            int t = 0;
            AutoResetEvent sleepEvent = new AutoResetEvent(false);
            while (WaitingForUploadConfirm && t < UPLOAD_CONFIRM_TIMEOUT)
            {
//                System.Threading.Thread.Sleep(SLEEP_INTERVAL);
                sleepEvent.waitOne(SLEEP_INTERVAL);
                t += SLEEP_INTERVAL;
            }

            if (t < UPLOAD_CONFIRM_TIMEOUT)
            {
                if (isMultiPacketUpload)
                {
                    WaitingForUploadConfirm = true;
                }
                PendingUpload = upload;
                Client.network.SendPacket(request);

                result.setTransactionID(upload.ID);
                return result;
            }
            else
            {
                throw new Exception("Timeout waiting for previous asset upload to begin");
            }
        }
    }

    public void RequestUploadBakedTexture(final byte[] textureData, final MethodDelegate<Void,
        BakedTextureUploadedCallbackArgs> callback) throws Exception
    {
        URI url = null;

        Caps caps = Client.network.getCurrentSim().Caps;
        if (caps != null)
            url = caps.CapabilityURI("UploadBakedTexture");

        if (url != null)
        {
//            // Fetch the uploader capability
            CapsHttpClient request = new CapsHttpClient(url);
            MethodDelegate<Void, CapsHttpRequestCompletedArg> requestCompleteDelegate
                = new MethodDelegate<Void, CapsHttpRequestCompletedArg>()
                {
            public Void execute(CapsHttpRequestCompletedArg e) {
//              CapsHttpClient client = e.getClient();
              OSD result = e.getResult();
              Exception error = e.getError();             
                        if (error == null && (result instanceof OSDMap))
                        {
                            UploadBakedTextureMessage message = new UploadBakedTextureMessage();
                            message.Deserialize((OSDMap)result);
   
                            if (message.Request.State.equals("upload"))
                            {
                                URI uploadUrl = ((UploaderRequestUpload)message.Request).Url;
   
                                if (uploadUrl != null)
                                {
                                    // POST the asset data
                                  CapsHttpClient upload = new CapsHttpClient(uploadUrl);
                                  upload.setRequestCompleteDelegate(new MethodDelegate<Void, CapsHttpRequestCompletedArg>()
                                      {
                            public Void execute(CapsHttpRequestCompletedArg e2) {
                              OSD result2 = e2.getResult();
                              Exception error2 = e2.getError();
                              if (error2 == null && result2 instanceof OSDMap)
                                            {
                                                UploadBakedTextureMessage message2 = new UploadBakedTextureMessage();
                                                message2.Deserialize((OSDMap)result2);
   
                                                if (message2.Request.State.equals("complete"))
                                                {                                                 
                                                    callback.execute(new BakedTextureUploadedCallbackArgs(((UploaderRequestComplete)message2.Request).AssetID));
                                                }
                                            }
                                                return null;
                            }
                                      });
                                    upload.BeginGetResponse(textureData, "application/octet-stream", Client.settings.CAPS_TIMEOUT);
                                    return null;
                                }
                            }
                        }
   
                        JLogger.warn("Bake upload failed during uploader retrieval");
                        callback.execute(new BakedTextureUploadedCallbackArgs(UUID.Zero));
              return null;
            }
                };
                request.setRequestCompleteDelegate(requestCompleteDelegate);
                  request.BeginGetResponse(new OSDMap(), OSDFormat.Xml, Client.settings.CAPS_TIMEOUT);

//            request.OnComplete +=
//                delegate(CapsClient client, OSD result, Exception error)
//                {
//                    if (error == null && result is OSDMap)
//                    {
//                        UploadBakedTextureMessage message = new UploadBakedTextureMessage();
//                        message.Deserialize((OSDMap)result);
//
//                        if (message.Request.State == "upload")
//                        {
//                            Uri uploadUrl = ((UploaderRequestUpload)message.Request).Url;
//
//                            if (uploadUrl != null)
//                            {
//                                // POST the asset data
//                                CapsClient upload = new CapsClient(uploadUrl);
//                                upload.OnComplete +=
//                                    delegate(CapsClient client2, OSD result2, Exception error2)
//                                    {
//                                        if (error2 == null && result2 is OSDMap)
//                                        {
//                                            UploadBakedTextureMessage message2 = new UploadBakedTextureMessage();
//                                            message2.Deserialize((OSDMap)result2);
//
//                                            if (message2.Request.State == "complete")
//                                            {
//                                                callback(((UploaderRequestComplete)message2.Request).AssetID);
//                                                return;
//                                            }
//                                        }
//
//                                        Logger.Log("Bake upload failed during asset upload", Helpers.LogLevel.Warning, Client);
//                                        callback(UUID.Zero);
//                                    };
//                                upload.BeginGetResponse(textureData, "application/octet-stream", Client.Settings.CAPS_TIMEOUT);
//                                return;
//                            }
//                        }
//                    }
//
//                    Logger.Log("Bake upload failed during uploader retrieval", Helpers.LogLevel.Warning, Client);
//                    callback(UUID.Zero);
//                };
//            request.BeginGetResponse(new OSDMap(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
        }
        else
        {
          JLogger.info("UploadBakedTexture not available, falling back to UDP method");
          final MethodDelegate<Void, BakedTextureUploadedCallbackArgs> uploadCallback = callback;
         
          threadPool.execute(new Runnable(){
        public void run()
        {
                  final UUID transactionID = UUID.Random();
                  final AutoResetEvent uploadEvent = new AutoResetEvent(false);
                  EventObserver<AssetUploadEventArgs> udpCallback = new EventObserver<AssetUploadEventArgs>()
                      {
              @Override
              public void handleEvent(Observable sender,
                  AssetUploadEventArgs e) {
                if (e.getUpload().ID.equals(transactionID))
                              {
                                  uploadEvent.set();
                                  uploadCallback.execute(new BakedTextureUploadedCallbackArgs(e.getUpload().Success ? e.getUpload().AssetID : UUID.Zero));
                              }
              }
                      };
                

                  onAssetUploaded.addObserver(udpCallback);

                  boolean success;
                  try
                  {
                    UploadRequestResult uploadResult = RequestUpload(AssetType.Texture, textureData, true, transactionID);
                      success = uploadEvent.waitOne(Client.settings.TRANSFER_TIMEOUT);
                  }
                  catch (Exception e)
                  {
                    JLogger.warn(e);
                      success = false;
                  }

                  onAssetUploaded.deleteObserver(udpCallback);

                  if (!success)
                    uploadCallback.execute(new BakedTextureUploadedCallbackArgs(UUID.Zero));
        }
      });
         
//            Logger.Log("UploadBakedTexture not available, falling back to UDP method", Helpers.LogLevel.Info, Client);
//
//            ThreadPool.QueueUserWorkItem(
//                delegate(object o)
//                {
//                    UUID transactionID = UUID.Random();
//                    BakedTextureUploadedCallback uploadCallback = (BakedTextureUploadedCallback)o;
//                    AutoResetEvent uploadEvent = new AutoResetEvent(false);
//                    EventHandler<AssetUploadEventArgs> udpCallback =
//                        delegate(Object sender, AssetUploadEventArgs e)
//                        {
//                            if (e.Upload.ID == transactionID)
//                            {
//                                uploadEvent.Set();
//                                uploadCallback(e.Upload.Success ? e.Upload.AssetID : UUID.Zero);
//                            }
//                        };
//
//                    AssetUploaded += udpCallback;
//
//                    UUID assetID;
//                    boolean success;
//
//                    try
//                    {
//                        RequestUpload(out assetID, AssetType.Texture, textureData, true, transactionID);
//                        success = uploadEvent.WaitOne(Client.Settings.TRANSFER_TIMEOUT, false);
//                    }
//                    catch (Exception)
//                    {
//                        success = false;
//                    }
//
//                    AssetUploaded -= udpCallback;
//
//                    if (!success)
//                        uploadCallback(UUID.Zero);
//                }, callback
//            );
        }
    }

    //region Texture Downloads

    /// <summary>
    /// Request a texture asset from the simulator using the <see cref="TexturePipeline"/> system to
    /// manage the requests and re-assemble the image from the packets received from the simulator
    /// </summary>
    /// <param name="textureID">The <see cref="UUID"/> of the texture asset to download</param>
    /// <param name="imageType">The <see cref="ImageType"/> of the texture asset.
    /// Use <see cref="ImageType.Normal"/> for most textures, or <see cref="ImageType.Baked"/> for baked layer texture assets</param>
    /// <param name="priority">A float indicating the requested priority for the transfer. Higher priority values tell the simulator
    /// to prioritize the request before lower valued requests. An image already being transferred using the <see cref="TexturePipeline"/> can have
    /// its priority changed by resending the request with the new priority value</param>
    /// <param name="discardLevel">Number of quality layers to discard.
    /// This controls the end marker of the data sent. Sending with value -1 combined with priority of 0 cancels an in-progress
    /// transfer.</param>
    /// <remarks>A bug exists in the Linden Simulator where a -1 will occasionally be sent with a non-zero priority
    /// indicating an off-by-one error.</remarks>
    /// <param name="packetStart">The packet number to begin the request at. A value of 0 begins the request
    /// from the start of the asset texture</param>
    /// <param name="callback">The <see cref="TextureDownloadCallback"/> callback to fire when the image is retrieved. The callback
    /// will contain the result of the request and the texture asset data</param>
    /// <param name="progress">If true, the callback will be fired for each chunk of the downloaded image.
    /// The callback asset parameter will contain all previously received chunks of the texture asset starting
    /// from the beginning of the request</param>
    /// <example>
    /// Request an image and fire a callback when the request is complete
    /// <code>
    /// Client.Assets.RequestImage(UUID.Parse("c307629f-e3a1-4487-5e88-0d96ac9d4965"), ImageType.Normal, TextureDownloader_OnDownloadFinished);
    ///
    /// private void TextureDownloader_OnDownloadFinished(TextureRequestState state, AssetTexture asset)
    /// {
    ///     if(state == TextureRequestState.Finished)
    ///     {
    ///       Console.WriteLine("Texture {0} ({1} bytes) has been successfully downloaded",
    ///         asset.AssetID,
    ///         asset.AssetData.Length);
    ///     }
    /// }
    /// </code>
    /// Request an image and use an inline anonymous method to handle the downloaded texture data
    /// <code>
    /// Client.Assets.RequestImage(UUID.Parse("c307629f-e3a1-4487-5e88-0d96ac9d4965"), ImageType.Normal, delegate(TextureRequestState state, AssetTexture asset)
    ///                                         {
    ///                                             if(state == TextureRequestState.Finished)
    ///                                             {
    ///                                                 Console.WriteLine("Texture {0} ({1} bytes) has been successfully downloaded",
    ///                                                 asset.AssetID,
    ///                                                 asset.AssetData.Length);
    ///                                             }
    ///                                         }
    /// );
    /// </code>
    /// Request a texture, decode the texture to a bitmap image and apply it to a imagebox
    /// <code>
    /// Client.Assets.RequestImage(UUID.Parse("c307629f-e3a1-4487-5e88-0d96ac9d4965"), ImageType.Normal, TextureDownloader_OnDownloadFinished);
    ///
    /// private void TextureDownloader_OnDownloadFinished(TextureRequestState state, AssetTexture asset)
    /// {
    ///     if(state == TextureRequestState.Finished)
    ///     {
    ///         ManagedImage imgData;
    ///         Image bitmap;
    ///
    ///         if (state == TextureRequestState.Finished)
    ///         {
    ///             OpenJPEG.DecodeToImage(assetTexture.AssetData, out imgData, out bitmap);
    ///             picInsignia.Image = bitmap;
    ///         }              
    ///     }
    /// }
    /// </code>
    /// </example>
    public void RequestImage(UUID textureID, ImageType imageType, float priority, int discardLevel,
        long packetStart, MethodDelegate<Void, TextureDownloadCallbackArgs> callback, boolean progress) throws Exception
    {
        if (Client.settings.USE_HTTP_TEXTURES &&
            Client.network.getCurrentSim().Caps != null &&
            Client.network.getCurrentSim().Caps.CapabilityURI("GetTexture") != null)
        {
            HttpRequestTexture(textureID, imageType, priority, discardLevel, packetStart, callback, progress);
        }
        else
        {
            Texture.RequestTexture(textureID, imageType, priority, discardLevel, packetStart, callback, progress);
        }
    }

    /// <summary>
    /// Overload: Request a texture asset from the simulator using the <see cref="TexturePipeline"/> system to
    /// manage the requests and re-assemble the image from the packets received from the simulator
    /// </summary>
    /// <param name="textureID">The <see cref="UUID"/> of the texture asset to download</param>
    /// <param name="callback">The <see cref="TextureDownloadCallback"/> callback to fire when the image is retrieved. The callback
    /// will contain the result of the request and the texture asset data</param>
    public void RequestImage(UUID textureID, MethodDelegate<Void, TextureDownloadCallbackArgs> callback) throws Exception
    {
        RequestImage(textureID, ImageType.Normal, 101300.0f, 0, 0, callback, false);
    }

    /// <summary>
    /// Overload: Request a texture asset from the simulator using the <see cref="TexturePipeline"/> system to
    /// manage the requests and re-assemble the image from the packets received from the simulator
    /// </summary>
    /// <param name="textureID">The <see cref="UUID"/> of the texture asset to download</param>
    /// <param name="imageType">The <see cref="ImageType"/> of the texture asset.
    /// Use <see cref="ImageType.Normal"/> for most textures, or <see cref="ImageType.Baked"/> for baked layer texture assets</param>
    /// <param name="callback">The <see cref="TextureDownloadCallback"/> callback to fire when the image is retrieved. The callback
    /// will contain the result of the request and the texture asset data</param>
    public void RequestImage(UUID textureID, ImageType imageType, MethodDelegate<Void, TextureDownloadCallbackArgs> callback) throws Exception
    {
        RequestImage(textureID, imageType, 101300.0f, 0, 0, callback, false);
    }

    /// <summary>
    /// Overload: Request a texture asset from the simulator using the <see cref="TexturePipeline"/> system to
    /// manage the requests and re-assemble the image from the packets received from the simulator
    /// </summary>
    /// <param name="textureID">The <see cref="UUID"/> of the texture asset to download</param>
    /// <param name="imageType">The <see cref="ImageType"/> of the texture asset.
    /// Use <see cref="ImageType.Normal"/> for most textures, or <see cref="ImageType.Baked"/> for baked layer texture assets</param>
    /// <param name="callback">The <see cref="TextureDownloadCallback"/> callback to fire when the image is retrieved. The callback
    /// will contain the result of the request and the texture asset data</param>
    /// <param name="progress">If true, the callback will be fired for each chunk of the downloaded image.
    /// The callback asset parameter will contain all previously received chunks of the texture asset starting
    /// from the beginning of the request</param>
    public void RequestImage(UUID textureID, ImageType imageType, MethodDelegate<Void, TextureDownloadCallbackArgs> callback, boolean progress) throws Exception
    {
        RequestImage(textureID, imageType, 101300.0f, 0, 0, callback, progress);
    }

    /// <summary>
    /// Cancel a texture request
    /// </summary>
    /// <param name="textureID">The texture assets <see cref="UUID"/></param>
    public void RequestImageCancel(UUID textureID)
    {
        Texture.AbortTextureRequest(textureID);
    }

    /// <summary>
    /// Requests download of a mesh asset
    /// </summary>
    /// <param name="meshID">UUID of the mesh asset</param>
    /// <param name="callback">Callback when the request completes</param>
    public void RequestMesh(final UUID meshID, final MethodDelegate<Void, MeshDownloadCallbackArgs> callback) throws URISyntaxException
    {
        if (meshID.equals(UUID.Zero) || callback == null)
            return;

        if (Client.network.getCurrentSim().Caps != null &&
            Client.network.getCurrentSim().Caps.CapabilityURI("GetMesh") != null)
        {
            // Do we have this mesh asset in the cache?
            if (Client.assets.Cache.hasAsset(meshID))
            {
                callback.execute(new MeshDownloadCallbackArgs(true, new AssetMesh(meshID, Client.assets.Cache.getCachedAssetBytes(meshID))));
                return;
            }

            URI url = Client.network.getCurrentSim().Caps.CapabilityURI("GetMesh");

            MethodDelegate<Void,HttpBaseRequestCompletedArg> downloadCompletedCallback = new MethodDelegate<Void,HttpBaseRequestCompletedArg>()
                {
            public Void execute(HttpBaseRequestCompletedArg e) {
//              HttpRequestBase request = e.getRequest();
//              HttpResponse response = e.getResponse();
              byte[] responseData = e.getResponseData();
              Exception error = e.getError();
                if (error == null && responseData != null) // success
                          {
                              callback.execute(new MeshDownloadCallbackArgs(true, new AssetMesh(meshID, responseData)));
                              Client.assets.Cache.saveAssetToCache(meshID, responseData);
                          }
                          else // download failed
                          {
                              JLogger.warn(
                                  String.format("Failed to fetch mesh asset %s: %s",
                                      meshID,
                                      (error == null) ? "" :Utils.getExceptionStackTraceAsString(error)
                                  ));
                          }
              return null;
            }
                };
               
           
            DownloadRequest req = new DownloadRequest(
                new URI(String.format("%s/?mesh_id=%s", url.toString(), meshID.toString())),
                Client.settings.CAPS_TIMEOUT,
                null,
                null,
                downloadCompletedCallback
            );

            HttpDownloads.QueueDownlad(req);
        }
        else
        {
            JLogger.error("GetMesh capability not available");
            callback.execute(new MeshDownloadCallbackArgs(false, null));
        }
    }

    /// <summary>
    /// Lets TexturePipeline class fire the progress event
    /// </summary>
    /// <param name="texureID">The texture ID currently being downloaded</param>
    /// <param name="transferredBytes">the number of bytes transferred</param>
    /// <param name="totalBytes">the total number of bytes expected</param>
    public void FireImageProgressEvent(UUID texureID, int transferredBytes, int totalBytes)
    {
        try { onImageReceiveProgress.raiseEvent(new ImageReceiveProgressEventArgs(texureID, transferredBytes, totalBytes)); }
        catch (Exception e) { JLogger.error(e); }
    }

    // Helper method for downloading textures via GetTexture cap
    // Same signature as the UDP variant since we need all the params to
    // pass to the UDP TexturePipeline in case we need to fall back to it
    // (Linden servers currently (1.42) don't support bakes downloads via HTTP)
    private void HttpRequestTexture(final UUID textureID, final ImageType imageType, final float priority, final int discardLevel,
        final long packetStart, final MethodDelegate<Void, TextureDownloadCallbackArgs> callback, final boolean progress) throws Exception
    {
        if (textureID.equals(UUID.Zero) || callback == null)
            return;

        byte[] assetData;
        // Do we have this image in the cache?
        if (Client.assets.Cache.hasAsset(textureID)
            && (assetData = Client.assets.Cache.getCachedAssetBytes(textureID)) != null)
        {
            ImageDownload image = new ImageDownload();
            image.ID = textureID;
            image.AssetData = assetData;
            image.Size = image.AssetData.length;
            image.Transferred = image.AssetData.length;
            image.ImageType = imageType;
            image.AssetType = AssetType.Texture;
            image.Success = true;

            callback.execute(new TextureDownloadCallbackArgs(TextureRequestState.Finished, new AssetTexture(image.ID, image.AssetData)));
           
            FireImageProgressEvent(image.ID, (int)image.Transferred, image.Size);
            return;
        }

        MethodDelegate<Void, HttpBaseDownloadProgressArg> progressHandler = null;
        if (progress)
        {
          progressHandler =  new MethodDelegate<Void, HttpBaseDownloadProgressArg>(){
        public Void execute(HttpBaseDownloadProgressArg e) {
//          HttpRequestBase request = e.getRequest();
//          HttpResponse response = e.getResponse();
          int bytesReceived = e.getBytesReceived();
          int totalBytesToReceive  = e.getTotalBytesToReceive();
                    FireImageProgressEvent(textureID, bytesReceived, totalBytesToReceive);
          return null;
        }
          };
        }
       
        MethodDelegate<Void,HttpBaseRequestCompletedArg> downloadCompletedHanlder = new MethodDelegate<Void,HttpBaseRequestCompletedArg>()
            {
          public Void execute(HttpBaseRequestCompletedArg e) {
//            HttpRequestBase request = e.getRequest();
//            HttpResponse response = e.getResponse();
            byte[] responseData = e.getResponseData();
            Exception error = e.getError();
             if (error == null && responseData != null) // success
                      {
                          ImageDownload image = new ImageDownload();
                          image.ID = textureID;
                          image.AssetData = responseData;
                          image.Size = image.AssetData.length;
                          image.Transferred = image.AssetData.length;
                          image.ImageType = imageType;
                          image.AssetType = AssetType.Texture;
                          image.Success = true;

                          callback.execute(new TextureDownloadCallbackArgs(TextureRequestState.Finished, new AssetTexture(image.ID, image.AssetData)));
                          FireImageProgressEvent(image.ID, (int)image.Transferred, image.Size);

                          Client.assets.Cache.saveAssetToCache(textureID, responseData);
                      }
                      else // download failed
                      {
                          JLogger.warn(
                              String.format("Failed to fetch texture %s over HTTP, falling back to UDP: %s",
                                  textureID,
                                  (error == null) ? "" : Utils.getExceptionStackTraceAsString(error)
                              ));

                          Texture.RequestTexture(textureID, imageType, priority, discardLevel, packetStart, callback, progress);
                      }
            return null;
          }
            };
       
        URI url = Client.network.getCurrentSim().Caps.CapabilityURI("GetTexture");

        DownloadRequest req = new DownloadRequest(
            new URI(String.format("%s/?texture_id=%s", url.toString(), textureID.toString())),
            Client.settings.CAPS_TIMEOUT,
            "image/x-j2c",
            progressHandler,downloadCompletedHanlder
        );

        HttpDownloads.QueueDownlad(req);

    }

    //endregion Texture Downloads

    //region Helpers

    private Asset CreateAssetWrapper(AssetType type)
    {
        Asset asset;

        switch (type)
        {
            case Notecard:
                asset = new AssetNotecard();
                break;
            case LSLText:
                asset = new AssetScriptText();
                break;
            case LSLBytecode:
                asset = new AssetScriptBinary();
                break;
            case Texture:
                asset = new AssetTexture();
                break;
            case Object:
                asset = new AssetPrim();
                break;
            case Clothing:
                asset = new AssetClothing();
                break;
            case Bodypart:
                asset = new AssetBodypart();
                break;
            case Animation:
                asset = new AssetAnimation();
                break;
            case Sound:
                asset = new AssetSound();
                break;
            case Landmark:
                asset = new AssetLandmark();
                break;
            case Gesture:
                asset = new AssetGesture();
                break;
            default:
                JLogger.error("Unimplemented asset type: " + type);
                return null;
        }

        return asset;
    }

    private Asset WrapAsset(AssetDownload download)
    {
        Asset asset = CreateAssetWrapper(download.AssetType);
        if (asset != null)
        {
            asset.setAssetID(download.AssetID);
            asset.AssetData = download.AssetData;
            return asset;
        }
        else
        {
            return null;
        }
    }

    private void SendNextUploadPacket(AssetUpload upload)
    {
        SendXferPacketPacket send = new SendXferPacketPacket();

        send.XferID.ID = upload.XferID;
        send.XferID.Packet = upload.PacketNum++;

        if (send.XferID.Packet == 0)
        {
            // The first packet reserves the first four bytes of the data for the
            // total length of the asset and appends 1000 bytes of data after that
            send.DataPacket.Data = new byte[1004];
            Utils.arraycopy(Utils.intToBytes(upload.Size), 0, send.DataPacket.Data, 0, 4);
            Utils.arraycopy(upload.AssetData, 0, send.DataPacket.Data, 4, 1000);
            upload.Transferred += 1000;

            synchronized(Transfers)
            {
                Transfers.remove(upload.AssetID);
                Transfers.put(upload.ID, upload);
            }
        }
        else if ((send.XferID.Packet + 1) * 1000 < upload.Size)
        {
            // This packet is somewhere in the middle of the transfer, or a perfectly
            // aligned packet at the end of the transfer
            send.DataPacket.Data = new byte[1000];
            Utils.arraycopy(upload.AssetData, (int)upload.Transferred, send.DataPacket.Data, 0, 1000);
            upload.Transferred += 1000;
        }
        else
        {
            // Special handler for the last packet which will be less than 1000 bytes
            int lastlen = upload.Size - ((int)send.XferID.Packet * 1000);
            send.DataPacket.Data = new byte[lastlen];
            Utils.arraycopy(upload.AssetData, (int)send.XferID.Packet * 1000, send.DataPacket.Data, 0, lastlen);
            send.XferID.Packet |= 0x80000000L; // This signals the final packet
            upload.Transferred += lastlen;
        }

        Client.network.SendPacket(send);
    }

    private void SendConfirmXferPacket(BigInteger xferID, long packetNum)
    {
        ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
        confirm.XferID.ID = xferID;
        confirm.XferID.Packet = packetNum;

        Client.network.SendPacket(confirm);
    }

    //endregion Helpers

    //region Transfer Callbacks

    /// <summary>Process an incoming packet and raise the appropriate events</summary>
    /// <param name="sender">The sender</param>
    /// <param name="e">The EventArgs object containing the packet data</param>
    protected void TransferInfoHandler(Object sender, PacketReceivedEventArgs e)
    {
        TransferInfoPacket info = (TransferInfoPacket)e.getPacket();
        Transfer transfer;
        AssetDownload download;

        if ((transfer = Transfers.get(info.TransferInfo.TransferID))!=null)
        {
            download = (AssetDownload)transfer;

            if (download.Callback == null) return;

            download.Channel = ChannelType.get(info.TransferInfo.ChannelType);
            download.Status = StatusCode.get(info.TransferInfo.Status);
            download.Target = TargetType.get(info.TransferInfo.TargetType);
            download.Size = info.TransferInfo.Size;

            // TODO: Once we support mid-transfer status checking and aborting this
            // will need to become smarter
            if (download.Status != StatusCode.OK)
            {
                JLogger.warn("Transfer failed with status code " + download.Status);

                synchronized(Transfers)
                {Transfers.remove(download.ID);}

                // No data could have been received before the TransferInfo packet
                download.AssetData = null;

                // Fire the event with our transfer that contains Success = false;
                try { download.Callback.execute(new AssetReceivedCallbackArgs(download, null)); }
                catch (Exception ex) { JLogger.error(ex); }
            }
            else
            {
                download.AssetData = new byte[download.Size];

                if (download.Source == SourceType.Asset && info.TransferInfo.Params.length == 20)
                {
                    download.AssetID = new UUID(info.TransferInfo.Params, 0);
                    download.AssetType = AssetType.get((byte)info.TransferInfo.Params[16]);

                    //Client.DebugLog(String.Format("TransferInfo packet received. AssetID: {0} Type: {1}",
                    //    transfer.AssetID, type));
                }
                else if (download.Source == SourceType.SimInventoryItem && info.TransferInfo.Params.length == 100)
                {
                    // TODO: Can we use these?
                    //UUID agentID = new UUID(info.TransferInfo.Params, 0);
                    //UUID sessionID = new UUID(info.TransferInfo.Params, 16);
                    //UUID ownerID = new UUID(info.TransferInfo.Params, 32);
                    //UUID taskID = new UUID(info.TransferInfo.Params, 48);
                    //UUID itemID = new UUID(info.TransferInfo.Params, 64);
                    download.AssetID = new UUID(info.TransferInfo.Params, 80);
                    download.AssetType = AssetType.get((byte)info.TransferInfo.Params[96]);

                    //Client.DebugLog(String.Format("TransferInfo packet received. AgentID: {0} SessionID: {1} " +
                    //    "OwnerID: {2} TaskID: {3} ItemID: {4} AssetID: {5} Type: {6}", agentID, sessionID,
                    //    ownerID, taskID, itemID, transfer.AssetID, type));
                }
                else
                {
                    JLogger.warn("Received a TransferInfo packet with a SourceType of " + download.Source.toString() +
                        " and a Params field length of " + info.TransferInfo.Params.length);
                }
            }
            download.HeaderReceivedEvent.set();
        }
        else
        {
            JLogger.warn("Received a TransferInfo packet for an asset we didn't request, TransferID: " +
                info.TransferInfo.TransferID);
        }
    }

    /// <summary>Process an incoming packet and raise the appropriate events</summary>
    /// <param name="sender">The sender</param>
    /// <param name="e">The EventArgs object containing the packet data</param>
    protected void TransferPacketHandler(Object sender, PacketReceivedEventArgs e) throws InterruptedException
    {
        TransferPacketPacket asset = (TransferPacketPacket)e.getPacket();
        Transfer transfer;
        AssetDownload download;

        if ((transfer = Transfers.get(asset.TransferData.TransferID))!=null)
        {
            download = (AssetDownload)transfer;

            if (download.Size == 0)
            {
                JLogger.debug("TransferPacket received ahead of the transfer header, blocking...");

                // We haven't received the header yet, block until it's received or times out
                download.HeaderReceivedEvent.waitOne(TRANSFER_HEADER_TIMEOUT);

                if (download.Size == 0)
                {
                    JLogger.warn("Timed out while waiting for the asset header to download for " +
                        download.ID.toString());

                    // Abort the transfer
                    TransferAbortPacket abort = new TransferAbortPacket();
                    abort.TransferInfo.ChannelType = (int)download.Channel.getIndex();
                    abort.TransferInfo.TransferID = download.ID;
                    Client.network.SendPacket(abort, download.Simulator);

                    download.Success = false;
                    synchronized(Transfers)
                    {Transfers.remove(download.ID);}

                    // Fire the event with our transfer that contains Success = false
                    if (download.Callback != null)
                    {
                        try { download.Callback.execute(new AssetReceivedCallbackArgs(download, null)); }
                        catch (Exception ex) { JLogger.error(ex); }
                    }

                    return;
                }
            }

            // This assumes that every transfer packet except the last one is exactly 1000 bytes,
            // hopefully that is a safe assumption to make
            try
            {
              byte[] bytes = new byte[asset.TransferData.Data.length];
                Utils.arraycopy(asset.TransferData.Data, 0, bytes, 0, asset.TransferData.Data.length);
                download.pmap.put(asset.TransferData.Packet, bytes);
                download.Transferred += asset.TransferData.Data.length;
                JLogger.debug(String.format("AssetManager: TransferPacketHandler: got asset %s packet length %d of packet no %d", download.AssetID.toString(), asset.TransferData.Data.length, asset.TransferData.Packet));
            }
            catch (IllegalArgumentException ex)
            {
                JLogger.error(String.format("TransferPacket handling failed. TransferData.Data.Length=%d, AssetData.Length=%d, TransferData.Packet=%d",
                    asset.TransferData.Data.length, download.AssetData.length, asset.TransferData.Packet));
                return;
            }

            //Client.DebugLog(String.Format("Transfer packet {0}, received {1}/{2}/{3} bytes for asset {4}",
            //    asset.TransferData.Packet, asset.TransferData.Data.Length, transfer.Transferred, transfer.Size,
            //    transfer.AssetID.toString()));

            // Check if we downloaded the full asset
            if (download.Transferred >= download.Size)
            {
              int i =0;
             
              for(Entry<Integer, byte[]> entry:download.pmap.entrySet())
              {
                Utils.arraycopy(entry.getValue(), 0, download.AssetData, i, entry.getValue().length);
                i += entry.getValue().length;
              }
              download.pmap.clear();
                JLogger.debug("Transfer for asset " + download.AssetID.toString() + " completed");

                download.Success = true;
                synchronized(Transfers)
                {Transfers.remove(download.ID);}

                // Cache successful asset download
                Cache.saveAssetToCache(download.AssetID, download.AssetData);

                if (download.Callback != null)
                {
                    try { download.Callback.execute(new AssetReceivedCallbackArgs(download, WrapAsset(download))); }
                    catch (Exception ex) { JLogger.error(ex); }
                }
            }
        }
    }

    //endregion Transfer Callbacks

    //region Xfer Callbacks

    /// <summary>Process an incoming packet and raise the appropriate events</summary>
    /// <param name="sender">The sender</param>
    /// <param name="e">The EventArgs object containing the packet data</param>
    protected void InitiateDownloadPacketHandler(Object sender, PacketReceivedEventArgs e)
    {
        InitiateDownloadPacket request = (InitiateDownloadPacket)e.getPacket();
        try
        {
            onInitiateDownload.raiseEvent(new InitiateDownloadEventArgs(Utils.bytesToString(request.FileData.SimFilename),
                Utils.bytesToString(request.FileData.ViewerFilename)));
        }
        catch (Exception ex) { JLogger.error(ex); }
    }

    /// <summary>Process an incoming packet and raise the appropriate events</summary>
    /// <param name="sender">The sender</param>
    /// <param name="e">The EventArgs object containing the packet data</param>
    protected void RequestXferHandler(Object sender, PacketReceivedEventArgs e)
    {
        if (PendingUpload == null)
            JLogger.warn("Received a RequestXferPacket for an unknown asset upload");
        else
        {
            AssetUpload upload = PendingUpload;
            PendingUpload = null;
            WaitingForUploadConfirm = false;
            RequestXferPacket request = (RequestXferPacket)e.getPacket();

            upload.XferID = request.XferID.ID;
            upload.Type = AssetType.get((byte)request.XferID.VFileType);

            UUID transferID = new UUID(upload.XferID.longValue());
            Transfers.put(transferID,  upload);

            // Send the first packet containing actual asset data
            SendNextUploadPacket(upload);
        }
    }

    /// <summary>Process an incoming packet and raise the appropriate events</summary>
    /// <param name="sender">The sender</param>
    /// <param name="e">The EventArgs object containing the packet data</param>
    protected void ConfirmXferPacketHandler(Object sender, PacketReceivedEventArgs e)
    {
        ConfirmXferPacketPacket confirm = (ConfirmXferPacketPacket)e.getPacket();

        // Building a new UUID every time an ACK is received for an upload is a horrible
        // thing, but this whole Xfer system is horrible
        UUID transferID = new UUID(confirm.XferID.ID.longValue());
        Transfer transfer;
        AssetUpload upload = null;

        if ((transfer = Transfers.get(transferID))!=null)
        {
            upload = (AssetUpload)transfer;

            //Client.DebugLog(String.Format("ACK for upload {0} of asset type {1} ({2}/{3})",
            //    upload.AssetID.toString(), upload.Type, upload.Transferred, upload.Size));

            try { onUploadProgress.raiseEvent(new AssetUploadEventArgs(upload)); }
            catch (Exception ex) { JLogger.error(ex); }

            if (upload.Transferred < upload.Size)
                SendNextUploadPacket(upload);
        }
    }

    /// <summary>Process an incoming packet and raise the appropriate events</summary>
    /// <param name="sender">The sender</param>
    /// <param name="e">The EventArgs object containing the packet data</param>
    protected void AssetUploadCompleteHandler(Object sender, PacketReceivedEventArgs e)
    {
        AssetUploadCompletePacket complete = (AssetUploadCompletePacket)e.getPacket();

        // If we uploaded an asset in a single packet, RequestXferHandler()
        // will never be called so we need to set this here as well
        WaitingForUploadConfirm = false;

        if (onAssetUploaded != null)
        {
            boolean found = false;
            Entry<UUID, Transfer> foundTransfer = null;

            // Xfer system sucks really really bad. Where is the damn XferID?
            synchronized(Transfers)
            {
                for (Entry<UUID, Transfer> transfer : Transfers.entrySet())
                {
                    if (transfer.getValue() instanceof AssetUpload)
                    {
                        AssetUpload upload = (AssetUpload)transfer.getValue();

                        if ((upload).AssetID.equals(complete.AssetBlock.UUID))
                        {
                            found = true;
                            foundTransfer = transfer;
                            upload.Success = complete.AssetBlock.Success;
                            upload.Type = AssetType.get(complete.AssetBlock.Type);
                            break;
                        }
                    }
                }
            }

            if (found)
            {
                synchronized(Transfers)
                {Transfers.remove(foundTransfer.getKey());}

                try { onAssetUploaded.raiseEvent(new AssetUploadEventArgs((AssetUpload)foundTransfer.getValue())); }
                catch (Exception ex) { JLogger.error(ex); }
            }
            else
            {
                JLogger.warn(String.format(
                    "Got an AssetUploadComplete on an unrecognized asset, AssetID: %s, Type: %s, Success: %s",
                    complete.AssetBlock.UUID, AssetType.get(complete.AssetBlock.Type), complete.AssetBlock.Success));
            }
        }
    }

    /// <summary>Process an incoming packet and raise the appropriate events</summary>
    /// <param name="sender">The sender</param>
    /// <param name="e">The EventArgs object containing the packet data</param>
    protected void SendXferPacketHandler(Object sender, PacketReceivedEventArgs e)
    {
        SendXferPacketPacket xfer = (SendXferPacketPacket)e.getPacket();

        // Lame ulong to UUID conversion, please go away Xfer system
        UUID transferID = new UUID(xfer.XferID.ID.longValue());
        Transfer transfer;
        XferDownload download = null;

        if ((transfer = Transfers.get(transferID))!=null)
        {
            download = (XferDownload)transfer;

            // Apply a mask to get rid of the "end of transfer" bit
            long packetNum = xfer.XferID.Packet & 0x0FFFFFFF;

            // Check for out of order packets, possibly indicating a resend
            if (packetNum != download.PacketNum)
            {
                if (packetNum == download.PacketNum - 1)
                {
                    JLogger.debug("Resending Xfer download confirmation for packet " + packetNum);
                    SendConfirmXferPacket(download.XferID, packetNum);
                }
                else
                {
                    JLogger.warn("Out of order Xfer packet in a download, got " + packetNum + " expecting " + download.PacketNum);
                    // Re-confirm the last packet we actually received
                    SendConfirmXferPacket(download.XferID, download.PacketNum - 1);
                }

                return;
            }

            if (packetNum == 0)
            {
                // This is the first packet received in the download, the first four bytes are a size integer
                // in little endian ordering
                byte[] bytes = xfer.DataPacket.Data;
                download.Size = (bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24));
                download.AssetData = new byte[download.Size];

                JLogger.debug("Received first packet in an Xfer download of size " + download.Size);

                Utils.arraycopy(xfer.DataPacket.Data, 4, download.AssetData, 0, xfer.DataPacket.Data.length - 4);
                download.Transferred += xfer.DataPacket.Data.length - 4;
            }
            else
            {
                Utils.arraycopy(xfer.DataPacket.Data, 0, download.AssetData, 1000 * (int)packetNum, xfer.DataPacket.Data.length);
                download.Transferred += xfer.DataPacket.Data.length;
            }

            // Increment the packet number to the packet we are expecting next
            download.PacketNum++;

            // Confirm receiving this packet
            SendConfirmXferPacket(download.XferID, packetNum);

            if ((xfer.XferID.Packet & 0x80000000) != 0)
            {
                // This is the last packet in the transfer
                if (!Utils.isNullOrEmpty(download.Filename))
                    JLogger.debug("Xfer download for asset " + download.Filename + " completed");
                else
                    JLogger.debug("Xfer download for asset " + download.VFileID.toString() + " completed");

                download.Success = true;
                synchronized(Transfers)
                {Transfers.remove(download.ID);}

                try { onXferReceived.raiseEvent(new XferReceivedEventArgs(download)); }
                catch (Exception ex) { JLogger.error(ex); }
            }
        }
    }

    /// <summary>Process an incoming packet and raise the appropriate events</summary>
    /// <param name="sender">The sender</param>
    /// <param name="e">The EventArgs object containing the packet data</param>
    protected void AbortXferHandler(Object sender, PacketReceivedEventArgs e)
    {
        AbortXferPacket abort = (AbortXferPacket)e.getPacket();
        XferDownload download = null;

        // Lame ulong to UUID conversion, please go away Xfer system
        UUID transferID = new UUID(abort.XferID.ID.longValue());

        synchronized(Transfers)
        {
            Transfer transfer;
            if ((transfer = Transfers.get(transferID))!=null)
            {
                download = (XferDownload)transfer;
                Transfers.remove(transferID);
            }
        }

        if (download != null && onXferReceived != null)
        {
            download.Success = false;
            download.Error = TransferError.get(abort.XferID.Result);

            try { onXferReceived.raiseEvent((new XferReceivedEventArgs(download))); }
            catch (Exception ex) { JLogger.error(ex); }
        }
    }

    //endregion Xfer Callbacks 
}
TOP

Related Classes of com.ngt.jopenmetaverse.shared.sim.AssetManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.