using Newtonsoft.Json;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
using ACG6MvcAdHcApi;
using ACG6MvcAdHcApi.BusinessLayer;
using ACG6MvcAdHcApi.Models;
using ACG6MvcAdHcApi.ViewModels;
using ACG6MvcAdHcApi.Domain;
using System.Collections.Generic;
using System.Threading.Tasks;
 
namespace ACG6MvcAdHc.Controllers
{
     /// <summary>
     /// Works like the Base class for ProductController class.
     /// ************************************* Do not make changes to this class *************************************
     /// ** Put your additional code in the ProductController class under the Controller folder. **
     /// *************************************************************************************************************
     /// </summary>
     public partial class ProductController : Controller
     {
         private Product _product;
         private ProductViewModel _productViewModel;
         private ProductForeachViewModel _productForEachViewModel;
         private readonly IProductBusinessLayer _productBusinessLayer;
         private readonly ISupplierBusinessLayer _supplierBusinessLayer;
         private readonly ICategoryBusinessLayer _categoryBusinessLayer;
 
         // constructor
         public ProductController(Product product, IProductBusinessLayer productBusinessLayer, ISupplierBusinessLayer supplierBusinessLayer, ICategoryBusinessLayer categoryBusinessLayer, ProductViewModel productViewModel, ProductForeachViewModel productForeachViewModel)
         {
             _product = product;
             _productViewModel = productViewModel;
             _productForEachViewModel = productForeachViewModel;
             _productBusinessLayer = productBusinessLayer;
             _supplierBusinessLayer = supplierBusinessLayer;
             _categoryBusinessLayer = categoryBusinessLayer;
         }
 
         #region actions used by their respective views
 
         /// <summary>
         /// GET: /Product/
         /// Gets the view used by the Index razor view
         /// </summary>
         public IActionResult Index()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/Add
         /// Gets the view model used by the Add razor view
         /// </summary>
         public async Task<IActionResult> Add(string urlReferrer = null)
         {
             // return the view model
             return await this.GetAddViewModelAsync(urlReferrer);
         }
 
         /// <summary>
         /// POST: /Product/Add
         /// Posts values from the Add razor view
         /// </summary>
         [HttpPost]
         [ValidateAntiForgeryToken]
         public async Task<IActionResult> Add(ProductViewModel viewModelstring returnUrl)
         {
             return await this.ValidateAndSave(CrudOperation.Add, viewModel, returnUrl);
         }
 
         /// <summary>
         /// GET: /Product/Update/*
         /// Gets the view model used by the Update razor view
         /// </summary>
         public async Task<IActionResult> Update(int idstring urlReferrer = null)
         {
             // return view model
             return await this.GetUpdateViewModelAsync(id, urlReferrer);
         }
 
         /// <summary>
         /// POST: /Product/Update/#
         /// Posts values from the Update razor view
         /// </summary>
         [HttpPost]
         [ValidateAntiForgeryToken]
         public async Task<IActionResult> Update(int id, ProductViewModel viewModelstring returnUrl)
         {
             if (ModelState.IsValid)
                 return await this.ValidateAndSave(CrudOperation.Update, viewModel, returnUrl, id);
 
             // if we got this far, something failed, redisplay form
             return await this.GetUpdateViewModelAsync(id, returnUrl);
         }
 
         /// <summary>
         /// GET: /Details/Details/#
         /// Gets the view model used by the Details razor view
         /// </summary>
         public async Task<IActionResult> Details(int idstring urlReferrer)
         {
             // fill the model by primary key
             await this.FillModelByPrimaryKeyAsync(id);
 
             // set and return the view model
             _productViewModel = await this.GetViewModelAsync("Details""Product", urlReferrer, _product, CrudOperation.None);
             return View(_productViewModel);
         }
 
         /// <summary>
         /// GET: /Product/ListCrudRedirect/
         /// Gets the view used by the ListCrudRedirect razor view
         /// </summary>
         public IActionResult ListCrudRedirect()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListReadOnly/
         /// Gets the view used by the ListReadOnly razor view
         /// </summary>
         public IActionResult ListReadOnly()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListCrud/
         /// Gets the view model used by the ListCrud razor view
         /// </summary>
         public async Task<IActionResult> ListCrud()
         {
             // return view model
             _productViewModel = await this.GetViewModelAsync("ListCrud");
             return View(_productViewModel);
         }
 
         /// <summary>
         /// POST: /Product/ListCrud
         /// Posts values from the ListCrud
         /// </summary>
         [HttpPost]
         public async Task<IActionResult> ListCrud(string inputSubmitstring serializedData)
         {
             // deserialize serializedData
             _product = JsonConvert.DeserializeObject<Product>(serializedData);
 
             // assign a value to the view model's Product (Model) property
             _productViewModel.Product = _product;
 
             if (ModelState.IsValid)
             {
                 CrudOperation operation = CrudOperation.Add;
 
                 if (inputSubmit == "Update")
                     operation = CrudOperation.Update;
 
                 try
                 {
                     // add a new record or update an existing record
                     await AddEditProductAsync(_productViewModel, operation, true);
                 }
                 catch(Exception ex)
                 {
                     ModelState.AddModelError("", ex.Message);
                 }
             }
 
             return Json(true);
         }
 
