public static StreamPrediction ConvertNode(object nodeRef)
        {
            var node = (dynamic)nodeRef;
            var obj  = new StreamPrediction();

            obj.ID     = node["id"];
            obj.Status = Enum.Parse(typeof(PredictionStatus), node["status"]);
            obj.Title  = node["title"];

            if (DateTime.TryParse(node["created_at"], out DateTime output1))
            {
                obj.CreateAt = output1;
            }

            if (DateTime.TryParse(node["ended_at"], out DateTime output2))
            {
                obj.EndedAt = output2;
            }

            if (DateTime.TryParse(node["locked_at"], out DateTime output3))
            {
                obj.LockedAt = output3;
            }

            obj.PredictionWindow = node["prediction_window"];
            var predictionResults = StreamPredictionOutcome.Convert(node["outcomes"]);

            obj.FirstOutcome  = predictionResults[0];
            obj.SecondOutcome = predictionResults[1];

            return(obj);
        }
        internal async Task <StreamPrediction> GetCurrentPredictionAsync()
        {
            DebugLogging.Log("Getting current prediction", true);

            if (BroadcasterID == "")
            {
                await GetUserIDAsync();
            }

            if (BroadcasterID == "")
            {
                DebugLogging.Log("[ERROR] Broadcaster ID wasn't set despite multiple requests!");
                return(null);
            }

            var requestResult = await PerformGetRequestAsync(
                BuildURI(new string[] { "predictions" }, new Tuple <string, string>[] { new Tuple <string, string>("broadcaster_id", BroadcasterID) }),
                new Dictionary <string, string>() { }
                );

            if (requestResult["data"] != null)
            {
                var dataNode = ((IEnumerable <dynamic>)requestResult["data"]).First();

                if (dataNode["status"] != null)
                {
                    DebugLogging.Log("Converting response to Stream Prediction object.", true);
                    StreamPrediction prediction = StreamPrediction.ConvertNode(dataNode);
                    DebugLogging.Log("Current prediction is: '" + prediction.Title + "' and the status is " + prediction.Status, true);
                    return(prediction);
                }
            }
            DebugLogging.Log("Failed to get current (or last) prediction.");
            return(null);
        }
        internal async Task <StreamPrediction> CompleteWithOptionAsync(int winningOption)
        {
            if (BroadcasterID == "")
            {
                await GetUserIDAsync();
            }

            if (BroadcasterID != "")
            {
                StreamPrediction prediction = TwitchConnection.GetInstance().CurrentPrediction;
                if (prediction == null)
                {
                    prediction = await GetCurrentPredictionAsync();
                }

                if (prediction != null && (prediction.Status == StreamPrediction.PredictionStatus.ACTIVE || prediction.Status == StreamPrediction.PredictionStatus.LOCKED))
                {
                    DebugLogging.Log("Trying to close a prediction (" + prediction.ID + ") with outcome #" + (winningOption + 1).ToString(), true);

                    var parameters = new StringBuilder();
                    parameters.AppendLine("{");
                    parameters.AppendLine("\"broadcaster_id\": \"" + BroadcasterID + "\",");
                    parameters.AppendLine("\"id\": \"" + prediction.ID + "\",");

                    parameters.AppendLine("\"status\": \"" + StreamPrediction.PredictionStatus.RESOLVED + "\",");
                    parameters.AppendLine("\"winning_outcome_id\": \"" + (winningOption == 0 ? prediction.FirstOutcome.ID : prediction.SecondOutcome.ID) + "\"");
                    parameters.AppendLine("}");

                    var response = await PerformPatchRequestAsync(BuildURI(new string[] { "predictions" }, new Tuple <string, string>[] { }),
                                                                  new Dictionary <string, string>() { }, parameters.ToString()
                                                                  );

                    if (response["data"] != null)
                    {
                        var dataNode = ((IEnumerable <dynamic>)response["data"]).First();
                        if (dataNode["id"] != null)
                        {
                            if (dataNode["status"] != null)
                            {
                                StreamPrediction newPredictionState = StreamPrediction.ConvertNode(dataNode);
                                if (newPredictionState.Status == StreamPrediction.PredictionStatus.RESOLVED)
                                {
                                    DebugLogging.Log("Successfully closed a prediction!", true);
                                    return(newPredictionState);
                                }
                                else
                                {
                                    DebugLogging.Log("Failed to close a new prediction!");
                                    return(null);
                                }
                            }
                        }
                    }
                }
                DebugLogging.Log("Prediction already closed", true);
                return(prediction);
            }
            return(null);
        }
        public async void CancelPrediction(TimeSpan delay)
        {
            delayTasksRunning.Clear();
            if (delay != TimeSpan.Zero)
            {
                var waitTask = Task.Delay(delay);
                delayTasksRunning.Add(waitTask);
                await waitTask;
                if (!delayTasksRunning.Contains(waitTask))
                {
                    return;
                }
                delayTasksRunning.Remove(waitTask);
            }

            var result = await twitchRequests.CancelPredictionAsync();

            CurrentPrediction = result;
        }
        public async void CompletePrediction(int winningOutcome, TimeSpan delay)
        {
            delayTasksRunning.Clear();
            if (delay != TimeSpan.Zero)
            {
                var waitTask = Task.Delay(delay);
                delayTasksRunning.Add(waitTask);
                await waitTask;
                if (!delayTasksRunning.Contains(waitTask))
                {
                    return;
                }
                delayTasksRunning.Remove(waitTask);
            }


            var result = await twitchRequests.CompleteWithOptionAsync(winningOutcome);

            CurrentPrediction = result;
        }
        public async void StartNewPrediction(string Header, string Option1, string Option2, uint Lenght, TimeSpan delay)
        {
            delayTasksRunning.Clear();
            if (delay != TimeSpan.Zero)
            {
                var waitTask = Task.Delay(delay);
                delayTasksRunning.Add(waitTask);
                await waitTask;
                if (!delayTasksRunning.Contains(waitTask))
                {
                    return;
                }
                delayTasksRunning.Remove(waitTask);
            }

            var newPrediction = await twitchRequests.StartPredictionAsync(Header, Option1, Option2, Lenght);

            if (newPrediction != null)
            {
                CurrentPrediction = newPrediction;
            }
        }
        internal async Task <StreamPrediction> CancelPredictionAsync()
        {
            if (BroadcasterID == "")
            {
                await GetUserIDAsync();
            }

            if (BroadcasterID != "")
            {
                StreamPrediction prediction = TwitchConnection.GetInstance().CurrentPrediction;
                if (prediction == null)
                {
                    prediction = await GetCurrentPredictionAsync();
                }

                if (prediction != null && (prediction.Status == StreamPrediction.PredictionStatus.ACTIVE || prediction.Status == StreamPrediction.PredictionStatus.LOCKED))
                {
                    DebugLogging.Log("Trying to cancel a cancel prediction (" + prediction.ID + ")", true);

                    var parameters = new StringBuilder();
                    parameters.AppendLine("{");
                    parameters.AppendLine("\"broadcaster_id\": \"" + BroadcasterID + "\",");
                    parameters.AppendLine("\"id\": \"" + prediction.ID + "\",");

                    parameters.AppendLine("\"status\": \"" + StreamPrediction.PredictionStatus.CANCELED + "\"");
                    parameters.AppendLine("}");

                    var response = await PerformPatchRequestAsync(BuildURI(new string[] { "predictions" }, new Tuple <string, string>[] { }),
                                                                  new Dictionary <string, string>() { }, parameters.ToString()
                                                                  );

                    if (response["data"] != null)
                    {
                        var dataNode = ((IEnumerable <dynamic>)response["data"]).First();
                        if (dataNode["id"] != null)
                        {
                            if (dataNode["status"] != null)
                            {
                                StreamPrediction newPredictionState = StreamPrediction.ConvertNode(dataNode);
                                if (newPredictionState.Status == StreamPrediction.PredictionStatus.CANCELED)
                                {
                                    DebugLogging.Log("Successfully cancelled a new prediction!", true);
                                    return(newPredictionState);
                                }
                                else
                                {
                                    DebugLogging.Log("Failed to cancelled a new prediction!");
                                    return(null);
                                }
                            }
                        }
                    }
                    DebugLogging.Log("[ERROR] Incorrect response?");
                    return(null);
                }
                DebugLogging.Log("Prediction already closed", true);
                return(prediction);
            }

            DebugLogging.Log("[ERROR] Can not cancel prediction. Broadcaster ID is null!");
            return(null);
        }
        internal async Task <StreamPrediction> StartPredictionAsync(string header, string option1, string option2, uint lenght)
        {
            StreamPrediction newPrediction;

            DebugLogging.Log("Trying to start a new prediction.", true);

            if (BroadcasterID == "")
            {
                await GetUserIDAsync();
            }

            if (BroadcasterID == "")
            {
                DebugLogging.Log("[ERROR] Broadcaster ID wasn't set despite multiple requests!");
                return(null);
            }

            newPrediction = null;
            if (lenght > 1800)
            {
                lenght = 1779;
            }
            if (lenght < 1)
            {
                lenght = 1;
            }

            var parameters = new StringBuilder();

            parameters.AppendLine("{");
            parameters.AppendLine("\"broadcaster_id\": \"" + BroadcasterID + "\",");
            parameters.AppendLine("\"title\": \"" + JSON.Escape(header) + "\",");

            parameters.AppendLine("\"outcomes\": [");
            parameters.AppendLine("{");
            parameters.AppendLine("\"title\": \"" + JSON.Escape(option1) + "\"");
            parameters.AppendLine("},");
            parameters.AppendLine("{");
            parameters.AppendLine("\"title\": \"" + JSON.Escape(option2) + "\"");
            parameters.AppendLine("}");
            parameters.AppendLine("],");

            parameters.AppendLine("\"prediction_window\": " + lenght.ToString() + "");
            parameters.AppendLine("}");

            try
            {
                var response = await PerformPostRequestAsync(
                    BuildURI(new string[] { "predictions" }, new Tuple <string, string>[] { }),
                    new Dictionary <string, string>() { }, parameters.ToString()
                    );

                if (response["data"] != null)
                {
                    var dataNode = ((IEnumerable <dynamic>)response["data"]).First();
                    if (dataNode["id"] != null)
                    {
                        if (dataNode["status"] != null)
                        {
                            newPrediction = StreamPrediction.ConvertNode(dataNode);
                            DebugLogging.Log("Successfully created a new prediction!", true);
                            return(newPrediction);
                        }
                        DebugLogging.Log("[ERROR] Incorrect response?");
                        return(newPrediction);
                    }
                }
                DebugLogging.Log("[ERROR] Incorrect response?");
                return(newPrediction);
            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.ProtocolError)
                {
                    if (((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.BadRequest)
                    {
                        var rawResponse = string.Empty;

                        var alreadyClosedStream = e.Response.GetResponseStream() as MemoryStream;
                        using (var brandNewStream = new MemoryStream(alreadyClosedStream.ToArray()))
                            using (var reader = new StreamReader(brandNewStream))
                                rawResponse = reader.ReadToEnd();

                        DebugLogging.Log("Error creating a prediction: " + rawResponse);
                        return(newPrediction);
                    }
                    DebugLogging.Log("[ERROR] " + e);
                    return(newPrediction);
                }
                DebugLogging.Log("[ERROR] " + e);
                return(newPrediction);
            }
        }