context: using System.Linq;
  using BMIRussianTV.Models;
  using BMIRussianTV.ViewModels.Categories;
  using System;
  using BMIRussianTV.Utils;
  using BMIRussianTV.ViewModels.Base;
  using System.Collections.Generic;
  using Microsoft.AspNetCore.Authorization;
  using Microsoft.AspNetCore.Mvc;
  using Microsoft.EntityFrameworkCore;
  
  namespace BMIRussianTV.Controllers
  {
      [Authorize(Roles = "Editor")]
      public class CategoriesController : Controller
      {
          private ApplicationDbContext _context;
  
          public CategoriesController(ApplicationDbContext context)
          {
              _context = context;    
          }
  
          // GET: Categries/Advise
          public AdviseViewModel<dynamic> Advise(string input, int page)
          {
              var categories = (from category in _context.Category
                                where category.Title.Contains(input.ToUpper())
                                select new
                                {
                                    id = category.Title,
                                    Name = category.Title,
                                    Status = "db"
                                }).ToList();
              if (!categories.Any(b => b.Name.ToUpper() == input.ToUpper()))
              {
                  categories.Insert(0, new
                  {
                      id = input,
                      Name = input,
                      Status = "new"
                  });
              }
              return AdviseViewModelFactory.Create(categories, page, GeneralSettings.ItemsPerQueryPage);
          }
  
          // GET: Shows
          [AllowAnonymous]
          [Route("Shows")]
          public IActionResult Index()
          {
              return View(_context.Category
                  .OrderByDescending(c => c.Priority)
                  .GroupBy(c => c.Group)
                  .ToList());
          }
  
          // GET: ShowNotFound
          [AllowAnonymous]
          [Route("ShowNotFound")]
          public IActionResult ShowNotFound()
          {
              return View(_context.Category.ToList());
          }
  
          const int LIMIT_NEWEST_RECORDS = 4;
  
          // GET: Show/5?Page=5
          [AllowAnonymous]
          [Route("Show/{id}")]
          public IActionResult Details(string id, int page)
          {
              if (id == null)
              {
                  return RedirectToAction("ShowNotFound");
              }
              var category = _context.Category.SingleOrDefault(c => c.CategorySID == id.ToUpper());
              if (category == null)
              {
                  category = _context.Category.SingleOrDefault(c => c.CategoryOSID == id.ToUpper());
                  if (category == null)
                  {
                      return RedirectToAction("ShowNotFound");
                  }
              }
  
              var videos = (from video in _context.Video.Include(v => v.Category)
                            where video.CategoryID == category.CategoryID && video.Status == VideoStatus.Published
                            orderby video.PublishDate descending
                            select video);
              var vm = new CategoryViewModel()
              {
                  CategoryID = category.CategoryID,
                  CategorySID = category.CategorySID,
                  Title = category.Title,
                  OriginalTitle = category.OriginalTitle,
                  CurrentPage = Math.Max(1, page),
                  TotalCount = videos.Count()
              };
              ViewData["Keywords"] = ContextUtil.GetKeywords(vm);
              vm.Videos = videos.Skip(vm.IndexOfFirstItem).Take(vm.ItemsOnPage).ToList();
  
              return View(vm);
          }
  
          // GET: Categories/Edit/5
          public IActionResult Edit(int? id)
          {
              if (id == null)
              {
                  return ShowNotFound();
              }
  
              Category category = _context.Category.Single(m => m.CategoryID == id);
              if (category == null)
              {
                  return ShowNotFound();
              }
              return View(category);
          }
  
          // POST: Categories/Edit/5
          [HttpPost]
          [ValidateAntiForgeryToken]
          public IActionResult Edit(Category category)
          {
              if (ModelState.IsValid)
              {
                  _context.Update(category.UpdateSID(_context));
                  _context.SaveChanges();
                  return RedirectToAction("Index");
              }
              return View(category);
          }
          
          // GET: Categories/Delete/5
          [ActionName("Delete")]
          public IActionResult Delete(int? id)
          {
              if (id == null)
              {
                  return ShowNotFound();
              }
  
              Category category = _context.Category.Single(m => m.CategoryID == id);
              if (category == null)
              {
                  return ShowNotFound();
              }
  
              return View(category);
          }
  
          // POST: Categories/Delete/5
          [HttpPost, ActionName("Delete")]
          [ValidateAntiForgeryToken]
          public IActionResult DeleteConfirmed(int id)
          {
              Category category = _context.Category.Single(m => m.CategoryID == id);
              _context.Category.Remove(category);
              _context.SaveChanges();
              return RedirectToAction("Index");
          }
  
          // GET: Categories/CategoryGroupUp/5
          public IActionResult CategoryGroupUp(string id)
          {
              if (id == null)
              {
                  return ShowNotFound();
              }
  
              var categories = new List<Tuple<int, IGrouping<string, Category>>>();
              IGrouping<string, Category> targetCategory = null;
              foreach (var category in _context.Category.GroupBy(c => c.Group))
              {
                  int groupStart = (category.First().Priority / 100) * 100;
                  if (category.Key == id)
                  {
                      targetCategory = category;
                  }
                  categories.Add(new Tuple<int, IGrouping<string, Category>>(groupStart, category));
              }
              var orderedCategories = categories.OrderByDescending(t => t.Item1).Select(t => t.Item2).ToList();
              var indexOfTragetCategory = orderedCategories.IndexOf(targetCategory);
              if (indexOfTragetCategory > 0)
              {
                  orderedCategories.Remove(targetCategory);
                  orderedCategories.Insert(indexOfTragetCategory - 1, targetCategory);
              }
  
              for (int i = 0; i < orderedCategories.Count; ++i)
              {
                  ReorderCategoriesPriority(orderedCategories[i], (orderedCategories.Count - i - 1) * 100);
              }
              
              _context.SaveChanges();
              return RedirectToAction("Index", "Home");
          }
  
          private void ReorderCategoriesPriority(IEnumerable<Category> nextGroupCategories, int priorityStart)
          {
              foreach (var category in nextGroupCategories.OrderBy(c => c.Priority))
              {
                  category.Priority = priorityStart++;
                  _context.Category.Update(category);
              }
          }
  
          // GET: Categories/CategoryUp/5
          public IActionResult CategoryUp(int? id)
          {
              if (id == null)
              {
                  return ShowNotFound();
              }
  
              var categoryToUp = _context.Category
                  .Where(c => c.CategoryID == id).FirstOrDefault();
  
              if (categoryToUp == null)
                  return RedirectToAction("Index", "Home");
  
              var groupPriorityStart = (categoryToUp.Priority / 100) * 100;
              var groupPriorityEnd = groupPriorityStart + 100;
              var nextCategory = _context.Category
                  .Where(c => c.Priority > categoryToUp.Priority && c.Priority < groupPriorityEnd)
  
gt:                 .OrderBy(c => c.Priority)