using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Renci.SshNet.Channels;
using Renci.SshNet.Common;
using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes
{
    /// 
    /// Contains operation for working with SSH Shell.
    /// 
    [TestClass]
    public class ShellStreamTest : TestBase
    {
        private Mock _sessionMock;
        private Mock _connectionInfoMock;
        private Encoding _encoding;
        private string _terminalName;
        private uint _widthColumns;
        private uint _heightRows;
        private uint _widthPixels;
        private uint _heightPixels;
        private Dictionary _terminalModes;
        private int _bufferSize;
        private Mock _channelSessionMock;
        protected override void OnInit()
        {
            base.OnInit();
            var random = new Random();
            _terminalName = random.Next().ToString(CultureInfo.InvariantCulture);
            _widthColumns = (uint)random.Next();
            _heightRows = (uint)random.Next();
            _widthPixels = (uint)random.Next();
            _heightPixels = (uint)random.Next();
            _terminalModes = new Dictionary();
            _bufferSize = random.Next(100, 500);
            _encoding = Encoding.UTF8;
            _sessionMock = new Mock(MockBehavior.Strict);
            _connectionInfoMock = new Mock(MockBehavior.Strict);
            _channelSessionMock = new Mock(MockBehavior.Strict);
        }
        [TestMethod] // issue #2190
        public void ReadLine_MultiByteCharacters()
        {
            // bash: /root/menu.sh: Отказан
            const string data1 = "bash: /root/menu.sh: \u041e\u0442\u043a\u0430\u0437\u0430\u043d";
            // о в доступе
            const string data2 = "\u043e \u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0435";
            // done
            const string data3 = "done";
            var shellStream = CreateShellStream();
            var channelDataPublishThread = new Thread(() =>
                {
                    _channelSessionMock.Raise(p => p.DataReceived += null,
                        new ChannelDataEventArgs(5, _encoding.GetBytes(data1)));
                    Thread.Sleep(50);
                    _channelSessionMock.Raise(p => p.DataReceived += null,
                        new ChannelDataEventArgs(5, _encoding.GetBytes(data2 + "\r\n")));
                    _channelSessionMock.Raise(p => p.DataReceived += null,
                        new ChannelDataEventArgs(5, _encoding.GetBytes(data3 + "\r\n")));
                });
            channelDataPublishThread.Start();
            Assert.AreEqual(data1 + data2, shellStream.ReadLine());
            Assert.AreEqual(data3, shellStream.ReadLine());
            channelDataPublishThread.Join();
        }
        [TestMethod]
        public void Write_Text_ShouldWriteNothingWhenTextIsNull()
        {
            var shellStream = CreateShellStream();
            const string text = null;
            shellStream.Write(text);
            _channelSessionMock.VerifyAll();
        }
        [TestMethod]
        public void WriteLine_Line_ShouldOnlyWriteLineTerminatorWhenLineIsNull()
        {
            var shellStream = CreateShellStream();
            const string line = null;
            var lineTerminator = _encoding.GetBytes("\r");
            _ = _channelSessionMock.Setup(p => p.SendData(
                                       It.Is(data => data.Take(lineTerminator.Length).IsEqualTo(lineTerminator)),
                                       0,
                                       lineTerminator.Length));
            shellStream.WriteLine(line);
            _channelSessionMock.VerifyAll();
        }
        [TestMethod]
        public void Write_AfterDispose_ThrowsObjectDisposedException()
        {
            var shellStream = CreateShellStream();
            _channelSessionMock.Setup(p => p.Dispose());
            shellStream.Dispose();
            var bytes = _encoding.GetBytes("Hello World!");
            Assert.ThrowsException(() => shellStream.Write(bytes, 0, bytes.Length));
        }
        private ShellStream CreateShellStream()
        {
            _sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
            _connectionInfoMock.Setup(p => p.Encoding).Returns(_encoding);
            _sessionMock.Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object);
            _channelSessionMock.Setup(p => p.Open());
            _channelSessionMock.Setup(p => p.SendPseudoTerminalRequest(_terminalName, _widthColumns, _heightRows,
                _widthPixels, _heightPixels, _terminalModes)).Returns(true);
            _channelSessionMock.Setup(p => p.SendShellRequest()).Returns(true);
            return new ShellStream(_sessionMock.Object,
                                   _terminalName,
                                   _widthColumns,
                                   _heightRows,
                                   _widthPixels,
                                   _heightPixels,
                                   _terminalModes,
                                   _bufferSize);
        }
    }
}