         /// <summary>
         /// GET: /Product/ListTotals/
         /// Gets the view used by the ListTotals razor view
         /// </summary>
         public IActionResult ListTotals()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListSearch/
         /// Gets the view model used by the ListSearch razor view
         /// </summary>
         public async Task<IActionResult> ListSearch()
         {
             // return view model
             _productViewModel = await this.GetViewModelAsync("ListSearch");
             return View(_productViewModel);
         }
 
         /// <summary>
         /// GET: /Product/ListScrollLoad/
         /// Gets the view used by the ListScrollLoad razor view
         /// </summary>
         public IActionResult ListScrollLoad()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListInline/
         /// Gets the view model used by the ListInline razor view
         /// </summary>
         public async Task<IActionResult> ListInline()
         {
             // return view model
             _productViewModel = await this.GetViewModelAsync("ListInline");
             return View(_productViewModel);
         }
 
         /// <summary>
         /// POST: /Product/ListInlineAdd
         /// Posts the values from the ListInlineAdd
         /// </summary>
         [HttpPost]
         public async Task<IActionResult> ListInlineAdd([FromBody]string modelString)
         {
             return await this.ListInlineAddOrUpdate(modelString, CrudOperation.Add);
         }
 
         /// <summary>
         /// POST: /Product/ListInlineUpdate
         /// Posts the values from the ListInlineUpdate
         /// </summary>
         [HttpPost]
         public async Task<IActionResult> ListInlineUpdate([FromBody]string modelString)
         {
             return await this.ListInlineAddOrUpdate(modelString, CrudOperation.Update);
         }
 
         /// <summary>
         /// GET: /Product/ListForeach
         /// Gets the view model used by the ListForeach razor view
         /// </summary>
         public async Task<IActionResult> ListForeach(string sidxstring sordintpage)
         {
             // get the default number of rows to show
             int rows = Functions.GetGridNumberOfRows();
 
             // the numberOfPagesToShow is used to show the "< First..." link in the razor view
             int numberOfPagesToShow = Functions.GetGridNumberOfPagesToShow();
 
             // when page is null, set it to 1
             int currentPage = page is null ? 1 : Convert.ToInt32(page);
 
             // get the total number of records
             int totalRecords = await _productBusinessLayer.GetRecordCountAsync();
 
             // get the index where to start retrieving records from and the total pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(currentPage, rows, totalRecords);
 
             // get records based on filters
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeAsync(rows, startRowIndex, sidx + " " + sord);
 
             // fields and titles
             string[,] fieldNames = new string[,] {
                 {"ProductID""Product ID"},
                 {"ProductName""Product Name"},
                 {"SupplierID""Supplier ID"},
                 {"CategoryID""Category ID"},
                 {"QuantityPerUnit""Quantity Per Unit"},
                 {"UnitPrice""Unit Price"},
                 {"UnitsInStock""Units In Stock"},
                 {"UnitsOnOrder""Units On Order"},
                 {"ReorderLevel""Reorder Level"},
                 {"Discontinued""Discontinued"}
             };
 
             // assign values to the view model
             _productForEachViewModel.ProductData = objProductsList;
             _productForEachViewModel.ProductFieldNames = fieldNames;
             _productForEachViewModel.TotalPages = totalPages;
             _productForEachViewModel.CurrentPage = currentPage;
             _productForEachViewModel.FieldToSort = String.IsNullOrEmpty(sidx) ? "ProductID" : sidx;
             _productForEachViewModel.FieldSortOrder = String.IsNullOrEmpty(sord) ? "asc" : sord;
             _productForEachViewModel.FieldToSortWithOrder = String.IsNullOrEmpty(sidx) ? "ProductID" : (sidx + " " + sord).Trim();
             _productForEachViewModel.NumberOfPagesToShow = numberOfPagesToShow;
             _productForEachViewModel.StartPage = Functions.GetPagerStartPage(currentPage, numberOfPagesToShow);
             _productForEachViewModel.EndPage = Functions.GetPagerEndPage(_productForEachViewModel.StartPage, numberOfPagesToShow, totalPages);
 
             // return the view model
             return View(_productForEachViewModel);
         }
 
