ilyalehchylin/educats-xamarin

View on GitHub
source/EduCATS/Pages/Today/Base/ViewModels/TodayPageViewModel.cs

Summary

Maintainability
C
1 day
Test Coverage
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EduCATS.Controls.RoundedListView;
using EduCATS.Data;
using EduCATS.Data.Models;
using EduCATS.Data.Models.Calendar;
using EduCATS.Helpers.Date;
using EduCATS.Helpers.Date.Enums;
using EduCATS.Helpers.Forms;
using EduCATS.Helpers.Logs;
using EduCATS.Pages.Today.Base.Models;
using EduCATS.Pages.Today.Base.Views;
using EduCATS.Themes;
using Nyxbull.Plugins.CrossLocalization;
using Xamarin.Forms;

namespace EduCATS.Pages.Today.Base.ViewModels
{
    public class TodayPageViewModel : ViewModel
    {
        readonly IPlatformServices _services;

        readonly double _subjectHeight;
        readonly double _subjectsFooterHeight;
        readonly double _subjectsHeaderHeight = 71;

        const int _minimumCalendarPosition = 0;
        const int _maximumCalendarPosition = 2;
        const double _subjectsHeightToAdd = 55;
        const double _emptySubjectsHeight = 110;

        // bool _isCreation = true;
        bool _isManualSelectedCalendarDay;
        DateTime _manualSelectedCalendarDay;
        List<CalendarSubjectsModel> _calendarSubjectsBackup;

        public TodayPageViewModel(double subjectHeight, double subjectsHeaderHeight, IPlatformServices services)
        {
            _subjectHeight = subjectHeight;
            _subjectsFooterHeight = subjectsHeaderHeight;
            _services = services;

            initSetup();
            update();
        }

        int _calendarPosition;
        public int CalendarPosition {
            get { return _calendarPosition; }
            set { SetProperty(ref _calendarPosition, value); }
        }

        ObservableCollection<CalendarViewModel> _calendarList;
        public ObservableCollection<CalendarViewModel> CalendarList {
            get { return _calendarList; }
            set { SetProperty(ref _calendarList, value); }
        }

        ObservableCollection<string> _calendarDaysOfWeekList;
        public ObservableCollection<string> CalendarDaysOfWeekList {
            get { return _calendarDaysOfWeekList; }
            set { SetProperty(ref _calendarDaysOfWeekList, value); }
        }

        List<NewsPageModel> _newsList;
        public List<NewsPageModel> NewsList {
            get { return _newsList; }
            set { SetProperty(ref _newsList, value); }
        }

        List<SubjectPageModel> _newsSubjectList;
        public List<SubjectPageModel> NewsSubjectList
        {
            get { return _newsSubjectList; }
            set { SetProperty(ref _newsSubjectList, value); }
        }

        List<CalendarSubjectsModel> _calendarSubjects;
        public List<CalendarSubjectsModel> CalendarSubjects {
            get { return _calendarSubjects; }
            set { SetProperty(ref _calendarSubjects, value); }
        }

        double _calendarSubjectsHeight;
        public double CalendarSubjectsHeight {
            get { return _calendarSubjectsHeight; }
            set { SetProperty(ref _calendarSubjectsHeight, value); }
        }

        string _month;
        public string Month {
            get { return _month; }
            set { SetProperty(ref _month, value); }
        }

        bool _isCalendarRefreshing;
        public bool IsCalendarRefreshing
        {
            get { return _isCalendarRefreshing; }
            set { SetProperty(ref _isCalendarRefreshing, value); }
        }

        bool _isNewsRefreshing;
        public bool IsNewsRefreshing {
            get { return _isNewsRefreshing; }
            set { SetProperty(ref _isNewsRefreshing, value); }
        }

        bool _isNewsRefreshed;
        public bool IsNewsRefreshed {
            get { return _isNewsRefreshed; }
            set { SetProperty(ref _isNewsRefreshed, value); }
        }

