private static ProjectData ResetTaskResourceNamesOrInitials(ProjectData project, TaskId id, bool isInitials)
        {
            var resourceField = isInitials ? ResourceFields.Initials : ResourceFields.Name;
            var taskField     = isInitials ? TaskFields.ResourceInitials : TaskFields.ResourceNames;

            var sb = new StringBuilder();

            foreach (var a in project.GetAssignments(id)
                     .OrderBy(a => project.Get(resourceField, project.Get(AssignmentFields.ResourceId, a))))
            {
                var resourceId   = project.Get(AssignmentFields.ResourceId, a);
                var resourceName = project.Get(resourceField, resourceId);
                var units        = project.Get(AssignmentFields.Units, a);

                if (sb.Length > 0)
                {
                    sb.Append(", ");
                }

                sb.Append(resourceName);

                if (units != 1.0)
                {
                    sb.Append(" [");
                    sb.Append(Math.Round(units * 100, 2, MidpointRounding.AwayFromZero));
                    sb.Append("%]");
                }
            }

            var resourceNames = sb.ToString();

            return(project.SetRaw(taskField, id, resourceNames));
        }
        private static void ComputeStart(ProjectData project, TaskId taskId, out DateTimeOffset start, ref DateTimeOffset finish)
        {
            var assignmentIds = project.GetAssignments(taskId);

            if (!assignmentIds.Any())
            {
                var work = GetWork(project, taskId);
                ComputeStart(project.Information.Calendar, out start, ref finish, work);
            }
            else
            {
                start = DateTimeOffset.MaxValue;

                foreach (var assignmentId in assignmentIds)
                {
                    var assignmentWork  = project.Get(AssignmentFields.Work, assignmentId);
                    var assignmentUnits = project.Get(AssignmentFields.Units, assignmentId);

                    var duration = TimeSpan.FromHours(assignmentWork.TotalHours / assignmentUnits);
                    ComputeStart(project.Information.Calendar, out var assignmentStart, ref finish, duration);

                    if (assignmentStart < start)
                    {
                        start = assignmentStart;
                    }
                }
            }
        }
        private static ProjectData SetTaskWork(ProjectData project, TaskId id, TimeSpan value)
        {
            project = project.SetRaw(TaskFields.Work, id, value);

            if (value == TimeSpan.Zero)
            {
                foreach (var assignmentId in project.GetAssignments(id))
                {
                    project = project.SetRaw(AssignmentFields.Work, assignmentId, TimeSpan.Zero);
                }
            }
            else
            {
                var totalExistingWork = TimeSpan.Zero;

                foreach (var assignmentId in project.GetAssignments(id))
                {
                    totalExistingWork += project.Get(AssignmentFields.Work, assignmentId);
                }

                var assignmentCount = project.GetAssignments(id).Count();

                foreach (var assignmentId in project.GetAssignments(id))
                {
                    var assignmentWork = project.Get(AssignmentFields.Work, assignmentId);

                    double newHours;

                    if (totalExistingWork > TimeSpan.Zero)
                    {
                        newHours = assignmentWork.TotalHours / totalExistingWork.TotalHours * value.TotalHours;
                    }
                    else
                    {
                        newHours = value.TotalHours / assignmentCount;
                    }

                    var newWork = TimeSpan.FromHours(newHours);

                    project = project.SetRaw(AssignmentFields.Work, assignmentId, newWork);
                }
            }

            return(project);
        }
        private static TimeSpan GetWork(ProjectData project, TaskId taskId)
        {
            var hasAssignments = project.GetAssignments(taskId).Any();

            if (!hasAssignments)
            {
                return(project.Get(TaskFields.Duration, taskId));
            }

            return(project.Get(TaskFields.Work, taskId));
        }
        private static ProjectData SetTaskName(ProjectData project, TaskId id, string value)
        {
            var assignmentIds = project.GetAssignments(id);

            project = project.SetRaw(TaskFields.Name, id, value);

            foreach (var assignmentId in assignmentIds)
            {
                project = project.Set(AssignmentFields.TaskName, assignmentId, value);
            }

            return(project);
        }
        private static ProjectData SetResourceName(ProjectData project, ResourceId id, string value)
        {
            project = project.SetRaw(ResourceFields.Name, id, value);

            // Update Assignment.ResourceName

            foreach (var assignmentId in project.GetAssignments(id))
            {
                project = project.SetRaw(AssignmentFields.ResourceName, assignmentId, value);
            }

            // Update Task.ResourceNames

            foreach (var taskId in project.GetTasks(id))
            {
                project = project.Reset(TaskFields.ResourceNames, taskId);
            }

            return(project);
        }
        private static ProjectData SetTaskDuration(ProjectData project, TaskId id, TimeSpan value)
        {
            project = project.SetRaw(TaskFields.Duration, id, value);

            var taskFinish = project.Get(TaskFields.Finish, id);

            foreach (var assignmentId in project.GetAssignments(id))
            {
                var assignmentFinish = project.Get(AssignmentFields.Finish, assignmentId);
                var assignmentUnits  = project.Get(AssignmentFields.Units, assignmentId);

                if (assignmentFinish == taskFinish)
                {
                    var assignmentWork = TimeSpan.FromHours(value.TotalHours * assignmentUnits);
                    project = project.Set(AssignmentFields.Work, assignmentId, assignmentWork);
                }
            }

            return(project);
        }
        private static ProjectData SetTaskResourceNamesOrInitials(ProjectData project, TaskId id, string value, bool isInitials)
        {
            var field = isInitials ? ResourceFields.Initials : ResourceFields.Name;

            value = value.Trim();

            var remainingAssignmentIds = project.GetAssignments(id).ToList();

            if (value.Length > 0)
            {
                var resourceParts = value.Split(',');

                foreach (var resourcePart in resourceParts)
                {
                    var initials    = resourcePart.Trim();
                    var units       = 1.0;
                    var openBracket = initials.IndexOf("[");
                    if (openBracket >= 0)
                    {
                        var closeBracket = initials.IndexOf("]");

                        if (closeBracket < openBracket)
                        {
                            throw new FormatException("Missing ']'");
                        }

                        var percentageText = initials.Substring(openBracket + 1, closeBracket - openBracket - 1).Trim();

                        if (percentageText.EndsWith("%"))
                        {
                            percentageText = percentageText.Substring(0, percentageText.Length - 1).Trim();
                        }

                        if (!double.TryParse(percentageText, out var percentage))
                        {
                            throw new FormatException($"'{percentageText}' isn't a valid percentage");
                        }

                        initials = initials.Substring(0, openBracket).Trim();
                        units    = percentage / 100.0;
                    }

                    var resourceId = project.GetResources(initials, isInitials).FirstOrDefault();
                    if (resourceId.IsDefault)
                    {
                        resourceId = ResourceId.Create();
                        project    = project.AddResource(resourceId).Set(field, resourceId, initials);
                    }

                    var assignmentId = project.GetAssignment(id, resourceId);
                    if (assignmentId.IsDefault)
                    {
                        assignmentId = AssignmentId.Create();
                        project      = project.AddAssignment(assignmentId, id, resourceId);
                    }

                    project = project.Set(AssignmentFields.Units, assignmentId, units);
                    remainingAssignmentIds.Remove(assignmentId);
                }
            }

            foreach (var assignmentId in remainingAssignmentIds)
            {
                project = project.RemoveAssignment(assignmentId);
            }

            return(project);
        }