         /// <summary>
         /// GET: /Product/ListMasterDetailGridBySupplierID/
         /// Gets the view used by the ListMasterDetailGridBySupplierID razor view
         /// </summary>
         public IActionResult ListMasterDetailGridBySupplierID()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListMasterDetailGridByCategoryID/
         /// Gets the view used by the ListMasterDetailGridByCategoryID razor view
         /// </summary>
         public IActionResult ListMasterDetailGridByCategoryID()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListMasterDetailSubGridBySupplierID/
         /// Gets the view used by the ListMasterDetailSubGridBySupplierID razor view
         /// </summary>
         public IActionResult ListMasterDetailSubGridBySupplierID()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListMasterDetailSubGridByCategoryID/
         /// Gets the view used by the ListMasterDetailSubGridByCategoryID razor view
         /// </summary>
         public IActionResult ListMasterDetailSubGridByCategoryID()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListMultipleDelete/
         /// Gets the view used by the ListMultipleDelete razor view
         /// </summary>
         public IActionResult ListMultipleDelete()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListGroupedBySupplierID/
         /// Gets the view used by the ListGroupedBySupplierID razor view
         /// </summary>
         public IActionResult ListGroupedBySupplierID()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListGroupedByCategoryID/
         /// Gets the view used by the ListGroupedByCategoryID razor view
         /// </summary>
         public IActionResult ListGroupedByCategoryID()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListTotalsGroupedBySupplierID/
         /// Gets the view used by the ListTotalsGroupedBySupplierID razor view
         /// </summary>
         public IActionResult ListTotalsGroupedBySupplierID()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// GET: /Product/ListTotalsGroupedByCategoryID/
         /// Gets the view used by the ListTotalsGroupedByCategoryID razor view
         /// </summary>
         public IActionResult ListTotalsGroupedByCategoryID()
         {
             // return view
             return View();
         }
 
         /// <summary>
         /// Gets the view used by the ListBySupplierID razor view
         /// </summary>
         public async Task<IActionResult> ListBySupplierID()
         {
             // assign values to the view model
             _productViewModel.SupplierDropDownListData = await _supplierBusinessLayer.SelectSupplierDropDownListDataAsync();
 
             // return the view model
             return View(_productViewModel);
         }
 
         /// <summary>
         /// Gets the view used by the ListByCategoryID razor view
         /// </summary>
         public async Task<IActionResult> ListByCategoryID()
         {
             // assign values to the view model
             _productViewModel.CategoryDropDownListData = await _categoryBusinessLayer.SelectCategoryDropDownListDataAsync();
 
             // return the view model
             return View(_productViewModel);
         }
 
         /// <summary>
         /// GET: /Product/Unbound
         /// Gets the view model used by the Unbound razor view
         /// </summary>
         public async Task<IActionResult> Unbound()
         {
             // set and return the view model
             _productViewModel = await this.GetViewModelAsync("Unbound""Product""/Home"null, CrudOperation.None, falsefalse);
             return View(_productViewModel);
         }
 
         /// <summary>
         /// POST: /Product/Unbound
         /// Post values fromy the Unbound razor view
         /// </summary>
         [HttpPost]
         [ValidateAntiForgeryToken]
         public async Task<IActionResult> Unbound(ProductViewModel viewModelstring returnUrl)
         {
             if (ModelState.IsValid)
             {
                 // do something here before redirecting
 
                 if (Url.IsLocalUrl(returnUrl))
                     return Redirect(returnUrl);
                 else
                     return RedirectToAction("Index""Home");
             }
 
             // if we got this far, something failed, redisplay form
             _productViewModel = await this.GetViewModelAsync("Unbound""Product""/Home"null, CrudOperation.None, falsefalse);
             return View(_productViewModel);
         }
 
         #endregion
 
         #region public methods
 
         /// <summary>
         /// POST: /Product/Delete/#
         /// Deletes a record based on the id(s)
         /// </summary>
         [HttpPost]
         public async Task<IActionResult> Delete(int id)
         {
             // delete record
             await _productBusinessLayer.DeleteAsync(id);
             return Json(true);
         }
 
         /// <summary>
         /// POST: /Product/DeleteMultiple/ids
         /// Deletes multiple records based on the comma-delimited ids
         /// </summary>
         [HttpPost]
         public async Task<IActionResult> DeleteMultiple(string ids)
         {
             // split ids into a List
             List<Int32> productIDList = ids.Split(",").Select(Int32.Parse).ToList();
 
             // delete multiple records
             await _productBusinessLayer.DeleteMultipleAsync(productIDList);
             return Json(true);
         }
 
         #endregion
 
         #region private methods
 
         /// <summary>
         /// Gets the view model used by the Add razor view
         /// </summary>
         private async Task<IActionResult> GetAddViewModelAsync(string returnUrl = null)
         {
             // set the return url to /ListCrudRedirect when the returnUrl paramater is empty
             string url = String.IsNullOrEmpty(returnUrl) ? "/Product/ListCrudRedirect" : returnUrl;
 
             // set and return the view model
             _productViewModel = await this.GetViewModelAsync("Add""Product", url, null, CrudOperation.Add);
             return View(_productViewModel);
         }
 
         /// <summary>
         /// Gets the view model used by the Update razor view
         /// </summary>
         private async Task<IActionResult> GetUpdateViewModelAsync(int idstring urlReferrer)
         {
             // fill the model by primary key
             await this.FillModelByPrimaryKeyAsync(id);
 
             // set and return the view model
             _productViewModel = await this.GetViewModelAsync("Update""Product", urlReferrer, _product, CrudOperation.Update);
             return View(_productViewModel);
         }
 
         /// <summary>
         /// Used when adding a new record or updating an existing record
         /// </summary>
         /// <param name="viewModel">ProductViewModel</param>
         /// <param name="operation">Operation to Add a new record or Update an existing record</param>
         /// <param name="isForListInlineOrListCrud">Used by the razor Views with ListInline or ListCrud when true</param>
         /// <returns>Task of IActionResult</returns>
         private async Task<IActionResult> AddEditProductAsync(ProductViewModel viewModel, CrudOperation operationbool isForListInlineOrListCrud = false)
         {
             Product model = viewModel.Product;
 
             try
             {
                 // create a new instance of the Product when adding a new record
                 // or, retrieve the record that needs to be updated
                 if (operation == CrudOperation.Add)
                    _product = new();
                 else
                    _product = await _productBusinessLayer.SelectByPrimaryKeyAsync(model.ProductID);
 
                 // assign values to the Product instance
                 _product.ProductID = model.ProductID;
                 _product.ProductName = model.ProductName;
                 _product.SupplierID = model.SupplierID;
                 _product.CategoryID = model.CategoryID;
                 _product.QuantityPerUnit = model.QuantityPerUnit;
                 _product.UnitPrice = model.UnitPrice;
                 _product.Discontinued = model.Discontinued;
 
                 if (isForListInlineOrListCrud)
                 {
                     _product.UnitsInStock = model.UnitsInStock;
                     _product.UnitsOnOrder = model.UnitsOnOrder;
                     _product.ReorderLevel = model.ReorderLevel;
                 }
                 else
                 {
                     if(!String.IsNullOrEmpty(model.UnitsInStockHidden))
                        _product.UnitsInStock = Convert.ToInt16(model.UnitsInStockHidden);
                     else
                        _product.UnitsInStock = null;
 
                     if(!String.IsNullOrEmpty(model.UnitsOnOrderHidden))
                        _product.UnitsOnOrder = Convert.ToInt16(model.UnitsOnOrderHidden);
                     else
                        _product.UnitsOnOrder = null;
 
                     if(!String.IsNullOrEmpty(model.ReorderLevelHidden))
                        _product.ReorderLevel = Convert.ToInt16(model.ReorderLevelHidden);
                     else
                        _product.ReorderLevel = null;
 
                 }
 
                 // save the new record, or the updated values of the current record
                 if (operation == CrudOperation.Add)
                    await _productBusinessLayer.InsertAsync(_product);
                 else
                    await _productBusinessLayer.UpdateAsync(_product);
 
                 // everthing went well
                 return Ok();
             }
             catch (Exception ex)
             {
                 // something went wrong
                 return BadRequest("Error Message: " + ex.Message);
             }
         }
 
         /// <summary>
         /// Gets the view model based on the actionName
         /// </summary>
         private async Task<ProductViewModel> GetViewModelAsync(string actionName,
         string controllerName = "Product"string returnUrl = null, Product objProduct = null,
         CrudOperation operation = CrudOperation.None, bool isFillSupplierDdl = truebool isFillCategoryDdl = true)
         {
             // assign values to the view model
             _productViewModel.Product = objProduct;
             _productViewModel.Operation = operation;
             _productViewModel.ViewControllerName = controllerName;
             _productViewModel.ViewActionName = actionName;
             _productViewModel.ViewReturnUrl = returnUrl;
 
             if(isFillSupplierDdl)
                 _productViewModel.SupplierDropDownListData = await _supplierBusinessLayer.SelectSupplierDropDownListDataAsync();
             else
                 _productViewModel.SupplierDropDownListData = null;
 
             if(isFillCategoryDdl)
                 _productViewModel.CategoryDropDownListData = await _categoryBusinessLayer.SelectCategoryDropDownListDataAsync();
             else
                 _productViewModel.CategoryDropDownListData = null;
 
             // return the view model
             return _productViewModel;
         }
 
         /// <summary>
         /// Validates the Add or Update operation and Saves the data when valid and no errors.
         /// </summary>
         /// <param name="operation">Add or Update only.  CrudOperation.None is not permitted</param>
         /// <param name="productViewModel">The View Model</param>
         /// <param name="returnUrl">Url to return to after the operation</param>
         /// <param name="id">Optional.  But required when operation = CrudOperation.Update</param>
         /// <returns>View Model as IActionResult</returns>
         private async Task<IActionResult> ValidateAndSave(CrudOperation operation, ProductViewModel productViewModelstring returnUrlintid = null)
         {
             if (operation == CrudOperation.None)
                 throw new ArgumentException("operation = CrudOperation.None is not valid for the ValidateAndSave method.");
 
             if (ModelState.IsValid)
             {
                 try
                 {
                     // add or update record
                     await this.AddEditProductAsync(productViewModel, operation);
 
                     if (Url.IsLocalUrl(returnUrl))
                         return Redirect(returnUrl);
                     else
                         return RedirectToAction("ListCrudRedirect""Product");
                 }
                 catch (Exception ex)
                 {
                     if (ex.InnerException != null)
                         ModelState.AddModelError("", ex.InnerException.Message);
                     else
                         ModelState.AddModelError("", ex.Message);
                 }
             }
 
             // if we got this far, something failed, redisplay form
             if (operation == CrudOperation.Add)
                 return await GetAddViewModelAsync(returnUrl);
             else
                 return await this.GetUpdateViewModelAsync(id.Value, returnUrl);
         }
 
         /// <summary>
         /// Selects records as a collection (List) of Product sorted by the sord filtered by the passed parameters
         /// </summary>
         private async Task<List<Product>> GetFilteredDataAsync(intproductIDstring productNameintsupplierIDintcategoryIDstring quantityPerUnitdecimalunitPrice, Int16? unitsInStock, Int16? unitsOnOrder, Int16? reorderLevelbooldiscontinuedstring sidxstring sordint rowsint startRowIndexstring sortExpression)
         {
             if (productID != null || !String.IsNullOrEmpty(productName) || supplierID != null || categoryID != null || !String.IsNullOrEmpty(quantityPerUnit) || unitPrice != null || unitsInStock != null || unitsOnOrder != null || reorderLevel != null || discontinued != null)
                 return await _productBusinessLayer.SelectSkipAndTakeDynamicWhereAsync(productID, productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, rows, startRowIndex, sortExpression);
 
             return await _productBusinessLayer.SelectSkipAndTakeAsync(rows, startRowIndex, sortExpression);
         }
 
         /// <summary>
         /// Fills the Product model information by primary key
         /// </summary>
         private async Task FillModelByPrimaryKeyAsync(int productID)
         {
             // select a record by primary key(s)
             Product objProduct = await _productBusinessLayer.SelectByPrimaryKeyAsync(productID);
 
             // assign values to the model
             _product.ProductID = objProduct.ProductID;
             _product.ProductName = objProduct.ProductName;
             _product.SupplierID = objProduct.SupplierID;
             _product.CategoryID = objProduct.CategoryID;
             _product.QuantityPerUnit = objProduct.QuantityPerUnit;
             _product.UnitPrice = objProduct.UnitPrice;
             _product.UnitsInStock = objProduct.UnitsInStock;
 
             if (objProduct.UnitsInStock.HasValue)
                 _product.UnitsInStockHidden = objProduct.UnitsInStock.Value.ToString();
             else
                 _product.UnitsInStockHidden = null;
 
             _product.UnitsOnOrder = objProduct.UnitsOnOrder;
 
             if (objProduct.UnitsOnOrder.HasValue)
                 _product.UnitsOnOrderHidden = objProduct.UnitsOnOrder.Value.ToString();
             else
                 _product.UnitsOnOrderHidden = null;
 
             _product.ReorderLevel = objProduct.ReorderLevel;
 
             if (objProduct.ReorderLevel.HasValue)
                 _product.ReorderLevelHidden = objProduct.ReorderLevel.Value.ToString();
             else
                 _product.ReorderLevelHidden = null;
 
             _product.Discontinued = objProduct.Discontinued;
 
             // assign values to the model
             _product.ProductID = objProduct.ProductID;
             _product.ProductName = objProduct.ProductName;
             _product.SupplierID = objProduct.SupplierID;
             _product.CategoryID = objProduct.CategoryID;
             _product.QuantityPerUnit = objProduct.QuantityPerUnit;
             _product.UnitPrice = objProduct.UnitPrice;
 
             if(objProduct.UnitsInStock.HasValue)
                 _product.UnitsInStockHidden = objProduct.UnitsInStock.ToString();
 
             if(objProduct.UnitsOnOrder.HasValue)
                 _product.UnitsOnOrderHidden = objProduct.UnitsOnOrder.ToString();
 
             if(objProduct.ReorderLevel.HasValue)
                 _product.ReorderLevelHidden = objProduct.ReorderLevel.ToString();
             _product.Discontinued = objProduct.Discontinued;
         }
 
         /// <summary>
         /// Deserializes the modelString and then Adds a New Record or Updates and existing record.
         /// This method is used by ListInlineAdd or ListInlineUpdate.
         /// </summary>
         private async Task<IActionResult> ListInlineAddOrUpdate(string modelString, CrudOperation operation)
         {
             // deserialize modelString
             _productViewModel.Product = JsonConvert.DeserializeObject<Product>("{" + modelString + "}");
 
             // add new record or update existing record
             await this.AddEditProductAsync(_productViewModel, operation, true);
             return Json(true);
         }
 
         /// <summary>
         /// Gets json-formatted data based on the List of Products for use by the jqgrid.
         /// </summary>
         /// <param name="objProductsList">List of Products</param>
         /// <param name="totalPages">Total number of pages</param>
         /// <param name="page">Current page the grid is on</param>
         /// <param name="totalRecords">Total number of records</param>
         /// <returns>List of Products in Json format</returns>
         private JsonResult GetJsonData(List<Product> objProductsListint totalPagesint pageint totalRecords)
         {
             // return a null in json for use by the jqgrid
             if (objProductsList is null)
                 return Json("{ total = 0, page = 0, records = 0, rows = null }");
 
             // create a serialized json object for use by the jqgrid
             var jsonData = new
             {
                 total = totalPages,
                 page,
                 records = totalRecords,
                 rows = (
                     from objProduct in objProductsList
                     select new
                     {
                         id = objProduct.ProductID,
                         cell = new string[] { 
                             objProduct.ProductID.ToString(),
                             objProduct.ProductName,
                             objProduct.SupplierID.HasValue ? objProduct.SupplierID.Value.ToString() : "",
                             objProduct.CategoryID.HasValue ? objProduct.CategoryID.Value.ToString() : "",
                             objProduct.QuantityPerUnit,
                             objProduct.UnitPrice.HasValue ? objProduct.UnitPrice.Value.ToString() : "",
                             objProduct.UnitsInStock.HasValue ? objProduct.UnitsInStock.Value.ToString() : "",
                             objProduct.UnitsOnOrder.HasValue ? objProduct.UnitsOnOrder.Value.ToString() : "",
                             objProduct.ReorderLevel.HasValue ? objProduct.ReorderLevel.Value.ToString() : "",
                             objProduct.Discontinued.ToString()
                         }
                     }).ToArray()
             };
 
             return Json(jsonData);
         }
 
         /// <summary>
         /// Gets json-formatted data based on the List of Products for use by the jqgrid.
         /// </summary>
         /// <param name="objProductsList">List of Products</param>
         /// <param name="totalPages">Total number of pages</param>
         /// <param name="page">Current page the grid is on</param>
         /// <param name="totalRecords">Total number of records</param>
         /// <returns>List of Products in Json format</returns>
         private JsonResult GetJsonDataGroupedBySupplierID(List<Product> objProductsListint totalPagesint pageint totalRecords)
         {
             // return a null in json for use by the jqgrid
             if (objProductsList is null)
                 return Json("{ total = 0, page = 0, records = 0, rows = null }");
 
             // create a serialized json object for use by the jqgrid
             var jsonData = new
             {
                 total = totalPages,
                 page,
                 records = totalRecords,
                 rows = (
                     from objProduct in objProductsList
                     select new
                     {
                         id = objProduct.ProductID,
                         cell = new string[] { 
                             objProduct.ProductID.ToString(),
                             objProduct.ProductName,
                             objProduct.SupplierID.HasValue ? objProduct.SupplierID.Value.ToString() : "",
                             objProduct.CategoryID.HasValue ? objProduct.CategoryID.Value.ToString() : "",
                             objProduct.QuantityPerUnit,
                             objProduct.UnitPrice.HasValue ? objProduct.UnitPrice.Value.ToString() : "",
                             objProduct.UnitsInStock.HasValue ? objProduct.UnitsInStock.Value.ToString() : "",
                             objProduct.UnitsOnOrder.HasValue ? objProduct.UnitsOnOrder.Value.ToString() : "",
                             objProduct.ReorderLevel.HasValue ? objProduct.ReorderLevel.Value.ToString() : "",
                             objProduct.Discontinued.ToString(),
                             objProduct.SupplierID is null ? "" : objProduct.Supplier.CompanyName
 
                         }
                     }).ToArray()
             };
 
             return Json(jsonData);
         }
 
         /// <summary>
         /// Gets json-formatted data based on the List of Products for use by the jqgrid.
         /// </summary>
         /// <param name="objProductsList">List of Products</param>
         /// <param name="totalPages">Total number of pages</param>
         /// <param name="page">Current page the grid is on</param>
         /// <param name="totalRecords">Total number of records</param>
         /// <returns>List of Products in Json format</returns>
         private JsonResult GetJsonDataGroupedByCategoryID(List<Product> objProductsListint totalPagesint pageint totalRecords)
         {
             // return a null in json for use by the jqgrid
             if (objProductsList is null)
                 return Json("{ total = 0, page = 0, records = 0, rows = null }");
 
             // create a serialized json object for use by the jqgrid
             var jsonData = new
             {
                 total = totalPages,
                 page,
                 records = totalRecords,
                 rows = (
                     from objProduct in objProductsList
                     select new
                     {
                         id = objProduct.ProductID,
                         cell = new string[] { 
                             objProduct.ProductID.ToString(),
                             objProduct.ProductName,
                             objProduct.SupplierID.HasValue ? objProduct.SupplierID.Value.ToString() : "",
                             objProduct.CategoryID.HasValue ? objProduct.CategoryID.Value.ToString() : "",
                             objProduct.QuantityPerUnit,
                             objProduct.UnitPrice.HasValue ? objProduct.UnitPrice.Value.ToString() : "",
                             objProduct.UnitsInStock.HasValue ? objProduct.UnitsInStock.Value.ToString() : "",
                             objProduct.UnitsOnOrder.HasValue ? objProduct.UnitsOnOrder.Value.ToString() : "",
                             objProduct.ReorderLevel.HasValue ? objProduct.ReorderLevel.Value.ToString() : "",
                             objProduct.Discontinued.ToString(),
                             objProduct.CategoryID is null ? "" : objProduct.Category.CategoryName
 
                         }
                     }).ToArray()
             };
 
             return Json(jsonData);
         }
 
         #endregion
 
         #region methods that return data in json format used by the jqgrid
 
