
namespace FBPLib
{
    using System;
    using System.Collections;


    /// <summary>This class implements buffering between Component threads.
    /// One is created behind the scenes whenever two ports are connected.
    /// </summary>

    public class Connection : IInputPort
    {
        /* *
        * Copyright 2007, 2008, J. Paul Morrison.  At your option, you may copy, 
        * distribute, or make derivative works under the terms of the Clarified Artistic License, 
        * based on the Everything Development Company's Artistic License.  A document describing 
        * this License may be found at http://www.jpaulmorrison.com/fbp/artistic2.htm. 
        * THERE IS NO WARRANTY; USE THIS PRODUCT AT YOUR OWN RISK.
        * */
        internal PacketBuffer _buffer;
        // The unique receiver.  We need to activate() it whenever
        // packets arrive, or if connection is closed and receiver is IMustRun.
        public Component _receiver;

        // The Component who happens to be sending on a send()
        internal Component _sender;

        // Number of senders who have called setSender() but are not closed.
        internal int _senderCount = 0;

        // The list of types (Class objects) that senders have declared.
        // Vector senderTypes;

        // The type that the receiver has declared.
        internal System.Type _receiverType;
        // internal bool partial = true; //set by NewComponent 
        // internal bool arrayPort;  // used when processing InPort attribute
        //internal bool triggering;

        internal string _name;
        internal string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }
        public Component Receiver
        {
            get
            {
                return _receiver;
            }
            set { _receiver = value; }

        }

        /// <summary>Constructor: make a new connection of a given size
        /// </summary>
        internal Connection(int size)
        {
            _buffer = new PacketBuffer(size);
        }
        /// <summary>Invoked to tell us we have a(nother) sender.
        /// </summary>
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'bumpSenderCount'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        internal void BumpSenderCount(OutputPort op)
        {
            lock (this)
            {
                _senderCount++;
            }
        }
        /// <summary>Returns the number of packets this Connection can hold.
        /// </summary>
        public int Capacity()
        {
            return _buffer.Capacity();
        }
        /// <summary> Throws an error if the input object types and output object types do not match
        /// (This needs some more thinking about...)
        /// </summary>
        public void CheckTypes()
        {

            return;
            /*
            if (senderTypes == null) return;
            if (receiverType == Object.class) return;
            Enumeration types = senderTypes.elements();
            while (types.hasMoreElements()) {
            Class senderType = (Class)(types.nextElement());
            if (!(receiverType.isAssignableFrom(senderType)))
            FlowError.complain("Connection type mismatch");
            }
			
            */
        }
        /// <summary>The close input connection function.
        /// </summary>

        //UPGRADE_NOTE: Synchronized keyword was removed from method 'close'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        public void Close()
        {
            lock (this)
            {
                _receiver._compLog.Trace("{0}: Close connection", Name);

                if (IsClosed())
                    return;
                _senderCount = 0; // set sender count to zero
                if (_buffer.Count() > 0)
                    Console.Out.WriteLine(_buffer.Count() + " packets on input connection lost");

                System.Threading.Monitor.PulseAll(this); // wakes up any senders waiting for slots

            }
        }
        /// <summary> Close the sending Component
        /// </summary>
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'closeSender'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        internal void IndicateOneSenderClosed()
        {
            lock (_receiver._inputPorts)
            {
                lock (this)
                {
                    if (!IsClosed())
                    {
                        --_senderCount;
                        if (IsDrained())
                        {
                            if (_receiver.Status == Component.States.Dormant ||
                              _receiver.Status == Component.States.NotStarted)
                                _receiver.Activate();
                            else
                                System.Threading.Monitor.PulseAll(this);
                        }
                    }
                }
            }
        }
        /// <summary>Return the number of packets currently in this Connection.
        /// </summary>
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'count'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        public int Count()
        {
            lock (this)
            {
                return _buffer.Count();
            }
        }
        /// <summary>Returns true if this connection is closed (not necessarily drained).
        /// </summary>
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'IsClosed'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        internal bool IsClosed()
        {
            lock (this)
            {
                return (_senderCount == 0);
            }
        }
        /// <summary>Returns true if this connection is drained (closed and empty).
        /// </summary>
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'isDrained'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        internal bool IsDrained()
        {
            lock (this)
            {
                return (_senderCount == 0 && _buffer.Count() == 0);
            }
        }
        /// <summary>Returns true if this connection is empty
        /// </summary>
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'isEmpty'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        internal bool IsEmpty()
        {
            lock (this)
            {
                return (_buffer.Count() == 0);
            }
        }
        /// <summary>Returns true if this connection is full.
        /// </summary>
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'isFull'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        internal bool IsFull()
        {
            lock (this)
            {
                return (_buffer.Count() == _buffer.Capacity());
            }
        }

