/*
* Copyright (c) Contributors, http://www.openmetaverse.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the OpenSim Project nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* 
*/
using System;
using System.Collections.Generic;
using libsecondlife;
using libsecondlife.Packets;
using OpenSim.Assets;
using OpenSim.Framework.Interfaces;
using OpenSim.Framework.Types;
using OpenSim.Framework.Utilities;
using OpenSim.Framework.Communications.Caches;
 

namespace OpenSim.Region.ClientStack
{
    partial class ClientView
    {
        public class AgentAssetUpload
        {
            private Dictionary<LLUUID, AssetTransaction> transactions = new Dictionary<LLUUID, AssetTransaction>();
            private ClientView ourClient;
            private AssetCache m_assetCache;
            private InventoryCache m_inventoryCache;

            public AgentAssetUpload(ClientView client, AssetCache assetCache, InventoryCache inventoryCache)
            {
                this.ourClient = client;
                m_assetCache = assetCache;
                m_inventoryCache = inventoryCache;
            }

            public void AddUpload(LLUUID transactionID, AssetBase asset)
            {
                AssetTransaction upload = new AssetTransaction();
                lock (this.transactions)
                {
                    upload.Asset = asset;
                    upload.TransactionID = transactionID;
                    this.transactions.Add(transactionID, upload);
                }
                if (upload.Asset.Data.Length > 2)
                {
                    //is complete
                    upload.UploadComplete = true;
                    AssetUploadCompletePacket response = new AssetUploadCompletePacket();
                    response.AssetBlock.Type = asset.Type;
                    response.AssetBlock.Success = true;
                    response.AssetBlock.UUID = transactionID.Combine(this.ourClient.SecureSessionID);
                    this.ourClient.OutPacket(response);
                    m_assetCache.AddAsset(asset);
                }
                else
                {
                    upload.UploadComplete = false;
                    upload.XferID = Util.GetNextXferID();
                    RequestXferPacket xfer = new RequestXferPacket();
                    xfer.XferID.ID = upload.XferID;
                    xfer.XferID.VFileType = upload.Asset.Type;
                    xfer.XferID.VFileID = transactionID.Combine(this.ourClient.SecureSessionID);
                    xfer.XferID.FilePath = 0;
                    xfer.XferID.Filename = new byte[0];
                    this.ourClient.OutPacket(xfer);
                }

            }

            public AssetBase GetUpload(LLUUID transactionID)
            {
                if (this.transactions.ContainsKey(transactionID))
                {
                    return this.transactions[transactionID].Asset;
                }

                return null;
            }

            public void HandleUploadPacket(AssetUploadRequestPacket pack, LLUUID assetID)
            {
                // Console.Write("asset upload request , type = " + pack.AssetBlock.Type.ToString());
                AssetBase asset = null;
                if (pack.AssetBlock.Type == 0)
                {

                    //first packet for transaction
                    asset = new AssetBase();
                    asset.FullID = assetID;
                    asset.Type = pack.AssetBlock.Type;
                    asset.InvType = asset.Type;
                    asset.Name = "UploadedTexture" + Util.RandomClass.Next(1, 1000).ToString("000");
                    asset.Data = pack.AssetBlock.AssetData;


                }
                else if (pack.AssetBlock.Type == 13 | pack.AssetBlock.Type == 5 | pack.AssetBlock.Type == 7)
                {

                    asset = new AssetBase();
                    asset.FullID = assetID;
                    //  Console.WriteLine("skin asset id is " + assetID.ToStringHyphenated());
                    asset.Type = pack.AssetBlock.Type;
                    asset.InvType = asset.Type;
                    asset.Name = "NewClothing" + Util.RandomClass.Next(1, 1000).ToString("000");
                    asset.Data = pack.AssetBlock.AssetData;


                }

                if (asset != null)
                {
                    this.AddUpload(pack.AssetBlock.TransactionID, asset);
                }
                else
                {

                    //currently we don't support this asset type 
                    //so lets just tell the client that the upload is complete
                    AssetUploadCompletePacket response = new AssetUploadCompletePacket();
                    response.AssetBlock.Type = pack.AssetBlock.Type;
                    response.AssetBlock.Success = true;
                    response.AssetBlock.UUID = pack.AssetBlock.TransactionID.Combine(this.ourClient.SecureSessionID);
                    this.ourClient.OutPacket(response);
                }

            }

            #region Xfer packet system for larger uploads

            public void HandleXferPacket(SendXferPacketPacket xferPacket)
            {
                lock (this.transactions)
                {
                    foreach (AssetTransaction trans in this.transactions.Values)
                    {
                        if (trans.XferID == xferPacket.XferID.ID)
                        {
                            if (trans.Asset.Data.Length > 1)
                            {
                                byte[] newArray = new byte[trans.Asset.Data.Length + xferPacket.DataPacket.Data.Length];
                                Array.Copy(trans.Asset.Data, 0, newArray, 0, trans.Asset.Data.Length);
                                Array.Copy(xferPacket.DataPacket.Data, 0, newArray, trans.Asset.Data.Length, xferPacket.DataPacket.Data.Length);
                                trans.Asset.Data = newArray;
                            }
                            else
                            {
                                byte[] newArray = new byte[xferPacket.DataPacket.Data.Length - 4];
                                Array.Copy(xferPacket.DataPacket.Data, 4, newArray, 0, xferPacket.DataPacket.Data.Length - 4);
                                trans.Asset.Data = newArray;
                            }

                            if ((xferPacket.XferID.Packet & 2147483648) != 0)
                            {
                                //end of transfer
                                trans.UploadComplete = true;
                                AssetUploadCompletePacket response = new AssetUploadCompletePacket();
                                response.AssetBlock.Type = trans.Asset.Type;
                                response.AssetBlock.Success = true;
                                response.AssetBlock.UUID = trans.TransactionID.Combine(this.ourClient.SecureSessionID);
                                this.ourClient.OutPacket(response);

                                m_assetCache.AddAsset(trans.Asset);
                                //check if we should add it to inventory 
                                if (trans.AddToInventory)
                                {
                                    // m_assetCache.AddAsset(trans.Asset);
                                    m_inventoryCache.AddNewInventoryItem(this.ourClient, trans.InventFolder, trans.Asset);
                                }


                            }
                            break;
                        }

                    }
                }

                ConfirmXferPacketPacket confirmXfer = new ConfirmXferPacketPacket();
                confirmXfer.XferID.ID = xferPacket.XferID.ID;
                confirmXfer.XferID.Packet = xferPacket.XferID.Packet;
                this.ourClient.OutPacket(confirmXfer);
            }