         /// <summary>
         /// GET: /Product/GridData
         /// Gets the json needed by the jqgrid for use by the GridData
         /// </summary>
         public async Task<IActionResult> GridData(string sidxstring sordint pageint rows)
         {
             // get the total number of records
             int totalRecords = await _productBusinessLayer.GetRecordCountAsync();
 
             // get the index where to start retrieving records from and the total number of pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(page, rows, totalRecords);
 
             // get records
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeAsync(rows, startRowIndex, sidx + " " + sord);
 
             // return json data for use by the jqgrid
             return this.GetJsonData(objProductsList, totalPages, page, totalRecords);
         }
 
         /// <summary>
         /// GET: /Product/GridDataWithFilters
         /// Gets the json needed by the jqgrid for use by the GridDataWithFilters
         /// </summary>
         public async Task<IActionResult> GridDataWithFilters(string _searchstring ndint rowsint pagestring sidxstring sordstring filters = "")
         {
             intproductID = null;
             string productName = String.Empty;
             intsupplierID = null;
             intcategoryID = null;
             string quantityPerUnit = String.Empty;
             decimalunitPrice = null;
             Int16? unitsInStock = null;
             Int16? unitsOnOrder = null;
             Int16? reorderLevel = null;
             booldiscontinued = null;
 
             if (!String.IsNullOrEmpty(filters))
             {
                 // deserialize filters and get values being searched
                 var jsonResult = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(filters);
 
                 foreach (var rule in jsonResult["rules"])
                 {
                     if (rule["field"].Value.ToLower() == "productid")
                         productID = Convert.ToInt32(rule["data"].Value);
 
                     if (rule["field"].Value.ToLower() == "productname")
                         productName = rule["data"].Value;
 
                     if (rule["field"].Value.ToLower() == "supplierid")
                         supplierID = Convert.ToInt32(rule["data"].Value);
 
                     if (rule["field"].Value.ToLower() == "categoryid")
                         categoryID = Convert.ToInt32(rule["data"].Value);
 
                     if (rule["field"].Value.ToLower() == "quantityperunit")
                         quantityPerUnit = rule["data"].Value;
 
                     if (rule["field"].Value.ToLower() == "unitprice")
                         unitPrice = Convert.ToDecimal(rule["data"].Value);
 
                     if (rule["field"].Value.ToLower() == "unitsinstock")
                         unitsInStock = Convert.ToInt16(rule["data"].Value);
 
                     if (rule["field"].Value.ToLower() == "unitsonorder")
                         unitsOnOrder = Convert.ToInt16(rule["data"].Value);
 
                     if (rule["field"].Value.ToLower() == "reorderlevel")
                         reorderLevel = Convert.ToInt16(rule["data"].Value);
 
                     if (rule["field"].Value.ToLower() == "discontinued")
                         discontinued = Convert.ToBoolean(rule["data"].Value);
 
                 }
 
                 // sometimes jqgrid assigns a -1 to numeric fields when no value is assigned
                 // instead of assigning a null, we'll correct this here
                 if (productID == -1)
                     productID = null;
 
                 if (supplierID == -1)
                     supplierID = null;
 
                 if (categoryID == -1)
                     categoryID = null;
 
                 if (unitPrice == -1)
                     unitPrice = null;
 
                 if (unitsInStock == -1)
                     unitsInStock = null;
 
                 if (unitsOnOrder == -1)
                     unitsOnOrder = null;
 
                 if (reorderLevel == -1)
                     reorderLevel = null;
 
             }
             // get records based on filters
             int totalRecords = await _productBusinessLayer.GetRecordCountDynamicWhereAsync(productID, productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued);
 
             // get the index where to start retrieving records from and the total pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(page, rows, totalRecords);
 
             // get records based on filters
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeDynamicWhereAsync(productID, productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, rows, startRowIndex, sidx + " " + sord);
 
             // return json data for use by the jqgrid
             return this.GetJsonData(objProductsList, totalPages, page, totalRecords);
         }
 
         /// <summary>
         /// GET: /Product/GridDataWithTotals
         /// Gets the json needed by the jqgrid for use by the GridDataWithTotals
         /// </summary>
         public async Task<IActionResult> GridDataWithTotals(string sidxstring sordint pageint rows)
         {
             // get the total number of records
             int totalRecords = await _productBusinessLayer.GetRecordCountAsync();
 
             // get the index where to start retrieving records from and the total number of pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(page, rows, totalRecords);
 
             // get records
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeAsync(rows, startRowIndex, sidx + " " + sord);
 
             // return json data for use by the jqgrid
             return this.GetJsonData(objProductsList, totalPages, page, totalRecords);
         }
 
         /// <summary>
         /// GET: /Product/GridDataGroupedBySupplierID
         /// Gets the json needed by the jqgrid for use by the GridDataGroupedBySupplierID
         /// </summary>
         public async Task<IActionResult> GridDataGroupedBySupplierID(string sidxstring sordint pageint rows)
         {
             // using a groupBy field in the jqgrid passes that field
             // along with the field to sort, remove the groupBy field
             string groupBy = "CompanyName asc, ";
             sidx = sidx.Replace(groupBy, "");
 
             // get the total number of records
             int totalRecords = await _productBusinessLayer.GetRecordCountAsync();
 
             // get the index where to start retrieving records from and the total number of pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(page, rows, totalRecords);
 
             // get records
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeAsync(rows, startRowIndex, sidx + " " + sord);
 
             // return json data for use by the jqgrid
             return this.GetJsonDataGroupedBySupplierID(objProductsList, totalPages, page, totalRecords);
         }
 
