context: using System;
  using System.Collections.Generic;
  using System.Linq;
  using SmartStore.Core;
  using SmartStore.Core.Caching;
  using SmartStore.Core.Data;
  using SmartStore.Core.Domain.Directory;
  using SmartStore.Core.Events;
  
  namespace SmartStore.Services.Directory
  {
      /// <summary>
      /// Measure dimension service
      /// </summary>
      public partial class MeasureService : IMeasureService
      {
          #region Constants
          private const string MEASUREDIMENSIONS_ALL_KEY = "SmartStore.measuredimension.all";
          private const string MEASUREWEIGHTS_ALL_KEY = "SmartStore.measureweight.all";
          private const string MEASUREDIMENSIONS_PATTERN_KEY = "SmartStore.measuredimension.";
          private const string MEASUREWEIGHTS_PATTERN_KEY = "SmartStore.measureweight.";
          private const string MEASUREDIMENSIONS_BY_ID_KEY = "SmartStore.measuredimension.id-{0}";
          private const string MEASUREWEIGHTS_BY_ID_KEY = "SmartStore.measureweight.id-{0}";
          #endregion
  
          #region Fields
  
          private readonly IRepository<MeasureDimension> _measureDimensionRepository;
          private readonly IRepository<MeasureWeight> _measureWeightRepository;
          private readonly IRequestCache _requestCache;
          private readonly MeasureSettings _measureSettings;
          private readonly IEventPublisher _eventPublisher;
  
          #endregion
  
          #region Ctor
  
          /// <summary>
          /// Ctor
          /// </summary>
          /// <param name="requestCache">Cache manager</param>
          /// <param name="measureDimensionRepository">Dimension repository</param>
          /// <param name="measureWeightRepository">Weight repository</param>
          /// <param name="measureSettings">Measure settings</param>
          /// <param name="eventPublisher">Event published</param>
          public MeasureService(IRequestCache requestCache,
              IRepository<MeasureDimension> measureDimensionRepository,
              IRepository<MeasureWeight> measureWeightRepository,
              MeasureSettings measureSettings,
              IEventPublisher eventPublisher)
          {
              _requestCache = requestCache;
              _measureDimensionRepository = measureDimensionRepository;
              _measureWeightRepository = measureWeightRepository;
              _measureSettings = measureSettings;
             _eventPublisher = eventPublisher;
          }
  
          #endregion
  
          #region Methods
  
          #region Dimensions
  
          /// <summary>
          /// Deletes measure dimension
          /// </summary>
          /// <param name="measureDimension">Measure dimension</param>
          public virtual void DeleteMeasureDimension(MeasureDimension measureDimension)
          {
              if (measureDimension == null)
                  throw new ArgumentNullException("measureDimension");
  
              _measureDimensionRepository.Delete(measureDimension);
  
              _requestCache.RemoveByPattern(MEASUREDIMENSIONS_PATTERN_KEY);
  
              //event notification
              _eventPublisher.EntityDeleted(measureDimension);
          }
          
  
          /// <summary>
          /// Gets a measure dimension by identifier
          /// </summary>
          /// <param name="measureDimensionId">Measure dimension identifier</param>
          /// <returns>Measure dimension</returns>
          public virtual MeasureDimension GetMeasureDimensionById(int measureDimensionId)
          {
              if (measureDimensionId == 0)
                  return null;
  
              string key = string.Format(MEASUREDIMENSIONS_BY_ID_KEY, measureDimensionId);
              return _requestCache.Get(key, () => 
              { 
                  return _measureDimensionRepository.GetById(measureDimensionId); 
              });
          }
  
          /// <summary>
          /// Gets a measure dimension by system keyword
          /// </summary>
          /// <param name="systemKeyword">The system keyword</param>
          /// <returns>Measure dimension</returns>
          public virtual MeasureDimension GetMeasureDimensionBySystemKeyword(string systemKeyword)
          {
              if (String.IsNullOrEmpty(systemKeyword))
                  return null;
  
              var measureDimensions = GetAllMeasureDimensions();
              foreach (var measureDimension in measureDimensions)
                  if (measureDimension.SystemKeyword.ToLowerInvariant() == systemKeyword.ToLowerInvariant())
                      return measureDimension;
              return null;
          }
  
          /// <summary>
          /// Gets all measure dimensions
          /// </summary>
          /// <returns>Measure dimension collection</returns>
          public virtual IList<MeasureDimension> GetAllMeasureDimensions()
          {
              string key = MEASUREDIMENSIONS_ALL_KEY;
              return _requestCache.Get(key, () =>
              {
                  var query = from md in _measureDimensionRepository.Table
                              orderby md.DisplayOrder
                              select md;
                  var measureDimensions = query.ToList();
                  return measureDimensions;
  
              });
          }
  
          /// <summary>
          /// Inserts a measure dimension
          /// </summary>
          /// <param name="measure">Measure dimension</param>
          public virtual void InsertMeasureDimension(MeasureDimension measure)
          {
              if (measure == null)
                  throw new ArgumentNullException("measure");
  
              _measureDimensionRepository.Insert(measure);
  
              _requestCache.RemoveByPattern(MEASUREDIMENSIONS_PATTERN_KEY);
  
              //event notification
              _eventPublisher.EntityInserted(measure);
          }
  
          /// <summary>
          /// Updates the measure dimension
          /// </summary>
          /// <param name="measure">Measure dimension</param>
          public virtual void UpdateMeasureDimension(MeasureDimension measure)
          {
              if (measure == null)
                  throw new ArgumentNullException("measure");
  
              _measureDimensionRepository.Update(measure);
  
              _requestCache.RemoveByPattern(MEASUREDIMENSIONS_PATTERN_KEY);
  
              //event notification
              _eventPublisher.EntityUpdated(measure);
          }
  
          /// <summary>
          /// Converts dimension
          /// </summary>
          /// <param name="quantity">Quantity</param>
          /// <param name="sourceMeasureDimension">Source dimension</param>
          /// <param name="targetMeasureDimension">Target dimension</param>
          /// <param name="round">A value indicating whether a result should be rounded</param>
          /// <returns>Converted value</returns>
          public virtual decimal ConvertDimension(decimal quantity, 
              MeasureDimension sourceMeasureDimension, MeasureDimension targetMeasureDimension, bool round = true)
          {
              decimal result = quantity;
              if (result != decimal.Zero && sourceMeasureDimension.Id != targetMeasureDimension.Id)
              {
                  result = ConvertToPrimaryMeasureDimension(result, sourceMeasureDimension);
                  result = ConvertFromPrimaryMeasureDimension(result, targetMeasureDimension);
              }
              if (round)
                  result = Math.Round(result, 2);
              return result;
          }
  
          /// <summary>
          /// Converts to primary measure dimension
          /// </summary>
          /// <param name="quantity">Quantity</param>
          /// <param name="sourceMeasureDimension">Source dimension</param>
          /// <returns>Converted value</returns>
          public virtual decimal ConvertToPrimaryMeasureDimension(decimal quantity,
              MeasureDimension sourceMeasureDimension)
          {
              decimal result = quantity;
              var baseDimensionIn = GetMeasureDimensionById(_measureSettings.BaseDimensionId);
              if (result != decimal.Zero && sourceMeasureDimension.Id != baseDimensionIn.Id)
              {
                  decimal exchangeRatio = sourceMeasureDimension.Ratio;
                  if (exchangeRatio == decimal.Zero)
                      throw new SmartException(string.Format("Exchange ratio not set for dimension [{0}]", sourceMeasureDimension.Name));
                  result = result / exchangeRatio;
              }
              return result;
          }
  
          /// <summary>
          /// Converts from primary dimension
          /// </summary>
          /// <param name="quantity">Quantity</param>
          /// <param name="targetMeasureDimension">Target dimension</param>
          /// <returns>Converted value</returns>
          public virtual decimal ConvertFromPrimaryMeasureDimension(decimal quantity,
              MeasureDimension targetMeasureDimension)
          {
              decimal result = quantity;
              var baseDimensionIn = GetMeasureDimensionById(_measureSettings.BaseDimensionId);
              if (result != decimal.Zero && targetMeasureDimension.Id != baseDimensionIn.Id)
              {
                  decimal exchangeRatio = targetMeasureDimension.Ratio;
  
gt:                 if (exchangeRatio == decimal.Zero)