        object _selectedNewsItem;
        public object SelectedNewsItem {
            get { return _selectedNewsItem; }
            set {
                SetProperty(ref _selectedNewsItem, value);

                if (_selectedNewsItem != null) {
                    openDetailsPage(_selectedNewsItem);
                }
            }
        }

        Command _newsRefreshCommand;
        public Command NewsRefreshCommand {
            get {
                return _newsRefreshCommand ??
                    (_newsRefreshCommand = new Command(update));
            }
        }

        Command<CarouselView.FormsPlugin.Abstractions.PositionSelectedEventArgs> _calendarPositionChangedCommand;
        public Command<CarouselView.FormsPlugin.Abstractions.PositionSelectedEventArgs> PositionSelectedCommandProperty
        {
            get {
                return _calendarPositionChangedCommand ?? (
                    _calendarPositionChangedCommand = new Command<CarouselView.FormsPlugin.Abstractions.PositionSelectedEventArgs>(
                        calendarPositionChangedEvent));
            }
        }

        void initSetup()
        {
            try {
                _manualSelectedCalendarDay = new DateTime();
                CalendarSubjects = new List<CalendarSubjectsModel>();
                NewsSubjectList = new List<SubjectPageModel>();
                CalendarDaysOfWeekList = new ObservableCollection<string>(DateHelper.GetDaysWithFirstLetters());
                setInitialCalendarState();
                NewsList = new List<NewsPageModel>();
                _calendarSubjectsBackup = new List<CalendarSubjectsModel>();
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        void setInitialCalendarState()
        {
            var todayDateTime = DateTime.Today;
            var previousWeekCalendarModel = getCalendarViewModel(todayDateTime, WeekEnum.Previous);
            var currentWeekCalendarModel = getCalendarViewModel(todayDateTime, WeekEnum.Current);
            var nextWeekCalendarModel = getCalendarViewModel(todayDateTime, WeekEnum.Next);

            CalendarList = new ObservableCollection<CalendarViewModel> {
                previousWeekCalendarModel,
                currentWeekCalendarModel,
                nextWeekCalendarModel
            };
            CalendarPosition = 1;
            selectCalendarDay(todayDateTime);
        }
        void update()
        {
            _services.Device.MainThread(async () => {
                try {
                    IsNewsRefreshing = true;
                    await getAndSetCalendarNotes();
                    await getAndSetNews();
                    //_isCreation = false;
                    IsNewsRefreshing = false;
                } catch (Exception ex) {
                    AppLogs.Log(ex);
                }
            });
        }

        async Task getAndSetCalendarNotes()
        {
            if (_services.Preferences.Server == Networking.Servers.EduCatsBntuAddress)
            {
                var calendar = await DataAccess.GetProfileInfoCalendar(_services.Preferences.UserLogin);

                if (calendar == null) {
                    return;
                }

                var calendarList = new List<CalendarSubjectsModel>();
                var calendarLabsList = calendar.Labs?.Select(c => new CalendarSubjectsModel(c));
                var calendarLectsList = calendar.Lectures?.Select(c => new CalendarSubjectsModel(c));

                if (calendarLabsList == null && calendarLectsList == null) {
                    return;
                } else if (calendarLabsList == null && calendarLectsList != null) {
                    calendarList = new List<CalendarSubjectsModel>(calendarLectsList);
                } else if (calendarLabsList != null && calendarLectsList == null) {
                    calendarList = new List<CalendarSubjectsModel>(calendarLabsList);
                } else {
                    calendarList = calendarLabsList.Concat(calendarLectsList).ToList();
                }

                _calendarSubjectsBackup = new List<CalendarSubjectsModel>(calendarList);
                setFilteredSubjectsList();
            }
            else
            {
                DateTime date;
                if (_isManualSelectedCalendarDay)
                {
                    date = _manualSelectedCalendarDay;
                }
                else
                {
                    date = DateTime.Today;
                }

                await setNewSubjectList(date);
            }
        }

        async Task getAndSetNews()
        {
            var news = await getNews();

            if (news != null) {
                NewsList = new List<NewsPageModel>(news);
            }
        }

        async Task<List<NewsPageModel>> getNews()
        {
            var news = await DataAccess.GetNews(_services.Preferences.UserLogin);

            if (DataAccess.IsError && !DataAccess.IsConnectionError) {
                _services.Dialogs.ShowError(DataAccess.ErrorMessage);
            }

            var subjectList = await getSubjects();
            
            if (subjectList == null)
            {
                return composeNewsWithSubjects(news, null);
            }

            return composeNewsWithSubjects(news, subjectList.OrderBy(x => x.Name).ToList());
        }

        async Task<IList<SubjectModel>> getSubjects()
        {
            return await DataAccess.GetProfileInfoSubjects(_services.Preferences.UserLogin);
        }

        List<NewsPageModel> composeNewsWithSubjects(IList<NewsModel> news, IList<SubjectModel> subjects)
        {
            if (news == null || subjects == null) {
                return null;
            }

            return news.Select(n => {
                var subject = subjects.FirstOrDefault(s => s.Id == n.SubjectId);
                return new NewsPageModel(n, subject?.Color);
            }).ToList();
        }

        void openDetailsPage(object obj)
        {
            try {
                var newsPageModel = (NewsPageModel)obj;
                _services.Navigation.OpenNewsDetails(
                    newsPageModel.Title,
                    newsPageModel.Body);
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        CalendarViewModel getCalendarViewModel(DateTime date, WeekEnum week)
        {
            var weekStartDate = DateHelper.GetWeekStartDate(date, week);
            var weekDates = DateHelper.GetWeekDays(weekStartDate);

            var calendarDaysModelList = weekDates
                .Select(d => new CalendarViewDayModel {
                    TextColor = Theme.Current.TodayCalendarBaseTextColor,
                    Day = d.Day,
                    Month = d.Month,
                    Year = d.Year
                });

            return new CalendarViewModel(this) {
                Days = new ObservableCollection<CalendarViewDayModel>(calendarDaysModelList),
                Month = weekStartDate.Month,
                Year = weekStartDate.Year
            };
        }

        void selectTodayDateWithoutSelectedFlag()
        {
            selectDay(DateTime.Today, false);
        }

        void selectCalendarDay(DateTime dateToSelect)
        {
            selectDay(dateToSelect, true);
        }

        void deselectAllCalendarDays()
        {
            selectDay(deselect: true);
        }

        void selectDay(DateTime dateToCheck = default, bool selected = false, bool deselect = false)
        {
            if (CalendarList == null) {
                return;
            }

            foreach (var calendarModel in CalendarList) {
                var calendarDayModel = calendarModel.Days.FirstOrDefault(d => {
                    return deselect ?
                    d.Selected :
                    d.Date.ToShortDateString().Equals(dateToCheck.ToShortDateString());

                });

                if (calendarDayModel != null) {
                    changeCalendarSelection(calendarModel, calendarDayModel, selected);
                    break;
                }
            }
        }

        void changeCalendarSelection(
            CalendarViewModel calendarModel,
            CalendarViewDayModel calendarDayModel,
            bool selected)
        {
            try {
                var indexCalendarModel = CalendarList.IndexOf(calendarModel);
                var indexCalendarDayModel = calendarModel.Days.IndexOf(calendarDayModel);

                if (DateTime.Today == calendarDayModel.Date) {
                    calendarDayModel.SelectionColor = Theme.Current.TodaySelectedTodayDateColor;
                } else {
                    if (selected) {
                        calendarDayModel.SelectionColor = Theme.Current.TodaySelectedAnotherDateColor;
                    } else {
                        calendarDayModel.SelectionColor = Theme.Current.TodayNotSelectedDateColor;
                    }
                }

                calendarDayModel.Selected = selected;

                if (selected) {
                    calendarDayModel.TextColor = Theme.Current.TodaySelectedDateTextColor;
                } else {
                    calendarDayModel.TextColor = Theme.Current.TodayNotSelectedDateTextColor;
                }

                var temp = CalendarViewModel.Clone(CalendarList[indexCalendarModel], this);
                temp.Days[indexCalendarDayModel] = calendarDayModel;

                CalendarList[indexCalendarModel] = temp;

            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        protected void calendarPositionChangedEvent(CarouselView.FormsPlugin.Abstractions.PositionSelectedEventArgs e)
        {
            try {
                selectTodayDateWithoutSelectedFlag();
                deselectAllCalendarDays();

                if (_isManualSelectedCalendarDay) {
                    selectCalendarDay(_manualSelectedCalendarDay);
                } else {
                    selectCalendarDay(DateTime.Today);
                }

                switch (CalendarPosition) {
                    case _minimumCalendarPosition:
                        getCalendarPosition(_minimumCalendarPosition, WeekEnum.Previous);
                        break;
                    case _maximumCalendarPosition:
                        getCalendarPosition(_maximumCalendarPosition, WeekEnum.Next);
                        break;
                }
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }
        int getCalendarPosition(int boundaryPosition, WeekEnum week)
        {
            int removePosition;
            int calculatedPosition;

            if (boundaryPosition == _maximumCalendarPosition) {
                removePosition = _minimumCalendarPosition;
                calculatedPosition = _maximumCalendarPosition - 1;
            } else {
                removePosition = _maximumCalendarPosition;
                calculatedPosition = _minimumCalendarPosition + 1;
            }

            var date = CalendarList[boundaryPosition].Date;
            var weekViewModel = getCalendarViewModel(date, week);

            CalendarList.RemoveAt(removePosition);
            CalendarList.Insert(boundaryPosition, weekViewModel);
            
            return calculatedPosition;
        }

        public void ExecuteCalendarSelectionChangedEvent(DateTime date)
        {
            try {
                selectTodayDateWithoutSelectedFlag();
                deselectAllCalendarDays();
                _manualSelectedCalendarDay = date;
                _isManualSelectedCalendarDay = true;
                selectCalendarDay(date);
            
                if (_services.Preferences.Server == Networking.Servers.EduCatsBntuAddress)
                {
                    setFilteredSubjectsList();
                }
                else
                {
                    _services.Device.MainThread(async () => {
                        await setNewSubjectList(date);
                    });
                }

            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        async Task setNewSubjectList(DateTime dateTime)
        {
            var subjects = await DataAccess.GetSchedule(dateTime.ToString(DateHelper.DateTime.Replace('.','-')));
            
            List<SubjectPageModel> temp = subjects.Schedule.Select(n => {
                return new SubjectPageModel(n);
            }).ToList().OrderBy(l => l.Date).ToList();

            NewsSubjectList = temp.OrderBy(x => x.Date).ToList();
            setupNewsSubjectsHeight();
        }


        void setFilteredSubjectsList()
        {
            var filteredList = new List<CalendarSubjectsModel>();

            if (_isManualSelectedCalendarDay) {
                filteredList = _calendarSubjectsBackup.Where(
                    x => x.Date.ToShortDateString() == _manualSelectedCalendarDay.ToShortDateString()).ToList();
            } else {
                filteredList = _calendarSubjectsBackup.Where(
                    x => x.Date.ToShortDateString() == DateTime.Today.ToShortDateString()).ToList();
            }

            CalendarSubjects = new List<CalendarSubjectsModel>(filteredList);
            setupSubjectsHeight();
        }

        void setupSubjectsHeight()
        {
            try {
                if (CalendarSubjects.Count == 0) {
                    CalendarSubjectsHeight = _emptySubjectsHeight;
                    return;
                }

                CalendarSubjectsHeight =
                    (_subjectHeight * CalendarSubjects.Count) +
                    (_subjectsHeaderHeight * 2) + _subjectsHeightToAdd;
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        void setupNewsSubjectsHeight()
        {
            try
            {
                if (NewsSubjectList.Count == 0)
                {
                    CalendarSubjectsHeight = _emptySubjectsHeight;
                    return;
                }

                CalendarSubjectsHeight =
                    (_subjectHeight * (NewsSubjectList.Count) /** 2*/) +
                    _subjectsHeaderHeight + _subjectsFooterHeight + _subjectsHeightToAdd;
            }
            catch (Exception ex)
            {
                AppLogs.Log(ex);
            }
        }
    }
}