        /// <summary>The receive function.
        /// See IInputPort.receive.
        /// </summary>
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'receive'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        public Packet Receive()
        {
            lock (this)
            {


                _receiver._compLog.Trace("{0}: Receiving", Name);

                if (IsDrained())
                {

                    _receiver._compLog.Trace("{0}: Recv/close", Name);
                    return null;
                }

                if (IsEmpty())
                {

                    _receiver.Status = Component.States.SuspRecv;

                    _receiver._compLog.Trace("{0}: Recv/susp", _receiver.Name);

                    try
                    {
                        System.Threading.Monitor.Wait(this);
                    }
                    catch (System.Threading.ThreadInterruptedException e)
                    {
                        //UPGRADE_NOTE: Exception 'java.lang.ThreadDeath' was converted to ' ' which has different behavior. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1100"'
                        //      throw new System.ApplicationException();
                    }
                    if (IsDrained())
                    {

                        _receiver._compLog.Trace("{0}: Receive drained", Name);
                        return null;
                    }

                    _receiver.Status = Component.States.Active;

                    _receiver._compLog.Trace("{0}: Recv/resume", _receiver.Name);

                }


                if (IsDrained())
                {

                    _receiver._compLog.Trace("{0}: Receive drained", Name);
                    return null;
                }

                if (IsFull())
                    System.Threading.Monitor.PulseAll(this);
                // if was full, notify any Components waiting to send
                Packet packet = _buffer.Take();

                packet.Owner = _receiver;

                if (null == packet.Content)

                    _receiver._compLog.Trace("{0}: Receive OK", Name);
                else

                    _receiver._compLog.Trace("{0}: Receive OK: {1}", Name, packet);
                _receiver._network._active = true;
                return packet;
            }
        }

        /// <summary>The send function.  See OutputPort.send.
        /// </summary>
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'send'. Lock expression was added. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1027"'
        internal bool Send(Packet packet, OutputPort op)
        {
            lock (this)
            {
                if (packet == null)
                    throw new System.ArgumentException();
                _sender = op._sender;


                if (IsClosed())
                {

                    return false;
                }


                // if (IsEmpty())
                //  System.Threading.Monitor.PulseAll(this);



                while (IsFull())
                {
                    _sender.Status = Component.States.SuspSend;

                    _sender._compLog.Trace("{0}: Send/susp", _sender.Name);
                    try
                    {
                        System.Threading.Monitor.Wait(this);
                    }
                    catch (System.Threading.ThreadInterruptedException e)
                    {
                        //UPGRADE_NOTE: Exception 'java.lang.ThreadDeath' was converted to ' ' which has different behavior. 'ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="jlca1100"'
                        //      throw new System.ApplicationException();
                    }

                    _sender = op._sender;
                    _sender.Status = Component.States.Active;

                    _sender._compLog.Trace("{0}: Send/resume", _sender.Name);

                }

                if (IsClosed())
                {

                    _sender._compLog.Trace("{0}: Send/close", _sender.Name);
                    return false;
                }
                packet.ClearOwner();
                _buffer.Put(packet);

                if (_receiver.Status == Component.States.Dormant ||
                    _receiver.Status == Component.States.NotStarted)
                    _receiver.Activate();
                else
                    System.Threading.Monitor.PulseAll(this);

                

                _sender._network._active = true;
                _sender = null;
                
                return true;
            }
        }
        /// <summary>Invoked to tell us we have a receiver.
        /// </summary>

        internal void setReceiver(Component newReceiver)
        {
            _receiver = newReceiver;
        }

        /// <summary>Invoked to tell us the type of packet content being sent
        /// or expected.  The receiver's type must be a supertype of
        /// every sender's type, or the Network is ill-formed.
        /// </summary>
        public void SetType(Type type)
        {

            if (type == null) return;

            _receiverType = type;

        }

    }
}