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 } }