Calendar.cs 13.2 KB
Newer Older
Stojcheska Teodora's avatar
Stojcheska Teodora committed
1
2
using System;
using System.Collections.Generic;
3
using System.Linq;
Stojcheska Teodora's avatar
Stojcheska Teodora committed
4
5
6
7

[Serializable]
public class Calendar
{
Stojcheska Teodora's avatar
Stojcheska Teodora committed
8
9
10
11
    public List<Day> Days { get; set; }
    public DateTime CurrentDate { get; set; }
    public (int, int) DefaultWorkingHoursInterval { get; set; }
    public int DefaultWorkingHours { get; set; }
Stojcheska Teodora's avatar
Stojcheska Teodora committed
12

Stojcheska Teodora's avatar
Changes    
Stojcheska Teodora committed
13
14
    public delegate void ErrorNotify();

Stojcheska Teodora's avatar
Stojcheska Teodora committed
15
16
    public Calendar()
    {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
17
        this.Days = new List<Day>();
18
19
        this.DefaultWorkingHours = 8;
        this.DefaultWorkingHoursInterval = (9, 17);
20
        Days.Add(new Day(DateTime.Now, new List<Task>(), DefaultWorkingHoursInterval));
Stojcheska Teodora's avatar
Stojcheska Teodora committed
21
        this.CurrentDate = DateTime.Now; // format dd.mm.yyyy hh:mm:ss
Stojcheska Teodora's avatar
Stojcheska Teodora committed
22
23
    }

24
    public Calendar(List<Day> days, (int, int) workingHoursInterval, int workingHours)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
25
    {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
26
        this.Days = days;
27
28
        this.DefaultWorkingHoursInterval = workingHoursInterval;
        this.DefaultWorkingHours = workingHours;
Stojcheska Teodora's avatar
Stojcheska Teodora committed
29
        this.CurrentDate = DateTime.Now; // format dd.mm.yyyy hh:mm:ss
30
        addDaysUpToDate(DateTime.Now);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
31
32
    }

33
    private static int numberOfDaysInRange(DateTime startingDate, DateTime endDate) => (endDate.Date - startingDate.Date).Days + 1;
34

35
    public static bool isDateValid(DateTime startingDate, DateTime endDate) => numberOfDaysInRange(startingDate, endDate) > 0;
Stojcheska Teodora's avatar
Stojcheska Teodora committed
36

37
    public Day getDayByDate(DateTime date)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
38
    {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
39
        foreach (Day day in Days)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
40
        {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
41
            if (day.Date.Date == date.Date)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
42
43
44
45
            {
                return day;
            }
        }
Stojcheska Teodora's avatar
Changes    
Stojcheska Teodora committed
46
47
48
        return null;
    }

49
    private int shouldAddDay(int hours)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
50
51
52
53
54
55
56
    {
        Day day = Days[Days.Count - 1];
        while (day != null && !day.isDayFull(0) && isDateValid(DateTime.Now, day.Date))
        {
            hours += day.hoursToShift;
            day = day.PrevDay;
        }
57
        return hours;
Stojcheska Teodora's avatar
Stojcheska Teodora committed
58
59
    }

60
    public void addDaysUpToDate(DateTime date)
Stojcheska Teodora's avatar
Changes    
Stojcheska Teodora committed
61
    {
62
63
        int daysToAdd = numberOfDaysInRange(Days[Days.Count - 1].Date, date);
        for (int i = 0; i < daysToAdd; ++i)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
64
        {
65
            Day newDay = new Day(Days[Days.Count - 1].Date.AddDays(1), new List<Task>(), DefaultWorkingHoursInterval);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
66
67
68
            newDay.PrevDay = Days[Days.Count - 1];
            Days[Days.Count - 1].NextDay = newDay;
            Days.Add(newDay);
69
70
71
        }
    }

Stojcheska Teodora's avatar
Stojcheska Teodora committed
72
73
74
75
    public List<Day> getRangeOfDaysForTask(Task task)
    {
        List<Day> validDays = new List<Day>();

76
        int shouldAddDayHours = shouldAddDay(task.Duration);
77
        if (shouldAddDayHours > 0)
78
        {
79
            addDaysUpToDate(Days[Days.Count - 1].Date.AddDays((int)Math.Ceiling((double)shouldAddDayHours / DefaultWorkingHours)));
Stojcheska Teodora's avatar
Stojcheska Teodora committed
80
81
        }

Stojcheska Teodora's avatar
Stojcheska Teodora committed
82
83
        int numberOfValidDaysInRange = numberOfDaysInRange(CurrentDate, task.Deadline);
        int startIndex = numberOfDaysInRange(Days[0].Date, CurrentDate) - 1;
84

Stojcheska Teodora's avatar
Stojcheska Teodora committed
85
86
        for (int i = 0; i < numberOfValidDaysInRange; ++i)
        {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
87
            if (Days.Count - 1 < startIndex + i)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
88
89
90
            {
                break;
            }
91

Stojcheska Teodora's avatar
Stojcheska Teodora committed
92
            validDays.Add(Days[startIndex + i]);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
93
94
95
96
97
        }

        return validDays;
    }

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
    public bool doesTaskExist(Task t)
    {
        foreach(Day day in Days)
        {
            foreach(Task task in day.Tasks)
            {
                if (t.Equals(task))
                {
                    return true;
                }
            }
        }
        return false;
    }

113
114
115
116
117
118
119
120
121
122
123
124
    public event ErrorNotify ErrorInTaskParameters;