         /// <summary>
         /// GET: /Product/GridDataGroupedByCategoryID
         /// Gets the json needed by the jqgrid for use by the GridDataGroupedByCategoryID
         /// </summary>
         public async Task<IActionResult> GridDataGroupedByCategoryID(string sidxstring sordint pageint rows)
         {
             // using a groupBy field in the jqgrid passes that field
             // along with the field to sort, remove the groupBy field
             string groupBy = "CategoryName asc, ";
             sidx = sidx.Replace(groupBy, "");
 
             // get the total number of records
             int totalRecords = await _productBusinessLayer.GetRecordCountAsync();
 
             // get the index where to start retrieving records from and the total number of pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(page, rows, totalRecords);
 
             // get records
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeAsync(rows, startRowIndex, sidx + " " + sord);
 
             // return json data for use by the jqgrid
             return this.GetJsonDataGroupedByCategoryID(objProductsList, totalPages, page, totalRecords);
         }
 
         /// <summary>
         /// GET: /Product/GridDataTotalsGroupedBySupplierID
         /// Gets the json needed by the jqgrid for use by the GridDataTotalsGroupedBySupplierID
         /// </summary>
         public async Task<IActionResult> GridDataTotalsGroupedBySupplierID(string sidxstring sordint pageint rows)
         {
             // using a groupBy field in the jqgrid passes that field
             // along with the field to sort, remove the groupBy field
             string groupBy = "CompanyName asc, ";
             sidx = sidx.Replace(groupBy, "");
 
             // get the total number of records
             int totalRecords = await _productBusinessLayer.GetRecordCountAsync();
 
             // get the index where to start retrieving records from and the total number of pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(page, rows, totalRecords);
 
             // get records
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeAsync(rows, startRowIndex, sidx + " " + sord);
 
             // return json data for use by the jqgrid
             return this.GetJsonDataGroupedBySupplierID(objProductsList, totalPages, page, totalRecords);
         }
 
         /// <summary>
         /// GET: /Product/GridDataTotalsGroupedByCategoryID
         /// Gets the json needed by the jqgrid for use by the GridDataTotalsGroupedByCategoryID
         /// </summary>
         public async Task<IActionResult> GridDataTotalsGroupedByCategoryID(string sidxstring sordint pageint rows)
         {
             // using a groupBy field in the jqgrid passes that field
             // along with the field to sort, remove the groupBy field
             string groupBy = "CategoryName asc, ";
             sidx = sidx.Replace(groupBy, "");
 
             // get the total number of records
             int totalRecords = await _productBusinessLayer.GetRecordCountAsync();
 
             // get the index where to start retrieving records from and the total number of pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(page, rows, totalRecords);
 
             // get records
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeAsync(rows, startRowIndex, sidx + " " + sord);
 
             // return json data for use by the jqgrid
             return this.GetJsonDataGroupedByCategoryID(objProductsList, totalPages, page, totalRecords);
         }
 
         /// <summary>
         /// GET: /Product/GridDataBySupplierID
         /// Gets the json needed by the jqgrid for use by the GridDataBySupplierID
         /// </summary>
         public async Task<IActionResult> GridDataBySupplierID(string sidxstring sordint pageint rowsintsupplierID)
         {
             // get the total number of records
             int totalRecords = await _productBusinessLayer.GetRecordCountBySupplierIDAsync(supplierID);
 
             // get the index where to start retrieving records from and the total number of pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(page, rows, totalRecords);
 
             // get records
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeBySupplierIDAsync(rows, startRowIndex, sidx + " " + sord, supplierID);
 
             // return json data for use by the jqgrid
             return this.GetJsonData(objProductsList, totalPages, page, totalRecords);
         }
 
         /// <summary>
         /// GET: /Product/GridDataByCategoryID
         /// Gets the json needed by the jqgrid for use by the GridDataByCategoryID
         /// </summary>
         public async Task<IActionResult> GridDataByCategoryID(string sidxstring sordint pageint rowsintcategoryID)
         {
             // get the total number of records
             int totalRecords = await _productBusinessLayer.GetRecordCountByCategoryIDAsync(categoryID);
 
             // get the index where to start retrieving records from and the total number of pages
             (int startRowIndexint totalPages) = Functions.GetStartRowIndexAndTotalPages(page, rows, totalRecords);
 
             // get records
             List<Product> objProductsList = await _productBusinessLayer.SelectSkipAndTakeByCategoryIDAsync(rows, startRowIndex, sidx + " " + sord, categoryID);
 
             // return json data for use by the jqgrid
             return this.GetJsonData(objProductsList, totalPages, page, totalRecords);
         }
 
         #endregion
     }
}