//-----------------------------------------------------------------------------
// 
//      Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
//      Licensed under the Apache License, Version 2.0 (the "License").
//      You may not use this file except in compliance with the License.
//      A copy of the License is located at
//
//      http://aws.amazon.com/apache2.0
//
//      or in the "license" file accompanying this file. This file is distributed
//      on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//      express or implied. See the License for the specific language governing
//      permissions and limitations under the License.
// 
//-----------------------------------------------------------------------------
using Amazon.Runtime.Internal.Util;
using Amazon.XRay.Recorder.Core.Sampling;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using Amazon.XRay.Recorder.Core.Exceptions;
namespace Amazon.XRay.Recorder.Core.Internal.Entities
{
    /// 
    /// A trace segment tracks a period of time associated with a computation or action, along with annotations and key / value data.
    /// A set of trace segments all of which share the same tracing ID form a trace.
    /// 
    /// 
    [Serializable]
    public class Segment : Entity
    {
        private long _size;           // Total number of subsegments
        private Lazy> _lazyService = new Lazy>();
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// Name of the node or service component.
        /// Unique id for the trace.
        /// Unique id of the upstream segment.
        public Segment(string name, string traceId = null, string parentId = null) : base(name)
        {
            if (traceId != null)
            {
                this.TraceId = traceId;
            }
            else
            {
                this.TraceId = Entities.TraceId.NewId();
            }
            if (parentId != null)
            {
                this.ParentId = parentId;
            }
            RootSegment = this;
        }
        /// 
        /// Gets or Sets the User for the segment
        /// 
        public string User { get; set; }
       
        /// 
        /// Gets or sets the origin of the segment.
        /// 
        public string Origin { get; set; }
        /// 
        /// Gets the size of subsegments.
        /// 
        public long Size
        {
            get
            {
                return Interlocked.Read(ref _size);
            }
        }
        /// 
        /// Gets the service.
        /// 
        public IDictionary Service
        {
            get
            {
                return _lazyService.Value;
            }
        }
        /// 
        /// Gets a value indicating whether any value has been added to service.
        /// 
        public bool IsServiceAdded
        {
            get
            {
                return _lazyService.IsValueCreated && !_lazyService.Value.IsEmpty;
            }
        }
        /// 
        /// Increment the size count.
        /// 
        public void IncrementSize()
        {
            Interlocked.Increment(ref _size);
        }
        /// 
        /// Decrement the size count.
        /// 
        public void DecrementSize()
        {
            Interlocked.Decrement(ref _size);
        }
        /// 
        /// Release reference to this instance of segment.
        /// 
        /// Reference count after release.
        public override long Release()
        {
            return DecrementReferenceCounter();
        }
        /// 
        /// Check if this segment or the root segment that this segment belongs to is ok to emit.
        /// 
        /// If the segment is ready to emit.
        public override bool IsEmittable()
        {
            return Reference == 0;
        }
        /// 
        /// Checks if the segment has been streamed already
        /// 
        /// The segment has been already streamed and no further operation can be performed on it.
        private void HasAlreadyStreamed()
        {
            if(HasStreamed)
            {
                throw new AlreadyEmittedException("Segment " + Name + " has already been emitted.");
            }
        }
        /// 
        /// Gets the value of the User for this segment
        /// 
        public string GetUser()
        {
            return User;
        }
        /// 
        /// Sets the User for this segment
        /// 
        /// the name of the user
        /// The value of user cannot be null.
        public void SetUser(string user)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }
            HasAlreadyStreamed();
            this.User = user;
        }
    }
}