//  
//   Rebex Sample Code License
// 
//   Copyright 2025, REBEX CR s.r.o.
//   All rights reserved.
//   https://www.rebex.net/
// 
//   Permission to use, copy, modify, and/or distribute this software for any
//   purpose with or without fee is hereby granted.
// 
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//   OTHER DEALINGS IN THE SOFTWARE.
// 

using System;
using System.ComponentModel;
using Rebex.Net;

namespace Rebex.TerminalEmulation
{
    /// <summary>
    /// Telnet terminal emulator control.
    /// </summary>
    public partial class TelnetTerminalControl : TerminalControl
    {
        private Telnet _telnet;

        private string _serverName = ServerNameDefault;
        private int _serverPort = ServerPortDefault;
        private Proxy _proxy;
        private ILogWriter _logger;

        // default values for both designer and properties
        private const string ServerNameDefault = "";
        private const int ServerPortDefault = Telnet.DefaultPort;

        /// <summary>
        /// Gets or sets the server address - either a hostname or a dotted string IP address.
        /// </summary>
        /// <value>Server address.</value>
        [Description("Gets or sets the server address - either a hostname or a dotted string IP address.")]
        [DefaultValue(ServerNameDefault)]
        public string ServerName
        {
            get { return _serverName; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                _serverName = value;
            }
        }

        /// <summary>
        /// Gets or sets the server port. Default value is 23.
        /// </summary>
        /// <value>Server port.</value>
        [DefaultValue(ServerPortDefault)]
        [Description("Gets or sets the server port. Default value is 23.")]
        public int ServerPort
        {
            get { return _serverPort; }
            set { _serverPort = value; }
        }

        /// <summary>
        /// Gets or sets the network proxy to use to access a remote server.
        /// </summary>
        /// <value>The <see cref="Proxy"/> to use to access a remote server.</value>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Proxy Proxy
        {
            get { return _proxy; }
            set { _proxy = value; }
        }

        /// <summary>
        /// Gets or sets the text associated with this control.  
        /// </summary>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override string Text
        {
            get { return base.Text; }
            set { base.Text = value; }
        }

        /// <summary>
        /// Gets or sets the logger used by this object.
        /// </summary>
        /// <value>Logger.</value>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ILogWriter LogWriter
        {
            get { return _logger; }
            set
            {
                _logger = value;
                if (_telnet != null)
                    _telnet.LogWriter = value;
            }
        }

        /// <summary>
        /// Occurs when connected to the server.
        /// </summary>
        public event EventHandler Connected;

        /// <summary>
        /// Raises the <see cref="Connected"/> event.
        /// </summary>
        /// <param name="e">An <see cref="EventArgs"/> that contains the event data.</param>
        protected virtual void OnConnected(EventArgs e)
        {
            EventHandler h = Connected;
            if (h != null)
                h(this, e);
        }

        /// <summary>
        /// Raises the <see cref="TerminalControl.Disconnected"/> event.
        /// </summary>
        /// <param name="e">An <see cref="EventArgs"/> that contains the event data.</param>
        protected override void OnDisconnected(EventArgs e)
        {
            base.OnDisconnected(e);
            _telnet = null;
        }

        /// <summary>
        /// Creates a new instance of Telnet terminal control.
        /// </summary>
        public TelnetTerminalControl()
        {
            // Set the default value to CRLF.
            // The TerminalControl can be used not only for communicating
            // with Telnet server, but also for connection to other server 
            // types implementing text-based protocols such as FTP, SMTP, POP3,
            // or IMAP. Some of those servers require CR LF line endings.
            Options.NewLineSequence = NewLineSequence.CRLF;
        }

        /// <summary>
        /// Connects to the Telnet server specified by <see cref="TelnetTerminalControl.ServerName"/> and <see cref="TelnetTerminalControl.ServerPort"/> properties.
        /// </summary>
        public void Connect()
        {
            if (_telnet != null)
                throw new TelnetException("Already connected to the server.");

            string serverName = _serverName.Trim();
            if (serverName.Length == 0)
                throw new TelnetException("Empty host name.");

            if (_serverPort < 1 || _serverPort > 65535)
                throw new TelnetException("Port out of range.");

            ConnectSync(serverName, _serverPort);
        }


        private void ConnectSync(string serverName, int port)
        {
            _telnet = new Telnet(serverName, port);
            _telnet.LogWriter = _logger;
            _telnet.SetSocketFactory(_proxy);

            bool ok = false;
            try
            {
                Bind(_telnet);
                ok = true;
            }
            finally
            {
                if (!ok)
                    _telnet = null;
            }

            if (InvokeRequired)
                Invoke(new Action<EventArgs>(OnConnected), EventArgs.Empty);
            else
                OnConnected(EventArgs.Empty);
        }

        /// <summary>
        /// Disconnects from the Telnet server.
        /// </summary>
        public void Disconnect()
        {
            Unbind();

            if (_telnet != null)
            {
                _telnet = null;
                OnDisconnected(EventArgs.Empty);
            }
        }
    }
}
