ilyalehchylin/educats-xamarin

View on GitHub
source/EduCATS/Pages/Eemc/ViewModels/EemcPageViewModel.cs

Summary

Maintainability
A
0 mins
Test Coverage
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using EduCATS.Data;
using EduCATS.Data.Models;
using EduCATS.Data.User;
using EduCATS.Demo;
using EduCATS.Helpers.Forms;
using EduCATS.Helpers.Logs;
using EduCATS.Networking;
using EduCATS.Pages.Pickers;
using Newtonsoft.Json;
using Nyxbull.Plugins.CrossLocalization;
using Xamarin.Forms;

namespace EduCATS.Pages.Eemc.ViewModels
{
    /// <summary>
    /// EEMC page view model.
    /// </summary>
    public class EemcPageViewModel : SubjectsViewModel
    {
        const string _filepathKey = "filepath";
        WebClient _client;
        object _progressDialog;
        /// <summary>
        /// Test identifier string.
        /// </summary>
        const string _testString = "test";

        /// <summary>
        /// Search ID.
        /// </summary>
        readonly int _searchId;

        /// <summary>
        /// Previous concepts.
        /// </summary>
        readonly Stack<ConceptModel> _previousConcepts;

        /// <summary>
        /// Root ID.
        /// </summary>
        int _rootId;

        /// <summary>
        /// Backup root concepts with children.
        /// </summary>
        ConceptModel _backupRootConceptsWithChildren;

        /// <summary>
        /// Backup root concepts without children.
        /// </summary>
        List<ConceptModel> _backupRootConceptsWithoutChildren;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="dialogs">App dialogs.</param>
        /// <param name="device">App device.</param>
        /// <param name="navigation">Pages navigation.</param>
        /// <param name="searchId">Search ID.</param>
        public EemcPageViewModel(IPlatformServices services, int searchId) : base(services)
        {
            IsRoot = true;
            _searchId = searchId;
            _previousConcepts = new Stack<ConceptModel>();

            Task.Run(async () => await update());
            SubjectChanged += async (id, name) => await update();
        }

        List<ConceptModel> _concepts;

        /// <summary>
        /// Concepts.
        /// </summary>
        public List<ConceptModel> Concepts {
            get { return _concepts; }
            set { SetProperty(ref _concepts, value); }
        }

        bool _isBackActionPossible;

        /// <summary>
        /// Is back action possible.
        /// </summary>
        public bool IsBackActionPossible {
            get { return _isBackActionPossible; }
            set { SetProperty(ref _isBackActionPossible, value); }
        }

        bool _isRoot;

        /// <summary>
        /// Is root directory.
        /// </summary>
        public bool IsRoot {
            get { return _isRoot; }
            set { SetProperty(ref _isRoot, value); }
        }

        object _selectedItem;

        /// <summary>
        /// Selected item.
        /// </summary>
        public object SelectedItem {
            get { return _selectedItem; }
            set {
                SetProperty(ref _selectedItem, value);
                Task.Run(async () => await openConcepts(_selectedItem));
            }
        }

        Command _backCommand;

        /// <summary>
        /// Back command.
        /// </summary>
        public Command BackCommand {
            get {
                return _backCommand ?? (_backCommand = new Command(goBack));
            }
        }

        /// <summary>
        /// Refresh data.
        /// </summary>
        /// <returns>Task.</returns>
        async Task update()
        {
            try {
                PlatformServices.Dialogs.ShowLoading();
                await SetupSubjects();
                await setRootConcepts();

                if (_searchId == -1 || _backupRootConceptsWithoutChildren == null) {
                    PlatformServices.Dialogs.HideLoading();
                    return;
                }

                await setConceptsFromRoot(_backupRootConceptsWithoutChildren[0].Id);
                searchForBook(_searchId);
                IsBackActionPossible = false;
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }

            PlatformServices.Dialogs.HideLoading();
        }

        /// <summary>
        /// Set root concepts.
        /// </summary>
        /// <returns>Task.</returns>
        async Task setRootConcepts()
        {
            var userId = AppUserData.UserId.ToString();
            var subjectId = CurrentSubject.Id.ToString();
            var root = await DataAccess.GetRootConcepts(userId, subjectId);

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

            var rootConcepts = root?.Concepts;

            if (rootConcepts != null) {
                _backupRootConceptsWithoutChildren = new List<ConceptModel>(rootConcepts);
                Concepts = new List<ConceptModel>(rootConcepts);
            }
        }

        /// <summary>
        /// Open concepts.
        /// </summary>
        /// <param name="selectedObject">Selected object.</param>
        /// <returns>Task.</returns>
        async Task openConcepts(object selectedObject)
        {
            try {
                if (selectedObject == null || !(selectedObject is ConceptModel)) {
                    return;
                }

                SelectedItem = null;
                var concept = selectedObject as ConceptModel;
                var id = concept.Id;

                if (IsRoot) {
                    _rootId = id;
                    PlatformServices.Dialogs.ShowLoading();
                    await setConceptsFromRoot(id);
                    PlatformServices.Dialogs.HideLoading();
                } else {
                    setOrOpenConcept(concept, id);
                }
            } catch (Exception ex) {
                AppLogs.Log(ex);
                PlatformServices.Dialogs.HideLoading();
            }
        }

