context: using System;
  using System.Collections.Generic;
  using SmartStore.Core.Domain.Catalog;
  using SmartStore.Core.Domain.Media;
  using SmartStore.Services.Localization;
  using SmartStore.Services.Media;
  using SmartStore.Services.Seo;
  using SmartStore.Services.Stores;
  
  namespace SmartStore.Services.Catalog
  {
      /// <summary>
      /// Copy Product service
      /// </summary>
      public partial class CopyProductService : ICopyProductService
      {
          #region Fields
  
          private readonly IProductService _productService;
          private readonly IProductAttributeService _productAttributeService;
          private readonly ILanguageService _languageService;
          private readonly ILocalizedEntityService _localizedEntityService;
          private readonly IPictureService _pictureService;
          private readonly ICategoryService _categoryService;
          private readonly IManufacturerService _manufacturerService;
          private readonly ISpecificationAttributeService _specificationAttributeService;
          private readonly IDownloadService _downloadService;
          private readonly IProductAttributeParser _productAttributeParser;
          private readonly IUrlRecordService _urlRecordService;
  		private readonly IStoreMappingService _storeMappingService;
  		private readonly ILocalizationService _localizationService;
  
          #endregion
  
          #region Ctor
  
          public CopyProductService(IProductService productService,
              IProductAttributeService productAttributeService, ILanguageService languageService,
              ILocalizedEntityService localizedEntityService, IPictureService pictureService,
              ICategoryService categoryService, IManufacturerService manufacturerService,
              ISpecificationAttributeService specificationAttributeService, IDownloadService downloadService,
              IProductAttributeParser productAttributeParser, IUrlRecordService urlRecordService,
  			IStoreMappingService storeMappingService,
  			ILocalizationService localizationService)
          {
              this._productService = productService;
              this._productAttributeService = productAttributeService;
              this._languageService = languageService;
              this._localizedEntityService = localizedEntityService;
              this._pictureService = pictureService;
              this._categoryService = categoryService;
              this._manufacturerService = manufacturerService;
              this._specificationAttributeService = specificationAttributeService;
              this._downloadService = downloadService;
              this._productAttributeParser = productAttributeParser;
  			this._urlRecordService = urlRecordService;
  			this._storeMappingService = storeMappingService;
  			this._localizationService = localizationService;
          }
  
          #endregion
  
          #region Methods
  
          /// <summary>
  		/// Create a copy of product with all depended data
          /// </summary>
          /// <param name="product">The product</param>
          /// <param name="newName">The name of product duplicate</param>
          /// <param name="isPublished">A value indicating whether the product duplicate should be published</param>
          /// <param name="copyImages">A value indicating whether the product images should be copied</param>
  		/// <param name="copyAssociatedProducts">A value indicating whether the copy associated products</param>
          /// <returns>Product entity</returns>
  		public virtual Product CopyProduct(Product product, string newName, bool isPublished, bool copyImages, bool copyAssociatedProducts = true)
          {
  			if (product == null)
  				throw new ArgumentNullException("product");
  
  			if (String.IsNullOrEmpty(newName))
  				throw new ArgumentException("Product name is required");
  
              Product productCopy = null;
  			var utcNow = DateTime.UtcNow;
  
  			// product download & sample download
  			int downloadId = product.DownloadId;
  			int? sampleDownloadId = product.SampleDownloadId;
  
  			if (product.IsDownload)
  			{
  				var download = _downloadService.GetDownloadById(product.DownloadId);
  				if (download != null)
  				{
  					var downloadCopy = new Download
  					{
  						DownloadGuid = Guid.NewGuid(),
  						UseDownloadUrl = download.UseDownloadUrl,
  						DownloadUrl = download.DownloadUrl,
  						DownloadBinary = download.DownloadBinary,
  						ContentType = download.ContentType,
  						Filename = download.Filename,
  						Extension = download.Extension,
  						IsNew = download.IsNew,
  					};
  					_downloadService.InsertDownload(downloadCopy);
  					downloadId = downloadCopy.Id;
  				}
  
  				if (product.HasSampleDownload)
  				{
  					var sampleDownload = _downloadService.GetDownloadById(product.SampleDownloadId.GetValueOrDefault());
  					if (sampleDownload != null)
  					{
  						var sampleDownloadCopy = new Download
  						{
  							DownloadGuid = Guid.NewGuid(),
  							UseDownloadUrl = sampleDownload.UseDownloadUrl,
  							DownloadUrl = sampleDownload.DownloadUrl,
  							DownloadBinary = sampleDownload.DownloadBinary,
  							ContentType = sampleDownload.ContentType,
  							Filename = sampleDownload.Filename,
  							Extension = sampleDownload.Extension,
  							IsNew = sampleDownload.IsNew
  						};
  						_downloadService.InsertDownload(sampleDownloadCopy);
  						sampleDownloadId = sampleDownloadCopy.Id;
  					}
  				}
  			}
  
              // product
              productCopy = new Product
              {
  				ProductTypeId = product.ProductTypeId,
  				ParentGroupedProductId = product.ParentGroupedProductId,
  				VisibleIndividually = product.VisibleIndividually,
                  Name = newName,
                  ShortDescription = product.ShortDescription,
                  FullDescription = product.FullDescription,
                  ProductTemplateId = product.ProductTemplateId,
                  AdminComment = product.AdminComment,
                  ShowOnHomePage = product.ShowOnHomePage,
  				HomePageDisplayOrder = product.HomePageDisplayOrder,
                  MetaKeywords = product.MetaKeywords,
                  MetaDescription = product.MetaDescription,
                  MetaTitle = product.MetaTitle,
                  AllowCustomerReviews = product.AllowCustomerReviews,
  				LimitedToStores = product.LimitedToStores,
  				Sku = product.Sku,
  				ManufacturerPartNumber = product.ManufacturerPartNumber,
  				Gtin = product.Gtin,
  				IsGiftCard = product.IsGiftCard,
  				GiftCardType = product.GiftCardType,
  				RequireOtherProducts = product.RequireOtherProducts,
  				RequiredProductIds = product.RequiredProductIds,
  				AutomaticallyAddRequiredProducts = product.AutomaticallyAddRequiredProducts,
  				IsDownload = product.IsDownload,
  				DownloadId = downloadId,
  				UnlimitedDownloads = product.UnlimitedDownloads,
  				MaxNumberOfDownloads = product.MaxNumberOfDownloads,
  				DownloadExpirationDays = product.DownloadExpirationDays,
  				DownloadActivationType = product.DownloadActivationType,
  				HasSampleDownload = product.HasSampleDownload,
  				SampleDownloadId = sampleDownloadId,
  				HasUserAgreement = product.HasUserAgreement,
  				UserAgreementText = product.UserAgreementText,
  				IsRecurring = product.IsRecurring,
  				RecurringCycleLength = product.RecurringCycleLength,
  				RecurringCyclePeriod = product.RecurringCyclePeriod,
  				RecurringTotalCycles = product.RecurringTotalCycles,
  				IsShipEnabled = product.IsShipEnabled,
  				IsFreeShipping = product.IsFreeShipping,
  				AdditionalShippingCharge = product.AdditionalShippingCharge,
  				IsEsd = product.IsEsd,
  				IsTaxExempt = product.IsTaxExempt,
  				TaxCategoryId = product.TaxCategoryId,
  				ManageInventoryMethod = product.ManageInventoryMethod,
  				StockQuantity = product.StockQuantity,
  				DisplayStockAvailability = product.DisplayStockAvailability,
  				DisplayStockQuantity = product.DisplayStockQuantity,
  				MinStockQuantity = product.MinStockQuantity,
  				LowStockActivityId = product.LowStockActivityId,
  				NotifyAdminForQuantityBelow = product.NotifyAdminForQuantityBelow,
  				BackorderMode = product.BackorderMode,
  				AllowBackInStockSubscriptions = product.AllowBackInStockSubscriptions,
  				OrderMinimumQuantity = product.OrderMinimumQuantity,
  				OrderMaximumQuantity = product.OrderMaximumQuantity,
  				AllowedQuantities = product.AllowedQuantities,
  				DisableBuyButton = product.DisableBuyButton,
  				DisableWishlistButton = product.DisableWishlistButton,
  				AvailableForPreOrder = product.AvailableForPreOrder,
  				CallForPrice = product.CallForPrice,
  				Price = product.Price,
  				OldPrice = product.OldPrice,
  				ProductCost = product.ProductCost,
  				SpecialPrice = product.SpecialPrice,
  				SpecialPriceStartDateTimeUtc = product.SpecialPriceStartDateTimeUtc,
  				SpecialPriceEndDateTimeUtc = product.SpecialPriceEndDateTimeUtc,
  				CustomerEntersPrice = product.CustomerEntersPrice,
  				MinimumCustomerEnteredPrice = product.MinimumCustomerEnteredPrice,
  				MaximumCustomerEnteredPrice = product.MaximumCustomerEnteredPrice,
  				LowestAttributeCombinationPrice = product.LowestAttributeCombinationPrice,
  				Weight = product.Weight,
  				Length = product.Length,
  				Width = product.Width,
  				Height = product.Height,
  				AvailableStartDateTimeUtc = product.AvailableStartDateTimeUtc,
  				AvailableEndDateTimeUtc = product.AvailableEndDateTimeUtc,
  				DisplayOrder = product.DisplayOrder,
                  Published = isPublished,
                  Deleted = product.Deleted,
  				CreatedOnUtc = utcNow,
  				UpdatedOnUtc = utcNow,
  				DeliveryTimeId = product.DeliveryTimeId,
                  QuantityUnitId = product.QuantityUnitId,
  				BasePriceEnabled = product.BasePriceEnabled,
  				BasePriceMeasureUnit = product.BasePriceMeasureUnit,
  				BasePriceAmount = product.BasePriceAmount,
  				BasePriceBaseAmount = product.BasePriceBaseAmount,
  				BundleTitleText = product.BundleTitleText,
  				BundlePerItemShipping = product.BundlePerItemShipping,
  				BundlePerItemPricing = product.BundlePerItemPricing,
  				BundlePerItemShoppingCart = product.BundlePerItemShoppingCart
              };
  
              _productService.InsertProduct(productCopy);
  
              //search engine name
              _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", productCopy.Name, true), 0);
  
              var languages = _languageService.GetAllLanguages(true);
  
              //localization
              foreach (var lang in languages)
              {
                  var name = product.GetLocalized(x => x.Name, lang.Id, false, false);
                  if (!String.IsNullOrEmpty(name))
                      _localizedEntityService.SaveLocalizedValue(productCopy, x => x.Name, name, lang.Id);
  
                  var shortDescription = product.GetLocalized(x => x.ShortDescription, lang.Id, false, false);
                  if (!String.IsNullOrEmpty(shortDescription))
                      _localizedEntityService.SaveLocalizedValue(productCopy, x => x.ShortDescription, shortDescription, lang.Id);
  
                  var fullDescription = product.GetLocalized(x => x.FullDescription, lang.Id, false, false);
                  if (!String.IsNullOrEmpty(fullDescription))
                      _localizedEntityService.SaveLocalizedValue(productCopy, x => x.FullDescription, fullDescription, lang.Id);
  
                  var metaKeywords = product.GetLocalized(x => x.MetaKeywords, lang.Id, false, false);
                  if (!String.IsNullOrEmpty(metaKeywords))
                      _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaKeywords, metaKeywords, lang.Id);
  
                  var metaDescription = product.GetLocalized(x => x.MetaDescription, lang.Id, false, false);
                  if (!String.IsNullOrEmpty(metaDescription))
                      _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaDescription, metaDescription, lang.Id);
  
                  var metaTitle = product.GetLocalized(x => x.MetaTitle, lang.Id, false, false);
                  if (!String.IsNullOrEmpty(metaTitle))
                      _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaTitle, metaTitle, lang.Id);
  
  				var bundleTitleText = product.GetLocalized(x => x.BundleTitleText, lang.Id, false, false);
  				if (!String.IsNullOrEmpty(bundleTitleText))
  					_localizedEntityService.SaveLocalizedValue(productCopy, x => x.BundleTitleText, bundleTitleText, lang.Id);
  
                  //search engine name
                  _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", name, false), lang.Id);
  
              }
  
              // product pictures
              if (copyImages)
              {
                  foreach (var productPicture in product.ProductPictures)
                  {
                      var picture = productPicture.Picture;
                      var pictureCopy = _pictureService.InsertPicture(
                          _pictureService.LoadPictureBinary(picture),
                          picture.MimeType, 
                          _pictureService.GetPictureSeName(newName), 
                          true,
  						false,
  						false);
                      _productService.InsertProductPicture(new ProductPicture
                      {
                          ProductId = productCopy.Id,
                          PictureId = pictureCopy.Id,
                          DisplayOrder = productPicture.DisplayOrder
                      });
                  }
              }
  
              // product <-> categories mappings
              foreach (var productCategory in product.ProductCategories)
              {
                  var productCategoryCopy = new ProductCategory()
                  {
                      ProductId = productCopy.Id,
                      CategoryId = productCategory.CategoryId,
                      IsFeaturedProduct = productCategory.IsFeaturedProduct,
                      DisplayOrder = productCategory.DisplayOrder
                  };
  
                  _categoryService.InsertProductCategory(productCategoryCopy);
              }
  
              // product <-> manufacturers mappings
              foreach (var productManufacturers in product.ProductManufacturers)
              {
                  var productManufacturerCopy = new ProductManufacturer()
                  {
                      ProductId = productCopy.Id,
                      ManufacturerId = productManufacturers.ManufacturerId,
                      IsFeaturedProduct = productManufacturers.IsFeaturedProduct,
                      DisplayOrder = productManufacturers.DisplayOrder
                  };
  
                  _manufacturerService.InsertProductManufacturer(productManufacturerCopy);
              }
  
              // product <-> releated products mappings
              foreach (var relatedProduct in _productService.GetRelatedProductsByProductId1(product.Id, true))
              {
                  _productService.InsertRelatedProduct(
                      new RelatedProduct()
                      {
                          ProductId1 = productCopy.Id,
                          ProductId2 = relatedProduct.ProductId2,
                          DisplayOrder = relatedProduct.DisplayOrder
                      });
              }
  
              // product <-> cross sells mappings
              foreach (var csProduct in _productService.GetCrossSellProductsByProductId1(product.Id, true))
              {
                  _productService.InsertCrossSellProduct(
                      new CrossSellProduct()
                      {
                          ProductId1 = productCopy.Id,
                          ProductId2 = csProduct.ProductId2,
                      });
              }
  
              // product specifications
              foreach (var productSpecificationAttribute in product.ProductSpecificationAttributes)
              {
                  var psaCopy = new ProductSpecificationAttribute()
                  {
                      ProductId = productCopy.Id,
                      SpecificationAttributeOptionId = productSpecificationAttribute.SpecificationAttributeOptionId,
                      AllowFiltering = productSpecificationAttribute.AllowFiltering,
                      ShowOnProductPage = productSpecificationAttribute.ShowOnProductPage,
                      DisplayOrder = productSpecificationAttribute.DisplayOrder
                  };
                  _specificationAttributeService.InsertProductSpecificationAttribute(psaCopy);
              }
  
  			//store mapping
  			var selectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(product);
  			foreach (var id in selectedStoreIds)
  			{
  				_storeMappingService.InsertStoreMapping(productCopy, id);
  			}
  
  			// product <-> attributes mappings
  			var associatedAttributes = new Dictionary<int, int>();
  			var associatedAttributeValues = new Dictionary<int, int>();
  			foreach (var productVariantAttribute in _productAttributeService.GetProductVariantAttributesByProductId(product.Id))
  			{
  
gt: 				var productVariantAttributeCopy = new ProductVariantAttribute()