private DocumentAttachment ParseDocAttachment(JObject _jDoc)
        {
            try
            {
                if (_jDoc[PAttachmentsDocument] is JObject jDoc)
                {
                    var docAttachment = new DocumentAttachment();

                    docAttachment.Id        = jDoc[PId].Value <int>();
                    docAttachment.OwnerId   = jDoc[PAttachmentOwnerId].Value <int>();
                    docAttachment.Title     = jDoc[PTitle]?.Value <string>();
                    docAttachment.Date      = EpochTimeConverter.ConvertToDateTime(jDoc[PDate].Value <long>());
                    docAttachment.Url       = jDoc[PUrl].Value <string>();
                    docAttachment.AccessKey = jDoc[PAttachmentAccessKey]?.Value <string>();

                    return(docAttachment);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"Failed to parse doc attachment \n {_jDoc.ToString()}", ex);
            }

            throw new DeserializerException($"Failed recognize jObject as document attachment \n {_jDoc?.ToString()}");
        }
        private PhotoAttachment ParsePhotoAttachment(JObject _jPhoto)
        {
            try
            {
                if (_jPhoto[PAttachmentsPhoto] is JObject photoJObj)
                {
                    var photoAttachment = new PhotoAttachment();

                    photoAttachment.Id        = photoJObj[PId].Value <int>();
                    photoAttachment.AlbumId   = photoJObj[PPhotoAlbumId].Value <int>();
                    photoAttachment.OwnerId   = photoJObj[PAttachmentOwnerId].Value <int>();
                    photoAttachment.UserId    = photoJObj[PPhotoUserId]?.Value <int>();
                    photoAttachment.Text      = photoJObj[PPhotoText]?.Value <string>();
                    photoAttachment.Date      = EpochTimeConverter.ConvertToDateTime(photoJObj[PDate].Value <long>());
                    photoAttachment.AccessKey = photoJObj[PAttachmentAccessKey]?.Value <string>();

                    var sizes = new List <PhotoSizeInfo>();

                    if (photoJObj[PPhotoSizes] is JArray jSizes)
                    {
                        foreach (var jSize in jSizes)
                        {
                            var type   = (PhotoSizeType)Enum.Parse(typeof(PhotoSizeType), jSize[PSizesType].Value <string>());
                            var url    = jSize[PUrl].Value <string>();
                            var width  = jSize[PSizesWidth].Value <int>();
                            var height = jSize[PSizesHeight].Value <int>();

                            var sizeInfo = new PhotoSizeInfo(type, url, width, height);

                            sizes.Add(sizeInfo);
                        }
                    }

                    photoAttachment.Sizes = sizes.ToArray();

                    return(photoAttachment);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"Failed to parse photo attachment \n {_jPhoto.ToString()}", ex);
            }

            throw new ArgumentException($"Failed recognize jObject as photo attachment \n {_jPhoto?.ToString()}");
        }
        private Post ParsePostItem(JObject _jPostItem, Func <VideoInfo, string> _loadVideoItem)
        {
            try
            {
                var post = new Post();

                post.SourceId    = _jPostItem[PSourceId].Value <int>();
                post.Date        = EpochTimeConverter.ConvertToDateTime(_jPostItem[PItemDate].Value <long>());
                post.PostId      = _jPostItem[PItemId].Value <int>();
                post.Text        = _jPostItem[PItemText].Value <string>();
                post.SignerId    = _jPostItem[PItemSignerId]?.Value <int>() ?? null;
                post.MarkedAsAds = _jPostItem[PItemMarkedAsAds].Value <int>() != 0;
                post.PostSource  = ParsePostSource((JObject)_jPostItem[PPostSource]);

                var attachmentsRaw = _jPostItem[PAttachments];
                if (attachmentsRaw != null)
                {
                    post.Attachments = ParseAttachments(attachmentsRaw, _loadVideoItem).ToArray();
                }

                post.Comments = ParseComments((JObject)_jPostItem[PComments]);
                post.Likes    = ParseLikes((JObject)_jPostItem[PLikes]);
                post.Reposts  = ParseReposts((JObject)_jPostItem[PReposts]);

                if (_jPostItem.ContainsKey(PViews))
                {
                    post.Views = ParseViews((JObject)_jPostItem[PViews]);
                }

                var rawHistoryElem = _jPostItem[PCopyHistrory];

                if (rawHistoryElem != null)
                {
                    post.CopyHistory = ParseHistory(rawHistoryElem, _loadVideoItem).ToArray();
                }

                return(post);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"Failed to parse post item \n {_jPostItem?.ToString()}", ex);
            }
        }
        private List <HistoryPost> ParseHistory(JToken _jHistory, Func <VideoInfo, string> _loadVideoItem)
        {
            if (_jHistory is JArray jCopyHistory)
            {
                try
                {
                    var historyCollection = new List <HistoryPost>();

                    foreach (JObject jHistoryElement in jCopyHistory)
                    {
                        var historyPost = new HistoryPost();

                        historyPost.Id         = jHistoryElement[PId].Value <int>();
                        historyPost.OwnerId    = jHistoryElement[PHistoryOwnerId].Value <int>();
                        historyPost.FromId     = jHistoryElement[PHistoryFromId].Value <int>();
                        historyPost.Date       = EpochTimeConverter.ConvertToDateTime(jHistoryElement[PDate].Value <long>());
                        historyPost.Text       = jHistoryElement[PItemText].Value <string>();
                        historyPost.PostSource = ParsePostSource((JObject)jHistoryElement[PPostSource]);

                        var attachmentsRaw = jHistoryElement[PAttachments];

                        if (attachmentsRaw != null)
                        {
                            historyPost.Attachments = ParseAttachments(attachmentsRaw, _loadVideoItem).ToArray();
                        }

                        historyCollection.Add(historyPost);
                    }

                    return(historyCollection);
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException($"Failed to parse history \n {_jHistory.ToString()}", ex);
                }
            }

            throw new ArgumentException($"History element not recognized as array \n {_jHistory?.ToString()}");
        }
        private VideoAttachment ParseVideoAttachment(JObject _jVideo, Func <VideoInfo, string> _loadVideoItem)
        {
            try
            {
                if (_jVideo[PAttachmentsVideo] is JObject jVideo)
                {
                    var videoAttachment = new VideoAttachment
                    {
                        Id                       = jVideo[PId].Value <int>(),
                        OwnerId                  = jVideo[PAttachmentOwnerId].Value <int>(),
                        Title                    = jVideo[PTitle].Value <string>(),
                        Description              = jVideo[PVideoDescription]?.Value <string>(),
                        Duration                 = jVideo[PVideoDuration].Value <int>(),
                        Date                     = EpochTimeConverter.ConvertToDateTime(jVideo[PDate].Value <long>()),
                        Views                    = jVideo[PVideoViews].Value <int>(),
                        CommentsCount            = jVideo[PVideoComments]?.Value <int>(),
                        PlayerUrl                = jVideo[PVideoPlayer]?.Value <string>(),
                        AccessKey                = jVideo[PAttachmentAccessKey].Value <string>(),
                        IsContentRestricted      = jVideo.ContainsKey(PVideoContentRestricted),
                        ContentRestrictedMessage = jVideo[PVideoContentRestrictedMessage]?.Value <string>()
                    };


                    if (jVideo.ContainsKey(PVideoImage) && jVideo[PVideoImage] is JArray jImages)
                    {
                        videoAttachment.Images = jImages.Select(_x => new Image
                        {
                            Height = _x[PSizesHeight].Value <int>(),
                            Width  = _x[PSizesWidth].Value <int>(),
                            Url    = _x[PUrl].Value <string>()
                        }).ToArray();
                    }

                    if (jVideo.ContainsKey(PVideoFirstFrame) && jVideo[PVideoFirstFrame] is JArray jFrames)
                    {
                        videoAttachment.FirstFrames = jFrames.Select(_x => new Image
                        {
                            Height = _x[PSizesHeight].Value <int>(),
                            Width  = _x[PSizesWidth].Value <int>(),
                            Url    = _x[PUrl].Value <string>()
                        }).ToArray();
                    }

                    if (videoAttachment.PlayerUrl == null && _loadVideoItem != null)
                    {
                        try
                        {
                            var data = _loadVideoItem(new VideoInfo
                            {
                                OwnerId = videoAttachment.OwnerId,
                                VideoId = videoAttachment.Id
                            });

                            var videoInfo = m_videoAttachmentDeserializer.Deserialize(data);

                            videoAttachment.PlayerUrl = videoInfo.PlayerUrl;
                        }
                        catch
                        {
                            //nothing to do
                        }
                    }

                    return(videoAttachment);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"Failed to parse video attachment \n {_jVideo.ToString()}", ex);
            }

            throw new ArgumentException($"Failed recognize jObject as video attachment \n {_jVideo?.ToString()}");
        }