            #endregion

            public AssetBase AddUploadToAssetCache(LLUUID transactionID)
            {
                AssetBase asset = null;
                if (this.transactions.ContainsKey(transactionID))
                {
                    AssetTransaction trans = this.transactions[transactionID];
                    if (trans.UploadComplete)
                    {
                        m_assetCache.AddAsset(trans.Asset);
                        asset = trans.Asset;
                    }
                }

                return asset;
            }

            public void CreateInventoryItem(CreateInventoryItemPacket packet)
            {
                if (this.transactions.ContainsKey(packet.InventoryBlock.TransactionID))
                {
                    AssetTransaction trans = this.transactions[packet.InventoryBlock.TransactionID];
                    trans.Asset.Description = Util.FieldToString(packet.InventoryBlock.Description);
                    trans.Asset.Name = Util.FieldToString(packet.InventoryBlock.Name);
                    trans.Asset.Type = packet.InventoryBlock.Type;
                    trans.Asset.InvType = packet.InventoryBlock.InvType;
                    if (trans.UploadComplete)
                    {
                        //already complete so we can add it to the inventory
                        //m_assetCache.AddAsset(trans.Asset);
                        m_inventoryCache.AddNewInventoryItem(this.ourClient, packet.InventoryBlock.FolderID, trans.Asset);
                    }
                    else
                    {
                        trans.AddToInventory = true;
                        trans.InventFolder = packet.InventoryBlock.FolderID;
                    }
                }
            }

            private class AssetTransaction
            {
                public uint XferID;
                public AssetBase Asset;
                public bool AddToInventory;
                public LLUUID InventFolder = LLUUID.Zero;
                public bool UploadComplete = false;
                public LLUUID TransactionID = LLUUID.Zero;

                public AssetTransaction()
                {

                }
            }

            //new class , not currently used.
            public class AssetXferUploader
            {
                private IClientAPI ourClient;

                public bool UploadComplete = false;

                public bool AddToInventory;
                public LLUUID InventFolder = LLUUID.Zero;
               
                public uint XferID;
                public AssetBase Asset;
                public LLUUID TransactionID = LLUUID.Zero;


                public AssetXferUploader(IClientAPI remoteClient, LLUUID assetID, LLUUID transaction, sbyte type, byte[] data)
                {
                    ourClient = remoteClient;
                    Asset = new AssetBase();
                    Asset.FullID = assetID;
                    Asset.InvType = type;
                    Asset.Type = type;
                    Asset.Data = data;
                    Asset.Name = "blank";
                    Asset.Description = "empty";
                    TransactionID = transaction;

                    if (Asset.Data.Length > 2)
                    {
                        //data block should only have data in it, if there is no more data to be uploaded
                        this.SendCompleteMessage();
                    }
                    else
                    {
                        this.ReqestStartXfer();
                    }
                }

                protected void SendCompleteMessage()
                {
                    UploadComplete = true;
                    AssetUploadCompletePacket response = new AssetUploadCompletePacket();
                    response.AssetBlock.Type = Asset.Type;
                    response.AssetBlock.Success = true;
                    response.AssetBlock.UUID = Asset.FullID;
                    this.ourClient.OutPacket(response);

                    //TODO trigger event
                }

                protected void ReqestStartXfer()
                {
                    UploadComplete = false;
                    XferID = Util.GetNextXferID();
                    RequestXferPacket xfer = new RequestXferPacket();
                    xfer.XferID.ID = XferID;
                    xfer.XferID.VFileType = Asset.Type;
                    xfer.XferID.VFileID = Asset.FullID;
                    xfer.XferID.FilePath = 0;
                    xfer.XferID.Filename = new byte[0];
                    this.ourClient.OutPacket(xfer);
                }

                public void HandleXferPacket(uint xferID, uint packetID, byte[] data)
                {
                    if (XferID == xferID)
                    {
                        if (Asset.Data.Length > 1)
                        {
                            byte[] newArray = new byte[Asset.Data.Length + data.Length];
                            Array.Copy(Asset.Data, 0, newArray, 0, Asset.Data.Length);
                            Array.Copy(data, 0, newArray, Asset.Data.Length, data.Length);
                            Asset.Data = newArray;
                        }
                        else
                        {
                            byte[] newArray = new byte[data.Length - 4];
                            Array.Copy(data, 4, newArray, 0, data.Length - 4);
                            Asset.Data = newArray;
                        }

                        ConfirmXferPacketPacket confirmXfer = new ConfirmXferPacketPacket();
                        confirmXfer.XferID.ID = xferID;
                        confirmXfer.XferID.Packet = packetID;
                        this.ourClient.OutPacket(confirmXfer);

                        if ((packetID & 2147483648) != 0)
                        {
                            this.SendCompleteMessage();
                        }
                    }
                }
            }
        }
    }
}