package com.salesmanager.web.shop.controller.product;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.salesmanager.core.business.catalog.category.service.CategoryService;
import com.salesmanager.core.business.catalog.product.model.Product;
import com.salesmanager.core.business.catalog.product.model.attribute.ProductAttribute;
import com.salesmanager.core.business.catalog.product.model.attribute.ProductOptionDescription;
import com.salesmanager.core.business.catalog.product.model.attribute.ProductOptionValue;
import com.salesmanager.core.business.catalog.product.model.attribute.ProductOptionValueDescription;
import com.salesmanager.core.business.catalog.product.model.price.FinalPrice;
import com.salesmanager.core.business.catalog.product.model.relationship.ProductRelationship;
import com.salesmanager.core.business.catalog.product.model.relationship.ProductRelationshipType;
import com.salesmanager.core.business.catalog.product.model.review.ProductReview;
import com.salesmanager.core.business.catalog.product.service.PricingService;
import com.salesmanager.core.business.catalog.product.service.ProductService;
import com.salesmanager.core.business.catalog.product.service.attribute.ProductAttributeService;
import com.salesmanager.core.business.catalog.product.service.relationship.ProductRelationshipService;
import com.salesmanager.core.business.catalog.product.service.review.ProductReviewService;
import com.salesmanager.core.business.merchant.model.MerchantStore;
import com.salesmanager.core.business.reference.language.model.Language;
import com.salesmanager.core.utils.CacheUtils;
import com.salesmanager.web.constants.Constants;
import com.salesmanager.web.entity.catalog.product.ReadableProduct;
import com.salesmanager.web.entity.catalog.product.ReadableProductPrice;
import com.salesmanager.web.entity.catalog.product.ReadableProductReview;
import com.salesmanager.web.entity.shop.Breadcrumb;
import com.salesmanager.web.entity.shop.PageInformation;
import com.salesmanager.web.populator.catalog.ReadableProductPopulator;
import com.salesmanager.web.populator.catalog.ReadableProductPricePopulator;
import com.salesmanager.web.populator.catalog.ReadableProductReviewPopulator;
import com.salesmanager.web.shop.controller.ControllerConstants;
import com.salesmanager.web.shop.model.catalog.Attribute;
import com.salesmanager.web.shop.model.catalog.AttributeValue;
import com.salesmanager.web.utils.BreadcrumbsUtils;
import com.salesmanager.web.utils.ImageFilePathUtils;
import com.salesmanager.web.utils.LabelUtils;
import com.salesmanager.web.utils.PageBuilderUtils;
import edu.emory.mathcs.backport.java.util.Arrays;
/**
* Populates the product details page
* @author Carl Samson
*
*/
@Controller
@RequestMapping("/shop/product")
public class ShopProductController {
@Autowired
private ProductService productService;
@Autowired
private ProductAttributeService productAttributeService;
@Autowired
private ProductRelationshipService productRelationshipService;
@Autowired
private PricingService pricingService;
@Autowired
private ProductReviewService productReviewService;
@Autowired
private LabelUtils messages;
@Autowired
private CacheUtils cache;
@Autowired
private CategoryService categoryService;
@Autowired
private BreadcrumbsUtils breadcrumbsUtils;
private static final Logger LOG = LoggerFactory.getLogger(ShopProductController.class);
/**
* Display product details with reference to caller page
* @param friendlyUrl
* @param ref
* @param model
* @param request
* @param response
* @param locale
* @return
* @throws Exception
*/
@RequestMapping("/{friendlyUrl}.html/ref={ref}")
public String displayProductWithReference(@PathVariable final String friendlyUrl, @PathVariable final String ref, Model model, HttpServletRequest request, HttpServletResponse response, Locale locale) throws Exception {
return display(ref, friendlyUrl, model, request, response, locale);
}
/**
* Display product details no reference
* @param friendlyUrl
* @param model
* @param request
* @param response
* @param locale
* @return
* @throws Exception
*/
@RequestMapping("/{friendlyUrl}.html")
public String displayProduct(@PathVariable final String friendlyUrl, Model model, HttpServletRequest request, HttpServletResponse response, Locale locale) throws Exception {
return display(null, friendlyUrl, model, request, response, locale);
}
public String display(final String reference, final String friendlyUrl, Model model, HttpServletRequest request, HttpServletResponse response, Locale locale) throws Exception {
MerchantStore store = (MerchantStore)request.getAttribute(Constants.MERCHANT_STORE);
Language language = (Language)request.getAttribute("LANGUAGE");
Product product = productService.getBySeUrl(store, friendlyUrl, locale);
if(product==null) {
return PageBuilderUtils.build404(store);
}
ReadableProductPopulator populator = new ReadableProductPopulator();
populator.setPricingService(pricingService);
ReadableProduct productProxy = populator.populate(product, new ReadableProduct(), store, language);
//meta information
PageInformation pageInformation = new PageInformation();
pageInformation.setPageDescription(productProxy.getDescription().getMetaDescription());
pageInformation.setPageKeywords(productProxy.getDescription().getKeyWords());
pageInformation.setPageTitle(productProxy.getDescription().getTitle());
pageInformation.setPageUrl(productProxy.getDescription().getFriendlyUrl());
request.setAttribute(Constants.REQUEST_PAGE_INFORMATION, pageInformation);
Breadcrumb breadCrumb = breadcrumbsUtils.buildProductBreadcrumb(reference, productProxy, store, language, request.getContextPath());
request.getSession().setAttribute(Constants.BREADCRUMB, breadCrumb);
request.setAttribute(Constants.BREADCRUMB, breadCrumb);
StringBuilder relatedItemsCacheKey = new StringBuilder();
relatedItemsCacheKey
.append(store.getId())
.append("_")
.append(Constants.RELATEDITEMS_CACHE_KEY)
.append("-")
.append(language.getCode());
StringBuilder relatedItemsMissed = new StringBuilder();
relatedItemsMissed
.append(relatedItemsCacheKey.toString())
.append(Constants.MISSED_CACHE_KEY);
Map<Long,List<ReadableProduct>> relatedItemsMap = null;
List<ReadableProduct> relatedItems = null;
if(store.isUseCache()) {
//get from the cache
relatedItemsMap = (Map<Long,List<ReadableProduct>>) cache.getFromCache(relatedItemsCacheKey.toString());
if(relatedItemsMap==null) {
//get from missed cache
//Boolean missedContent = (Boolean)cache.getFromCache(relatedItemsMissed.toString());
//if(missedContent==null) {
relatedItems = relatedItems(store, product, language);
if(relatedItems!=null) {
relatedItemsMap = new HashMap<Long,List<ReadableProduct>>();
relatedItemsMap.put(product.getId(), relatedItems);
cache.putInCache(relatedItemsMap, relatedItemsCacheKey.toString());
} else {
//cache.putInCache(new Boolean(true), relatedItemsMissed.toString());
}
//}
} else {
relatedItems = relatedItemsMap.get(product.getId());
}
} else {
relatedItems = relatedItems(store, product, language);
}
model.addAttribute("relatedProducts",relatedItems);
Set<ProductAttribute> attributes = product.getAttributes();
//split read only and options
Map<Long,Attribute> readOnlyAttributes = null;
Map<Long,Attribute> selectableOptions = null;
if(!CollectionUtils.isEmpty(attributes)) {
for(ProductAttribute attribute : attributes) {
Attribute attr = null;
AttributeValue attrValue = new AttributeValue();
ProductOptionValue optionValue = attribute.getProductOptionValue();
if(attribute.getAttributeDisplayOnly()==true) {//read only attribute
if(readOnlyAttributes==null) {
readOnlyAttributes = new TreeMap<Long,Attribute>();
}
attr = readOnlyAttributes.get(attribute.getProductOption().getId());
if(attr==null) {
attr = createAttribute(attribute, language);
}
if(attr!=null) {
readOnlyAttributes.put(attribute.getProductOption().getId(), attr);
attr.setReadOnlyValue(attrValue);
}
} else {//selectable option
if(selectableOptions==null) {
selectableOptions = new TreeMap<Long,Attribute>();
}
attr = selectableOptions.get(attribute.getProductOption().getId());
if(attr==null) {
attr = createAttribute(attribute, language);
}
if(attr!=null) {
selectableOptions.put(attribute.getProductOption().getId(), attr);
}
}
attrValue.setDefaultAttribute(attribute.getAttributeDefault());
attrValue.setId(attribute.getId());//id of the attribute
attrValue.setLanguage(language.getCode());
if(attribute.getProductAttributePrice()!=null && attribute.getProductAttributePrice().doubleValue()>0) {
String formatedPrice = pricingService.getDisplayAmount(attribute.getProductAttributePrice(), store);
attrValue.setPrice(formatedPrice);
}
if(!StringUtils.isBlank(attribute.getProductOptionValue().getProductOptionValueImage())) {
attrValue.setImage(ImageFilePathUtils.buildProductPropertyImageFilePath(store, attribute.getProductOptionValue().getProductOptionValueImage()));
}
List<ProductOptionValueDescription> descriptions = optionValue.getDescriptionsSettoList();
ProductOptionValueDescription description = null;
if(descriptions!=null && descriptions.size()>0) {
description = descriptions.get(0);
if(descriptions.size()>1) {
for(ProductOptionValueDescription optionValueDescription : descriptions) {
if(optionValueDescription.getLanguage().getId().intValue()==language.getId().intValue()) {
description = optionValueDescription;
break;
}
}
}
}
attrValue.setName(description.getName());
attrValue.setDescription(description.getDescription());
List<AttributeValue> attrs = attr.getValues();
if(attrs==null) {
attrs = new ArrayList<AttributeValue>();
attr.setValues(attrs);
}
attrs.add(attrValue);
}
}
List<ProductReview> reviews = productReviewService.getByProduct(product, language);
if(!CollectionUtils.isEmpty(reviews)) {
List<ReadableProductReview> revs = new ArrayList<ReadableProductReview>();
ReadableProductReviewPopulator reviewPopulator = new ReadableProductReviewPopulator();
for(ProductReview review : reviews) {
ReadableProductReview rev = new ReadableProductReview();
reviewPopulator.populate(review, rev, store, language);
revs.add(rev);
}
model.addAttribute("reviews", revs);
}
List<Attribute> attributesList = null;
if(readOnlyAttributes!=null) {
attributesList = new ArrayList<Attribute>(readOnlyAttributes.values());
}
List<Attribute> optionsList = null;
if(selectableOptions!=null) {
optionsList = new ArrayList<Attribute>(selectableOptions.values());
}
model.addAttribute("attributes", attributesList);
model.addAttribute("options", optionsList);
model.addAttribute("product", productProxy);
/** template **/
StringBuilder template = new StringBuilder().append(ControllerConstants.Tiles.Product.product).append(".").append(store.getStoreTemplate());
return template.toString();
}
@RequestMapping(value={"/{productId}/calculatePrice.html"}, method=RequestMethod.POST)
public @ResponseBody
ReadableProductPrice calculatePrice(@RequestParam(value="attributeIds[]") Long[] attributeIds, @PathVariable final Long productId, final HttpServletRequest request, final HttpServletResponse response, final Locale locale) throws Exception {
MerchantStore store = (MerchantStore)request.getAttribute(Constants.MERCHANT_STORE);
Language language = (Language)request.getAttribute("LANGUAGE");
Product product = productService.getById(productId);
@SuppressWarnings("unchecked")
List<Long> ids = new ArrayList<Long>(Arrays.asList(attributeIds));
List<ProductAttribute> attributes = productAttributeService.getByAttributeIds(store, ids);
for(ProductAttribute attribute : attributes) {
if(attribute.getProduct().getId().longValue()!=productId.longValue()) {
return null;
}
}
FinalPrice price = pricingService.calculateProductPrice(product, attributes);
ReadableProductPrice readablePrice = new ReadableProductPrice();
ReadableProductPricePopulator populator = new ReadableProductPricePopulator();
populator.setPricingService(pricingService);
populator.populate(price, readablePrice, store, language);
return readablePrice;
}
private Attribute createAttribute(ProductAttribute productAttribute, Language language) {
Attribute attribute = new Attribute();
attribute.setId(productAttribute.getProductOption().getId());//attribute of the option
attribute.setType(productAttribute.getProductOption().getProductOptionType());
List<ProductOptionDescription> descriptions = productAttribute.getProductOption().getDescriptionsSettoList();
ProductOptionDescription description = null;
if(descriptions!=null && descriptions.size()>0) {
description = descriptions.get(0);
if(descriptions.size()>1) {
for(ProductOptionDescription optionDescription : descriptions) {
if(optionDescription.getLanguage().getId().intValue()==language.getId().intValue()) {
description = optionDescription;
break;
}
}
}
}
if(description==null) {
return null;
}
attribute.setType(productAttribute.getProductOption().getProductOptionType());
attribute.setLanguage(language.getCode());
attribute.setName(description.getName());
attribute.setCode(productAttribute.getProductOption().getCode());
return attribute;
}
private List<ReadableProduct> relatedItems(MerchantStore store, Product product, Language language) throws Exception {
ReadableProductPopulator populator = new ReadableProductPopulator();
populator.setPricingService(pricingService);
List<ProductRelationship> relatedItems = productRelationshipService.getByType(store, product, ProductRelationshipType.RELATED_ITEM);
if(relatedItems!=null && relatedItems.size()>0) {
List<ReadableProduct> items = new ArrayList<ReadableProduct>();
for(ProductRelationship relationship : relatedItems) {
Product relatedProduct = relationship.getRelatedProduct();
ReadableProduct proxyProduct = populator.populate(relatedProduct, new ReadableProduct(), store, language);
items.add(proxyProduct);
}
return items;
}
return null;
}
}