﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Turbocharged.NSQ
{
    /// <summary>
    /// A message delivered from NSQ.
    /// </summary>
    public class Message
    {
        /// <summary>
        /// The ID of the message, which is generated by nsqd.
        /// </summary>
        public string Id { get; private set; }

        /// <summary>
        /// The number of times this message has been delivered to consumers.
        /// </summary>
        public short Attempts { get; private set; }

        /// <summary>
        /// The nanosecond time the mssage was created in NSQ.
        /// </summary>
        public long Timestamp { get; private set; }

        /// <summary>
        /// The body of the message.
        /// </summary>
        public MessageBody Body { get; private set; }

        readonly NsqTcpConnection _connection;

        const int TIMESTAMP_START = 0;
        const int TIMESTAMP_COUNT = 8;
        const int ATTEMPTS_START = 8;
        const int ATTEMPTS_COUNT = 2;
        const int ID_START = 10;
        const int ID_COUNT = 16;
        const int DATA_START = TIMESTAMP_COUNT + ATTEMPTS_COUNT + ID_COUNT;

        internal Message(Frame frame, NsqTcpConnection connection)
        {
            _connection = connection;

            if (frame.Type != FrameType.Message)
                throw new ArgumentException("Frame must have FrameType 'Message'", "frame");

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(frame.Data, TIMESTAMP_START, TIMESTAMP_COUNT);
                Array.Reverse(frame.Data, ATTEMPTS_START, ATTEMPTS_COUNT);
            }

            Timestamp = BitConverter.ToInt64(frame.Data, TIMESTAMP_START);
            Attempts = BitConverter.ToInt16(frame.Data, ATTEMPTS_START);
            Id = Encoding.ASCII.GetString(frame.Data, ID_START, ID_COUNT);

            // Data
            var dataLength = frame.Data.Length - DATA_START;
            Body = new byte[dataLength];
            Array.ConstrainedCopy(frame.Data, DATA_START, Body, 0, dataLength);
        }

        /// <summary>
        /// Finishes the message, which tells the nsqd instance the message has been processed.
        /// </summary>
        public Task FinishAsync()
        {
            return _connection.SendCommandAsync(new Finish(this));
        }

        /// <summary>
        /// Re-queues the message in NSQ so it will be delivered again to a consumer.
        /// </summary>
        public Task RequeueAsync()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Notifies NSQ that the message is still being processed. This prevents
        /// NSQ from re-queueing the message automatically.
        /// </summary>
        public Task TouchAsync()
        {
            throw new NotImplementedException();
        }
    }
}