        /// <summary>
        /// Search for book.
        /// </summary>
        /// <param name="id">Concept ID.</param>
        void searchForBook(int id)
        {
            if (Concepts == null) {
                return;
            }

            var concept = getConcept(Concepts, id);

            if (concept != null) {
                openConcept(concept);
            }
        }

        /// <summary>
        /// Get concept.
        /// </summary>
        /// <param name="concepts">Concepts.</param>
        /// <param name="id">Concept ID.</param>
        /// <returns>Concept.</returns>
        ConceptModel getConcept(List<ConceptModel> concepts, int id)
        {
            var item = concepts.FirstOrDefault(c => c.Id == id);

            if (item != null) {
                return item;
            }

            item = concepts.FirstOrDefault(c => {
                var concept = getConcept(c.Children, id);
                return concept != null;
            });

            return item;
        }

        /// <summary>
        /// Set concepts from root.
        /// </summary>
        /// <param name="id">Element ID.</param>
        /// <returns>Task.</returns>
        async Task setConceptsFromRoot(int id)
        {
            ConceptModel conceptTree = null;

            if (Servers.Current == Servers.EduCatsBntuAddress)
                conceptTree = await DataAccess.GetConceptTree(id);
            else
            {
                ConceptModelTest conceptCascade = await DataAccess.GetConceptCascade(id);
                conceptTree = JsonConvert.DeserializeObject<ConceptModel>(conceptCascade.Concept.ToString());
            }

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

            var concepts = conceptTree?.Children;

            if (concepts == null) {
                return;
            }

            IsRoot = false;
            _previousConcepts.Push(conceptTree);
            _backupRootConceptsWithChildren = conceptTree;
            Concepts = new List<ConceptModel>(concepts);
            IsBackActionPossible = true;
        }

        /// <summary>
        /// Set or open concept.
        /// </summary>
        /// <param name="selectedConcept">Selected object.</param>
        /// <param name="id">Concept ID.</param>
        void setOrOpenConcept(ConceptModel selectedConcept, int id)
        {
            if (Concepts == null) {
                return;
            }

            var concept = Concepts.FirstOrDefault(c => c.Id == id);

            if (concept == null) {
                return;
            }

            if (concept.HasData && !string.IsNullOrEmpty(concept.FilePath)) {
                openFile(concept.FilePath);
                return;
            }

            openConcept(concept, selectedConcept);
        }

        /// <summary>
        /// Open file.
        /// </summary>
        /// <param name="filePath">File path.</param>
        void openFile(string filePath)
        {
            if (AppDemo.Instance.IsDemoAccount) {
                PlatformServices.Device.MainThread(
                        () => PlatformServices.Dialogs.ShowError(
                            CrossLocalization.Translate("demo_eemc_open_error")));
                return;
            }

            if (Servers.Current == Servers.EduCatsBntuAddress)
                PlatformServices.Device.MainThread(
                    async () => await PlatformServices.Device.OpenUri($"{Servers.Current}/{filePath}"));
            else
            {
                PlatformServices.Device.MainThread(
                    async () => await PlatformServices.Device.OpenUri($"{Servers.Current}/api/Upload?fileName={filePath}"));
            }
        }

        /// <summary>
        /// Open test.
        /// </summary>
        /// <param name="id">Test ID.</param>
        void openTest(int id)
        {
            PlatformServices.Device.MainThread(
                async () => await PlatformServices.Navigation.OpenTestPassing(id, true));
        }

        /// <summary>
        /// Open concept.
        /// </summary>
        /// <param name="conceptToCheck">Concept to check.</param>
        /// <param name="conceptToPush">Concept to push.</param>
        void openConcept(ConceptModel conceptToCheck, ConceptModel conceptToPush = null)
        {
            if (conceptToCheck.IsGroup) {
                _previousConcepts.Push(conceptToPush ?? conceptToCheck);
                Concepts = new List<ConceptModel>(conceptToCheck.Children);
            } else if (conceptToCheck.Container.Equals(_testString)) {
                openTest(conceptToCheck.Id);
            }
        }

        /// <summary>
        /// Open previous directory.
        /// </summary>
        void goBack()
        {
            try {
                if (_previousConcepts.Count == 0) {
                    return;
                }

                var previousConcept = _previousConcepts.Pop();

                if (previousConcept.Id == _rootId &&
                    _backupRootConceptsWithoutChildren != null &&
                    _backupRootConceptsWithoutChildren.Count > 0) {
                    IsRoot = true;
                    IsBackActionPossible = false;
                    Concepts = new List<ConceptModel>(_backupRootConceptsWithoutChildren);
                    return;
                }

                if (_previousConcepts.Count > 0) {
                    var earlierConcept = _previousConcepts.Peek();
                    Concepts = new List<ConceptModel>(earlierConcept.Children);
                } else {
                    Concepts = new List<ConceptModel>(_backupRootConceptsWithChildren.Children);
                }
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }
    }
}