using System;
using System.Collections.Generic;

#if NETFX_CORE
    using FileStream = BestHTTP.PlatformSupport.IO.FileStream;
    using FileMode = BestHTTP.PlatformSupport.IO.FileMode;
    using FileAccess = BestHTTP.PlatformSupport.IO.FileAccess;

    using Directory = BestHTTP.PlatformSupport.IO.Directory;
    using File = BestHTTP.PlatformSupport.IO.File;
#else
using FileStream = System.IO.FileStream;
    using FileMode = System.IO.FileMode;
    using FileAccess = System.IO.FileAccess;
#endif

using BestHTTP.Extensions;

namespace BestHTTP
{
    public sealed class StreamList : System.IO.Stream
    {
        private System.IO.Stream[] Streams;
        private int CurrentIdx;

        public StreamList(params System.IO.Stream[] streams)
        {
            this.Streams = streams;
            this.CurrentIdx = 0;
        }

        public override bool CanRead
        {
            get {
                if (CurrentIdx >= Streams.Length)
                    return false;
                return Streams[CurrentIdx].CanRead;
            }
        }

        public override bool CanSeek { get { return false; } }

        public override bool CanWrite
        {
            get {
                if (CurrentIdx >= Streams.Length)
                    return false;
                return Streams[CurrentIdx].CanWrite;
            }
        }

        public override void Flush()
        {
            if (CurrentIdx >= Streams.Length)
                return;

            // We have to call the flush to all previous streams, as we may advanced the CurrentIdx
            for (int i = 0; i <= CurrentIdx; ++i)
                Streams[i].Flush();
        }

        public override long Length
        {
            get {
                if (CurrentIdx >= Streams.Length)
                    return 0;

                long length = 0;
                for (int i = 0; i < Streams.Length; ++i)
                    length += Streams[i].Length;

                return length;
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (CurrentIdx >= Streams.Length)
                return -1;

            int readCount = Streams[CurrentIdx].Read(buffer, offset, count);

            while (readCount < count && CurrentIdx++ < Streams.Length)
            {
                readCount += Streams[CurrentIdx].Read(buffer, offset + readCount, count - readCount);
            }

            return readCount;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (CurrentIdx >= Streams.Length)
                return;

            Streams[CurrentIdx].Write(buffer, offset, count);
        }

        public void Write(string str)
        {
            byte[] bytes = str.GetASCIIBytes();

            this.Write(bytes, 0, bytes.Length);
        }

        protected override void Dispose(bool disposing)
        {
            for (int i = 0; i < Streams.Length; ++i)
            {
                try
                {
                    Streams[i].Dispose();
                }
                catch(Exception ex)
                {
                    HTTPManager.Logger.Exception("StreamList", "Dispose", ex);
                }
            }
        }

        public override long Position
        {
            get
            {
                throw new NotImplementedException("Position get");
            }
            set
            {
                throw new NotImplementedException("Position set");
            }
        }

        public override long Seek(long offset, System.IO.SeekOrigin origin)
        {
            if (CurrentIdx >= Streams.Length)
                return 0;

            return Streams[CurrentIdx].Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException("SetLength");
        }
    }

    /*public static class AndroidFileHelper
    {
        // AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        // AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");

        public static Stream GetAPKFileStream(string path)
        {
            UnityEngine.AndroidJavaClass up = new UnityEngine.AndroidJavaClass("com.unity3d.player.UnityPlayer");
            UnityEngine.AndroidJavaObject cActivity = up.GetStatic<UnityEngine.AndroidJavaObject>("currentActivity");

            UnityEngine.AndroidJavaObject assetManager = cActivity.GetStatic<UnityEngine.AndroidJavaObject>("getAssets");

            return new AndroidInputStream(assetManager.Call<UnityEngine.AndroidJavaObject>("open", path));
        }
    }

    public sealed class AndroidInputStream : Stream
    {
        private UnityEngine.AndroidJavaObject baseStream;

        public override bool CanRead
        {
            get { throw new NotImplementedException(); }
        }

        public override bool CanSeek
        {
            get { throw new NotImplementedException(); }
        }

        public override bool CanWrite
        {
            get { throw new NotImplementedException(); }
        }

        public override void Flush()
        {
            throw new NotImplementedException();
        }

        public override long Length
        {
            get { throw new NotImplementedException(); }
        }

        public override long Position
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public AndroidInputStream(UnityEngine.AndroidJavaObject inputStream)
        {
            this.baseStream = inputStream;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return this.baseStream.Call<int>("read", buffer, offset, count);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }
    }*/

    internal sealed class FileConnection : ConnectionBase
    {
        public FileConnection(string serverAddress)
            :base(serverAddress)
        { }

        internal override void Abort(HTTPConnectionStates newState)
        {
            State = newState;

            switch (State)
            {
                case HTTPConnectionStates.TimedOut: TimedOutStart = DateTime.UtcNow; break;
            }

            throw new NotImplementedException();
        }

        protected override void ThreadFunc(object param)
        {
            try
            {
                // Step 1 : create a stream with header information
                // Step 2 : create a stream from the file
                // Step 3 : create a StreamList
                // Step 4 : create a HTTPResponse object
                // Step 5 : call the Receive function of the response object

                using (FileStream fs = new FileStream(this.CurrentRequest.CurrentUri.LocalPath, FileMode.Open, FileAccess.Read))
                //using (Stream fs = AndroidFileHelper.GetAPKFileStream(this.CurrentRequest.CurrentUri.LocalPath))
                    using (StreamList stream = new StreamList(new System.IO.MemoryStream(), fs))
                    {
                        // This will write to the MemoryStream
                        stream.Write("HTTP/1.1 200 Ok\r\n");
                        stream.Write("Content-Type: application/octet-stream\r\n");
                        stream.Write("Content-Length: " + fs.Length.ToString() + "\r\n");
                        stream.Write("\r\n");

                        stream.Seek(0, System.IO.SeekOrigin.Begin);

                        base.CurrentRequest.Response = new HTTPResponse(base.CurrentRequest, stream, base.CurrentRequest.UseStreaming, false);

                        if (!CurrentRequest.Response.Receive())
                            CurrentRequest.Response = null;
                    }
            }
            catch(Exception ex)
            {
                if (CurrentRequest != null)
                {
                    // Something gone bad, Response must be null!
                    CurrentRequest.Response = null;

                    switch (State)
                    {
                        case HTTPConnectionStates.AbortRequested:
                            CurrentRequest.State = HTTPRequestStates.Aborted;
                            break;
                        case HTTPConnectionStates.TimedOut:
                            CurrentRequest.State = HTTPRequestStates.TimedOut;
                            break;
                        default:
                            CurrentRequest.Exception = ex;
                            CurrentRequest.State = HTTPRequestStates.Error;
                            break;
                    }
                }
            }
            finally
            {
                State = HTTPConnectionStates.Closed;
                if (CurrentRequest.State == HTTPRequestStates.Processing)
                {
                    if (CurrentRequest.Response != null)
                        CurrentRequest.State = HTTPRequestStates.Finished;
                    else
                        CurrentRequest.State = HTTPRequestStates.Error;
                }
            }
        }
    }
}