using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Organization.Project.Common.Bll;
using Organization.Project.Common.Bll.Geocode;
using Organization.Project.Common.Bll.Geocode.Providers;
using Organization.Project.Common.Dal.Objects;
using Organization.Project.Common.Dal;
using Organization.Project.Common.Enums.Bll;
using Organization.Project.Bll.Managers.Extensions;
namespace Organization.Project.Bll.Managers
{
///
/// Handles store management activities
///
public class StoresManager : IStoresManager
{
#region User Defined Variables
private readonly IRepository storesRepository;
private readonly IRepository countryStateRepository;
private readonly IGeocodeProvider geocodeProvider;
private readonly IRepository countryRepository;
private readonly IErrorLogManager errorLog;
#endregion
#region Constructors
///
/// Contstructor
///
/// factory for resolving different repositories
/// geocode provider for indirect search
/// error log manager
public StoresManager(IRepositoriesFactory repositoriesFactory, IGeocodeProvider geocodeProvider, IErrorLogManager errorLogManager)
{
this.storesRepository = repositoriesFactory.GetStoresRepository();
this.countryRepository = repositoriesFactory.GetCountriesRepository();
this.countryStateRepository = repositoriesFactory.GetCountriesStatesRepository();
this.geocodeProvider = geocodeProvider;
this.errorLog = errorLogManager;
}
#endregion
#region Public Methods
///
/// Gets list of stores
///
/// country id
/// country state id
/// country zip code
/// search type
/// list of stores
public IEnumerable GetStores(int countryId, int? stateId, string zip, StoresSearchProductType searchType)
{
IEnumerable result;
IEnumerable states = GetStateNames(stateId);
IEnumerable offlineStores = GetOfflineStores(countryId, states, zip, null, searchType);
if (string.IsNullOrEmpty(zip))
{
//search without zip
IEnumerable onlineStores = GetOnlineStores(countryId, searchType);
result = StoresManagerExtensions.GetMergedStores(offlineStores, onlineStores);
}
else
{
//search with zip
IEnumerable excludeId = offlineStores.Select(s => s.Id).ToArray();
IEnumerable indirectStores = GetGeocodeIndirectStores(countryId, states, zip, searchType, excludeId);
//if there are no direct and indirect results => remove zip term
if (offlineStores.Count() == 0 && indirectStores.Count() == 0)
{
result = GetStores(countryId, stateId, null, searchType);
}
else
{
//merge direct, online, indirect results
IEnumerable onlineStores = GetOnlineStores(countryId, searchType);
result = offlineStores.Union(StoresManagerExtensions.GetMergedStores(indirectStores, onlineStores));
}
}
return result;
}
#endregion
#region Private Methods
///
/// Gets list of online stores
///
/// country id
/// search type
/// list of online stores
private IQueryable GetOnlineStores(int countryId, StoresSearchProductType searchType)
{
return storesRepository.GetAll()
.Where(s => s.CountryId == countryId && s.IsOnline)
.ApplySearchTypeFilter(searchType)
.OrderByDescending(s => s.Weight);
}
///
/// Gets list of offline stores
///
/// country id
/// country state id
/// country zip code
/// list of cities
/// search type
/// list of offline stores
private IQueryable GetOfflineStores(int countryId, IEnumerable states, string zip, IEnumerable cities, StoresSearchProductType searchType)
{
return storesRepository.GetAll()
.Where(s => s.CountryId == countryId && !s.IsOnline)
.ApplyZipFilter(zip)
.ApplyCityFilter(cities)
.ApplyStatesFilter(states)
.ApplySearchTypeFilter(searchType)
.OrderByDescending(s => s.Weight);
}
private IEnumerable GetGeocodeIndirectStores(int countryId, IEnumerable states, string zip, StoresSearchProductType searchType, IEnumerable excludeId)
{
ICountry country = countryRepository.GetAll().SingleOrDefault(c => c.Id == countryId);
if (country == null) return null;
IGeocodeSearchResult result = geocodeProvider.GetSearchResult(country.Abbreviation, country.Name, zip);
if (result.Status == GeocodeSearchStatus.Success)
{
//filter by country
IEnumerable resultItems = result.SearchItems.Where(si => si.CountryCode.ToLower() == country.Abbreviation.ToLower());
//if states exists apply state filter
IEnumerable indirectStates = (states != null) ? resultItems.Select(si => si.AdministrativeArea).ToArray() : null;
IEnumerable indirectCities = resultItems.Select(si => si.Locality).ToArray();
//if after filter by country we have empty list -> return empty list
return (indirectCities.Count() == 0)
? new IStore[0]
: (IEnumerable)GetOfflineStores(countryId,
indirectStates,
null,
indirectCities,
searchType)
.ApplyExcludeIdFilter(excludeId);
}
else
{
errorLog.LogMessage(string.Format("Geocode search error: {0} - {1}", result.Status, result.StatusCode));
return new IStore[0];
}
}
///
/// Gets short and long names of state as enumerable sequence
///
/// country state id
/// enumerable sequence of state names
private IEnumerable GetStateNames(int? stateId)
{
if (stateId == null) return null;
ICountryState state = countryStateRepository.GetAll().Where(s => s.Id == stateId).SingleOrDefault();
if (state == null) return null;
return Enumerable.Repeat(state.ShortName.ToLower(), 1).Concat(Enumerable.Repeat(state.LongName.ToLower(), 1));
}
#endregion
}
}