    /* ----------------------------- Utility for add and delete task ----------------------------- */

    public bool checkForCondition(int i, int end, Direction dir) => dir == Direction.NEXT ? i >= end : i < end;

    public void reorderCalendar(Day day, int hoursToShift, Day returnPoint, Direction dir)
    {
        int hours = hoursToShift;
        Day dirDay = dir == Direction.NEXT ? day.NextDay : day.PrevDay;

        if (dirDay == null || (dirDay.Equals(returnPoint) && dir == Direction.PREVIOUS) || (day.hoursToShift <= 0 && dir == Direction.NEXT)) { return; }
125
126
        
        if (numberOfDaysInRange(DateTime.Now, dirDay.Date) <= 0) { return; }
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

        List<Task> tasks = new List<Task>();

        for (int i = dir == Direction.NEXT ? day.Tasks.Count - 1 : 0; checkForCondition(i, (dir == Direction.NEXT ? 0 : day.Tasks.Count), dir); i += (int)dir)
        {
            if (day.Tasks[i].Type != Type.FIXED)
            {
                if (hours == 0) { break; }

                Task curTask = day.Tasks[i];
                Task curTaskDir = curTask.getTaskByDirection(dir);

                if (!isDateValid(dirDay.Date, curTask.Deadline))
                {
                    for (int q = tasks.Count - 1; q >= 0; --q) {
                        day.addTask(tasks[q], day.Tasks.Count); 
                    }
                    throw new NoSpaceForTaskException("There is no space to add the Task, error occured during shifting the Tasks", day, curTask, hours);
                }

                if (curTask.Duration > hours)
                {
                    int additionalHours = 0;

                    if (curTaskDir != null)
                    {
                        additionalHours = curTaskDir.Duration;
                        dirDay.removeTask(curTaskDir);
                        curTask.mergeTasks(curTask, curTaskDir, dir);
                    }

                    int[] splitHours = { curTask.Duration - hours - additionalHours, hours + additionalHours };
                    curTask.splitTask(splitHours, 0, curTask, day, dir);
                    hours = 0;
                    tasks.Add(curTask.getTaskByDirection(dir));
                }
                else
                {
                    int curTaskHours = curTask.Duration;
                    if (curTaskDir != null)
                    {
                        dirDay.removeTask(curTaskDir);
                        if (dir == Direction.NEXT)
                        {
                            curTask.mergeTasks(curTask, curTaskDir, dir);
                            tasks.Add(curTask);
                        }
                        else
                        {
                            curTaskDir.mergeTasks(curTaskDir, curTask, dir);
                            tasks.Add(curTaskDir);
                        }
                    }
                    else
                    {
                        tasks.Add(curTask);
                    }
                    hours -= curTaskHours;
                    day.removeTask(curTask);

                    if (dir == Direction.PREVIOUS) { i--; }
                }
            }
        }

        foreach (Task task in tasks) { dirDay.addTask(task, dir == Direction.NEXT ? 0 : dirDay.Tasks.Count); }

        reorderCalendar(dirDay, dir == Direction.NEXT ? dirDay.hoursToShift < 0 ? hoursToShift : dirDay.hoursToShift : hoursToShift, returnPoint, dir);
    }
Stojcheska Teodora's avatar
Changes    
Stojcheska Teodora committed
196

197
198
    /* ------------------------------- Add Task --------------------------------- */

199
200
201
202
    public void addTask(Task task)
    {
        try
        {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
203
            if(task.Type == Type.NORMAL)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
204
205
206
            {
                _addTask(task);
            }
Stojcheska Teodora's avatar
Stojcheska Teodora committed
207
            else if(task.Type == Type.FIXED)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
208
            {
209
                _addFixedTask(task);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
210
            }
211
        }
212
        catch (NoSpaceForTaskException exception)
213
        {
214
215
216
217
218
            if (task.Type == Type.FIXED)
            {
                exception.Day.removeTask(task);
            }
            else if (task.Type == Type.NORMAL && exception.Day != null)
219
            {
220
                Day day = exception.Day;
221
222
                deleteTask(day, exception.Task);

223
224
225
226
                if (day.hoursToShift > 0)
                {
                    reorderCalendar(day, day.hoursToShift, null, Direction.NEXT);
                }
227
228
            }
            ErrorInTaskParameters?.Invoke();
229
        }
Stojcheska Teodora's avatar
Stojcheska Teodora committed
230
231
    }

232
    public void _addFixedTask(Task task)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
233
    {
234
        int shouldAddDayHours = shouldAddDay(task.Duration);
235
236
        if (shouldAddDayHours > 0)
        {
237
            addDaysUpToDate(Days[Days.Count - 1].Date.AddDays((int)Math.Ceiling((double)shouldAddDayHours / DefaultWorkingHours)));
Stojcheska Teodora's avatar
Stojcheska Teodora committed
238
        }
239
        if(numberOfDaysInRange(task.Deadline, Days[Days.Count - 1].Date) <= 0)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
240
        {
241
            addDaysUpToDate(task.Deadline);
242
243
244
245
246
247
248
        }

        Day day = getDayByDate(task.Deadline);
        day.addTask(task, 0);
        if (day.isDayFull(0))
        {
            reorderCalendar(day, day.hoursToShift, null, Direction.NEXT);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
249
250
251
        }
    }

252
    public void _addTask(Task task)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
253
    {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
254
        List<Day> validDays;
255
        validDays = getRangeOfDaysForTask(task);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
256
257
258

        foreach (Day day in validDays)
        {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
259
            for (int i = 0; i < day.Tasks.Count; ++i)
260
            {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
261
                if (numberOfDaysInRange(day.Tasks[i].Deadline, task.Deadline) <= 0 && day.Tasks[i].Type != Type.FIXED)
262
                {
Stojcheska Teodora's avatar
Stojcheska Teodora committed
263
                    day.addTask(task, i);
264
265
                    if (day.isDayFull(0))
                    {
266
                        reorderCalendar(day, day.hoursToShift, null, Direction.NEXT);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
267
268
269
                    }
                    return;
                }
270
            }
Stojcheska Teodora's avatar
Stojcheska Teodora committed
271
272
273
274
275
            if (!day.isDayFull(0))
            {
                day.addTask(task, day.Tasks.Count);
                if (day.isDayFull(0))
                {
276
                    reorderCalendar(day, day.hoursToShift, null, Direction.NEXT);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
277
278
279
                }
                return;
            }
Stojcheska Teodora's avatar
Stojcheska Teodora committed
280
        }
281
        throw new NoSpaceForTaskException("There is no space to add the Task", null, null, 0);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
282
283
    }

284
285
    /* ------------------------------- Delete Task ----------------------------------- */
    public void deleteTask(Day day, Task task)
286
    {
287
        while (task.NextSplitTaskPtr != null) 
288
        {
289
290
            task = task.NextSplitTaskPtr;
            day = day.NextDay;
291
        }
292

293
        while (task != null)
Stojcheska Teodora's avatar
Changes    
Stojcheska Teodora committed
294
        {
295
296
            day.removeTask(task);
            task.NextSplitTaskPtr = null;
Stojcheska Teodora's avatar
Changes    
Stojcheska Teodora committed
297

298
            reorderCalendar(Days[Days.Count - 1], task.Duration, day.PrevDay, Direction.PREVIOUS);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
299

300
301
            task = task.PrevSplitTaskPtr;
            day = day.PrevDay;
Stojcheska Teodora's avatar
Stojcheska Teodora committed
302
303
304
        }
    }

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
    /* ------------------------------ Modify Task ---------------------------------- */
    public void modifyTask(Task task, DateTime newDeadline, String newName)
    {
        Task t = task;
        while (t != null)
        {
            t.Name = newName;
            t.Deadline = newDeadline;
        }

        t = task.NextSplitTaskPtr;
        while (t != null)
        {
            t.Name = newName;
            t.Deadline = newDeadline;
        }
    }

Stojcheska Teodora's avatar
Stojcheska Teodora committed
323
    /* --------------------------- Change Working Hours ------------------------------- */
324
    public bool changeWorkingHours(DateTime date, (int, int) previousWorkingHoursInterval)
Stojcheska Teodora's avatar
Stojcheska Teodora committed
325
326
    {
        Day day = getDayByDate(date);
327
        int previousWorkingHours = previousWorkingHoursInterval.Item2 - previousWorkingHoursInterval.Item1;
Stojcheska Teodora's avatar
Stojcheska Teodora committed
328
329
        if(previousWorkingHours - day.WorkingHours < 0)
        {
330
            reorderCalendar(Days[Days.Count - 1], Math.Abs(previousWorkingHours - day.WorkingHours), day, Direction.PREVIOUS);
Stojcheska Teodora's avatar
Stojcheska Teodora committed
331
332
333
        }
        else if(previousWorkingHours - day.WorkingHours > 0)
        {
334
            int hoursNeeded = shouldAddDay(day.hoursToShift);
335
            if (hoursNeeded > 0)
336
            {
337
                int daysNeeded = (int)Math.Ceiling((double)hoursNeeded / day.WorkingHours);
338
                addDaysUpToDate(Days[Days.Count - 1].Date.AddDays(daysNeeded));
339
            }
340
341

            int shiftHours = day.hoursToShift;
342
343
            try
            {
344
                reorderCalendar(day, shiftHours, null, Direction.NEXT);
345
            }
346
            catch(NoSpaceForTaskException exception)
347
            {
348
                int tryWorkingHours = day.WorkingHours;
349
                day.WorkingHoursInterval = previousWorkingHoursInterval;
350

351
352
                if (exception.Day != null)
                {
353
354
355
356
                    if (exception.Day.hoursToShift - (previousWorkingHours - tryWorkingHours) > 0)
                    {
                        reorderCalendar(exception.Day, exception.Day.hoursToShift - (previousWorkingHours - tryWorkingHours), null, Direction.NEXT);
                    }
357
                    reorderCalendar(exception.Day, shiftHours, day.PrevDay, Direction.PREVIOUS);
358
                }
359
                
360
                ErrorInTaskParameters?.Invoke();
361
                return false;
362
363
            }
        }
364
        return true;
365
366
    }

367
    public bool changeDefaultWorkingHours((int, int) workingHoursInterval)
368
369
370
371
372
373
374
375
    {
        this.DefaultWorkingHours = workingHoursInterval.Item2 - workingHoursInterval.Item1;
        this.DefaultWorkingHoursInterval = workingHoursInterval;

        Day day = getDayByDate(DateTime.Now);

        while (day != null)
        {
376
            (int, int) previousWorkingHoursInterval = day.WorkingHoursInterval;
377
378
            day.WorkingHoursInterval = this.DefaultWorkingHoursInterval;

379
380
381
382
383
384
385
386
387
388
            if(!changeWorkingHours(day.Date, previousWorkingHoursInterval))
            {
                while(day != null && numberOfDaysInRange(DateTime.Now, day.Date) > 0)
                {
                    day.WorkingHoursInterval = previousWorkingHoursInterval;
                    reorderCalendar(Days[Days.Count - 1], day.hoursToShift * -1, day.PrevDay, Direction.PREVIOUS);
                    day = day.PrevDay;
                }
                return false;
            }
389
            day = day.NextDay;
Stojcheska Teodora's avatar
Stojcheska Teodora committed
390
        }
391
        return true;
392
    }
Stojcheska Teodora's avatar
Stojcheska Teodora committed
393
}