/// <summary>
        /// writes as run log
        /// </summary>
        /// <param name="runLog">log message</param>
        /// <param name="toPrint">log data if any</param>
        /// <exception cref="ObjectDisposedException">
        /// thrown when invoked on disposed object.
        /// </exception>
        private void writeAsRunLog(
            string runLog,
            DataBlock toPrint)
        {
            if (IsDisposed)
                throw
                    new ObjectDisposedException("IOHandler");

            var logger = AsRunLogger;
            if (null == logger)
                return;

            lock (logger)
            {
                logger.WriteLine(runLog);

                logger.WriteLine(
                    toPrint.ToString(
                        ShowByteDisplayInAsRunLog));

                logger.WriteLine();
            }
        }
        /// <summary>
        /// inner operation for sending data to PS.
        /// In this context, locking is handled upward(in calling context),
        /// so we don't care about synchronization here.
        /// </summary>
        /// <param name="toSend">data to be sent to PS</param>
        /// <returns>
        /// true, when sending operation succeeded, so we can proceed forward.
        /// else communication error arised, so we should stop proceeding.
        /// </returns>
        /// <exception cref="ObjectDisposedException">
        /// thrown when invoking on disposed object
        /// </exception>
        private bool sendDataBlock(
            DataBlock toSend)
        {
            // 01. check if disposed
            if (IsDisposed)
                throw
                    new ObjectDisposedException("IOHandler");

            // 02. prepare data to send
            var plainBytes = toSend.GetPlainBuffer();
            writeAsRunLog(
                "Bytes to encrypt: " + plainBytes.Length,
                plainBytes);

            var encryptedBytes = toSend.GetEncryptedBuffer(_encryptDecrypt);
            writeAsRunLog(
                "Bytes encrypted: " + encryptedBytes.Length,
                encryptedBytes);

            try
            {
                // 03. send data to PS
                using (
                    var bwWriter =
                        new BinaryWriter(
                            _netStream,
                            Encoding.UTF8,
                            true))
                {
                    // send length value in network order
                    var lengthValue =
                        IPAddress.HostToNetworkOrder(
                            (int)PhotoShopConstants.COMM_LENGTH +
                                encryptedBytes.Length);
                    bwWriter.Write((int)lengthValue);

                    // send status value in network order
                    var statusValue =
                        IPAddress.HostToNetworkOrder(
                            (int)PhotoShopConstants.NO_COMM_ERROR);
                    bwWriter.Write((int)statusValue);

                    // send encrypted data
                    bwWriter.Write(
                        encryptedBytes,
                        0,
                        encryptedBytes.Length);

                    bwWriter.Flush();
                }

                return true;
            }
            catch (Exception e)
            {
                var strOut =
                    string.Format(
                        "{0} raised during data sending to PS \n" +
                            "with message \"{1}\"",
                        e.GetType().Name,
                        e.Message);
                writeAsRunLog(strOut);

                return false;
            }
        }
        /// <summary>
        /// create new <see cref="DataBlock"/> from given encrypted byte array
        /// </summary>
        /// <param name="decryptor">
        /// used for descryption of given encrypted byte array</param>
        /// <param name="encryptedBytes">
        /// encrypted byte array to decrypt</param>
        /// <returns>
        /// new <see cref="DataBlock"/> from given encrypted byte array
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// thrown when given "decryptor" parameter is null
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when given "encryptedBytes" parameter is null
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// thrown when given "encryptedBytes" parameter is shorter than 
        /// <seealso cref="PhotoShopConstants.PROTOCOL_LENGTH"/>
        /// </exception>
        public static DataBlock CreateNonErrorDataBlock(
            EncryptDecrypt decryptor,
            byte[] encryptedBytes)
        {
            // check for required conditions
            if (null == decryptor)
                throw new ArgumentNullException("decryptor");
            if (null == encryptedBytes)
                throw new ArgumentNullException("encryptedBytes");
            if (encryptedBytes.Length < PhotoShopConstants.PROTOCOL_LENGTH)
                throw new ArgumentOutOfRangeException("encryptedBytes");

            using (var msStream = new MemoryStream(decryptor.Decrypt(encryptedBytes)))
            {
                var result = new DataBlock();
                using (var brReader = new BinaryReader(msStream))
                {
                    // protocol version, transaction id, content type are passed
                    // with Big-Endian byte order
                    // so need to convert to host byte order, ie little-endian
                    result.ProtocolVersion =
                        IPAddress.NetworkToHostOrder(
                            brReader.ReadInt32());
                    result.TransactionID =
                        IPAddress.NetworkToHostOrder(
                            brReader.ReadInt32());
                    var contentTypeValue =
                        IPAddress.NetworkToHostOrder(
                            brReader.ReadInt32());
                    switch (contentTypeValue)
                    {
                        case (int)ContentType.ERRORSTRING:
                        case (int)ContentType.JAVASCRIPT:
                        case (int)ContentType.IMAGE:
                        case (int)ContentType.PROFILE:
                        case (int)ContentType.DATA:
                            result.ContentType = (ContentType)contentTypeValue;
                            break;
                        default:
                            break;
                    }
                    var toRead =
                        (int)(msStream.Length - PhotoShopConstants.PROTOCOL_LENGTH);
                    result.Content =
                        (0 == toRead) ?
                        null :
                        brReader.ReadBytes(toRead);

                    return result;
                }
            }
        }
        /// <summary>
        /// base function for communication with PhotoShop
        /// </summary>
        /// <param name="toSend">
        /// <see cref="DataBlock"/> containing data to pass to PhotoShop</param>
        /// <returns>PhotoShop Response w.r.t <paramref name="toSend"/></returns>
        /// <exception cref="ObjectDisposedException">
        /// thrown when invoking on disposed object
        /// </exception>
        private PhotoShopResponse SendAndReceive(
            DataBlock toSend)
        {
            // 01. check if object is disposed
            if (IsDisposed)
                throw
                    new ObjectDisposedException("IOHandler");

            var commError =
                new PhotoShopResponse()
                {
                    Status = CommunicationStatus.ERROR_COMMUNICATION
                };

            lock (_netStream)
            {
                // 02. send data to PS
                if (false == sendDataBlock(toSend))
                    return commError;

                // 03. receive data from PS for dedicated transaction ID
                return
                    getPhotoShopResponseUntil(toSend.TransactionID);
            }
        }
        /// <summary>
        /// create new <see cref="DataBlock"/> from given error byte array, 
        /// ie plain byte array
        /// </summary>
        /// <param name="errorBytes">byte array containing raw data.</param>
        /// <returns>
        /// new <see cref="DataBlock"/> from given error byte array
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// thrown when given "errorBytes" parameter is null
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// thrown when given "errorBytes" parameter is shorter than 
        /// <seealso cref="PhotoShopConstants.PROTOCOL_LENGTH"/>
        /// </exception>
        public static DataBlock CreateErrorDataBlock(
            byte[] errorBytes)
        {
            // check for required conditions
            if (null == errorBytes)
                throw new ArgumentNullException("errorBytes");
            if (errorBytes.Length < PhotoShopConstants.PROTOCOL_LENGTH)
                throw new ArgumentOutOfRangeException("errorBytes");

            using (var msStream = new MemoryStream(errorBytes))
            {
                var result = new DataBlock();
                using (var brReader = new BinaryReader(msStream))
                {
                    // never mind protocol version, transaction id, content type.
                    result.ProtocolVersion =
                        IPAddress.NetworkToHostOrder(
                            brReader.ReadInt32());
                    result.TransactionID =
                        IPAddress.NetworkToHostOrder(
                            brReader.ReadInt32());
                    // meaningless operation, just move stream pointer forward.
                    var contentTypeValue =
                        IPAddress.NetworkToHostOrder(
                            brReader.ReadInt32());

                    // context assumes that comming content is error string type.
                    result.ContentType = ContentType.ERRORSTRING;

                    var toRead =
                        (int)(msStream.Length - PhotoShopConstants.PROTOCOL_LENGTH);
                    result.Content =
                        (0 == toRead) ?
                        null :
                        brReader.ReadBytes(toRead);

                    return result;
                }
            }
        }