public static void WriteConfig(Config config, string path)
        {
            var oldPath = String.Concat(path, ".old");
            File.Copy(path, oldPath, true);
            File.Delete(path);

            var configObject = new Dictionary<string, object>
                                   {
                                       {"GrindstonePath", config.GrindstonePath},
                                       {"PivotalEmail", config.Email},
                                       {"PivotalPassword", config.Password},
                                       {"AutoClose", config.AutoClose},
                                       {"AutoSubmit", config.AutoSubmit},
                                       {"ProfileName", config.ProfileName},
                                       {"ShowTasksFor", config.ShowTasksFor}
                                   };

            try
            {
                File.WriteAllText(path, JsonConvert.SerializeObject(configObject));
            }
            catch (Exception)
            {
                File.Copy(oldPath, path, true);
            }
            finally
            {
                File.Delete(oldPath);
            }
        }
        public bool LoadPrivateData()
        {
            string[] args = Environment.GetCommandLineArgs();
            foreach (var arg in args.Select(a => a.ToLower()))
            {
                if (arg.StartsWith("projectid=")) ProjectId = Convert.ToInt32(arg.Replace("projectid=", ""));
                if (arg.StartsWith("storyid=")) StoryId = Convert.ToInt32(arg.Replace("storyid=", ""));
            }
            if (ProjectId == 0 || StoryId == 0)
            {
                MessageBox.Show("ProjectId and StoryId are both required.");
                return false;
            }

            // Load the config
            Config config = new Config();
            try
            {
                config = PivotalUtils.GetConfig(baseDir + configFileName);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }

            // Get the password if it is not set
            if (string.IsNullOrEmpty(config.Password))
            {
                if (PasswordBox("Password Required", "PivotalTracker Password:"******"GrindstonPivotalLink unable to run without password.");
                        return false;
                    }
                }
                else
                {
                    MessageBox.Show("GrindstonPivotalLink unable to run without password.");
                    return false;
                }
            }

            try
            {
                token = PivotalUtils.GetPivotalTrackerUserToken(config.Email, config.Password);
            }
            catch (Exception)
            {
                MessageBox.Show("Unable to retrieve user token.");
                return false;
            }

            // Get story node and populate private story object
            XmlNode storyNode = new XmlDocument();
            try
            {
                storyNode = PivotalUtils.GetStoryNode(token, ProjectId, StoryId);
                story = PivotalUtils.GetStoryFromStoryNode(storyNode);
            }
            catch (Exception)
            {
                MessageBox.Show("Unable to retireve story data from PivotalTracker.");
                return false;
            }

            // Get comments from story node and build private comments string
            try
            {
                var listComments = PivotalUtils.GetCommentsFromStoryNode(storyNode);
                var sbComments = new StringBuilder();
                if (listComments.Count > 0)
                {
                    foreach (var comment in listComments)
                    {
                        sbComments.AppendLine(string.Concat(comment.timestamp.ToShortTimeString(), " - ", comment.author));
                        sbComments.AppendLine(comment.text);
                        sbComments.AppendLine();
                    }
                }
                else
                {
                    sbComments.AppendLine("No comments found.");
                }
                comments = sbComments.ToString();
            }
            catch (Exception)
            {
                comments = "Unable to retrieve story comments from PivotalTracker.";
            }

            // Set the form tasks
            try
            {
                tasks = PivotalUtils.GetStoryTasks(token, ProjectId, StoryId);
            }
            catch (Exception)
            {
                MessageBox.Show("Unable to retrieve story tasks from PivotalTracker.");
                return false;
            }

            // Set up the story owner
            try
            {
                var authenticityToken = string.Empty;
                var sessionCookie = PivotalUtils.GetPivotalTrackerSessionCookie(config.Email, config.Password, out authenticityToken);
                owners = PivotalUtils.GetUserIds(ref sessionCookie);
            }
            catch (Exception)
            {
                MessageBox.Show("Unable to retrieve potential story owners from PivotalTracker.");
                return false;
            }

            return true;
        }
        public static Config GetConfig(string path)
        {
            // Attempt to load the config file
            try
            {
                var config = new Config();

                // Check that the file exists
                if (!File.Exists(path))
                {
                    throw new Exception(String.Format("Unable to find the config file {0}.", path));
                }

                // Load the config file into the string builder
                var sbConfig = new StringBuilder();
                var srConfig = new StreamReader(path);
                while (!srConfig.EndOfStream)
                {
                    sbConfig.Append((srConfig.ReadLine() ?? string.Empty).Trim(' '));
                }
                srConfig.Close();

                // Deserialize the json string
                var jsonObject = (JObject)JsonConvert.DeserializeObject(sbConfig.ToString().Replace("\t", string.Empty));

                // Load the grindstone path
                if (jsonObject["GrindstonePath"] == null)
                {
                    throw new Exception("The 'GrindstonePath' in the config file appears to be missing.");
                }
                config.GrindstonePath = jsonObject["GrindstonePath"].ToString().Trim('"').Replace("\\\\", "\\");
                if (config.GrindstonePath == string.Empty)
                {
                    throw new Exception("The 'GrindstonePath' in the config file does not appear to be set.");
                }

                // Locate grindstone executable
                if (!File.Exists(config.GrindstonePath))
                {
                    throw new Exception(String.Concat("Unable to find grindstone (", config.GrindstonePath, ")."));
                }

                // Load the pivotal email
                if (jsonObject["PivotalEmail"] == null)
                {
                    throw new Exception("The 'PivotalEmail' in the config file appears to be missing.");
                }
                config.Email = jsonObject["PivotalEmail"].ToString().Trim('"');
                if (config.Email == string.Empty)
                {
                    throw new Exception("The 'PivotalEmail' in the config file does not appear to be set.");
                }

                // Load the pivotal password
                if (jsonObject["PivotalPassword"] == null)
                {
                    throw new Exception("The 'PivotalPassword' in the config file appears to be missing.");
                }
                config.Password = jsonObject["PivotalPassword"].ToString().Trim('"');

                // Load the AutoSubmit bool
                if (jsonObject["AutoSubmit"] == null)
                {
                    throw new Exception("The 'AutoSubmit' in the config file appears to be missing.");
                }
                if (!bool.TryParse(jsonObject["AutoSubmit"].ToString(), out config.AutoSubmit))
                {
                    throw new Exception("Unable to parse the 'AutoSubmit' field (boolean) in the config file.");
                }

                // Load the AutoSubmit bool
                if (jsonObject["AutoClose"] == null)
                {
                    throw new Exception("The 'AutoClose' in the config file appears to be missing.");
                }
                if (!bool.TryParse(jsonObject["AutoClose"].ToString(), out config.AutoClose))
                {
                    throw new Exception("Unable to parse the 'AutoClose' field (boolean) in the config file.");
                }

                // Load the ProfileName string
                if (jsonObject["ProfileName"] == null)
                {
                    throw new Exception("The 'ProfileName' in the config file appears to be missing.");
                }
                config.ProfileName = jsonObject["ProfileName"].ToString().Trim('"');

                // Load the ShowTasksFor string
                if (jsonObject["ShowTasksFor"] == null)
                {
                    throw new Exception("The 'ShowTasksFor' in the config file appears to be missing.");
                }
                config.ShowTasksFor = jsonObject["ShowTasksFor"].ToString().Trim('"');

                return config;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }