package de.escalon.hypermedia.sample.store;
import de.escalon.hypermedia.action.Cardinality;
import de.escalon.hypermedia.action.Input;
import de.escalon.hypermedia.action.ResourceHandler;
import de.escalon.hypermedia.affordance.TypedResource;
import de.escalon.hypermedia.sample.beans.store.Offer;
import de.escalon.hypermedia.sample.beans.store.Order;
import de.escalon.hypermedia.sample.beans.store.Product;
import de.escalon.hypermedia.sample.beans.store.Store;
import de.escalon.hypermedia.sample.model.store.OrderModel;
import de.escalon.hypermedia.sample.model.store.OrderStatus;
import de.escalon.hypermedia.sample.model.store.ProductModel;
import de.escalon.hypermedia.spring.AffordanceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.ExposesResourceFor;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.List;
import static de.escalon.hypermedia.spring.AffordanceBuilder.linkTo;
import static de.escalon.hypermedia.spring.AffordanceBuilder.methodOn;
/**
* Created by Dietrich on 17.02.2015.
*/
@ExposesResourceFor(Order.class)
@RequestMapping("/orders")
@Controller
public class OrderController {
@Autowired
private OrderBackend orderBackend;
@Autowired
private OrderAssembler orderAssembler;
@Autowired
private ProductController productController;
@Autowired
private StoreController storeController;
@ResourceHandler(Cardinality.COLLECTION)
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Void> makeOrder(@Input(readOnly = {"productID"}) @RequestBody Product product) {
OrderModel orderModel = orderBackend.createOrder();
Product resolvedProduct = productController.getProduct(product.productID);
orderModel = orderBackend.addOrderedItem(orderModel.getId(),
new ProductModel(resolvedProduct.name, resolvedProduct.productID));
return redirectToUpdatedOrder(orderModel.getId());
}
@RequestMapping("/{orderId}")
public ResponseEntity<Order> getOrder(@PathVariable int orderId) {
OrderModel orderModel = orderBackend.getOrder(orderId);
Order order = orderAssembler.toResource(orderModel);
//TODO need to add additional action for delete to self rel
// offer extras for each product
List<? extends Product> items = order.getItems();
for (int i = 0; i < items.size(); i++) {
Product product = items.get(i);
Offer offer = new Offer();
if (!product
.hasExtra("9052006")) {
Offer shot = createAddOnOffer(product, "9052006", 0.2, orderId, i);
offer.addOn(shot);
}
if (!product.hasExtra("9052007")) {
Offer briocheCrema = createAddOnOffer(product, "9052007", 0.8, orderId, i);
offer.addOn(briocheCrema);
}
if (!offer.getAddOns()
.isEmpty()) {
product.addOffer(offer);
}
}
Store store = storeController.createStoreWithoutOffers();
store.add(linkTo(methodOn(this.getClass()).getOffersForOrder(orderId)).withRel("makesOffer"));
order.setSeller(store);
order.add(linkTo(methodOn(PaymentController.class).makePayment(orderId)).withRel("paymentUrl"));
return new ResponseEntity<Order>(order, HttpStatus.OK);
}
@RequestMapping(value = "/{orderId}/items/{orderedItemId}/accessories", method = RequestMethod.POST)
public ResponseEntity<Void> orderAccessory(@PathVariable int orderId, @PathVariable int orderedItemId,
@RequestBody @Input(readOnly = "productID") Product product) {
// TODO should write a productBackend to avoid this resolution nonsense:
Product resolvedProduct = productController.getProduct(product.productID);
orderBackend.orderAccessoryForOrderedItem(orderId, orderedItemId, new ProductModel(resolvedProduct.name,
resolvedProduct.productID));
return redirectToUpdatedOrder(orderId);
}
private ResponseEntity<Void> redirectToUpdatedOrder(int orderId) {
AffordanceBuilder location = linkTo(methodOn(this.getClass()).getOrder(orderId));
HttpHeaders headers = new HttpHeaders();
headers.setLocation(location.toUri());
return new ResponseEntity<Void>(headers, HttpStatus.SEE_OTHER);
}
@RequestMapping(value = "/{orderId}/items", method = RequestMethod.POST)
public ResponseEntity<Void> orderAdditionalItem(
@PathVariable int orderId, @RequestBody @Input(readOnly = "productID") Product product) {
Product resolvedProduct = productController.getProduct(product.productID);
OrderModel orderModel = orderBackend.addOrderedItem(orderId,
new ProductModel(resolvedProduct.name, resolvedProduct.productID));
return redirectToUpdatedOrder(orderId);
}
@RequestMapping(value = "/{orderId}/items/{orderedItemId}", method = RequestMethod.DELETE)
public ResponseEntity<Void> deleteOrderedItem(@PathVariable int orderId, @PathVariable int orderedItemId) {
orderBackend.deleteOrderedItem(orderId, orderedItemId);
return redirectToUpdatedOrder(orderId);
}
private Offer createAddOnOffer(Product product, String addOnProductID, double price, int orderId, int
orderedItemId) {
Offer addOnOffer = new Offer();
addOnOffer.setPriceCurrency(Currency.getInstance("EUR"));
addOnOffer.setPrice(BigDecimal.valueOf(price));
Product addOnProduct = productController.getProduct(addOnProductID);
addOnOffer.setItemOffered(addOnProduct);
String productSelfRel = product.getLink(Link.REL_SELF)
.getHref();
addOnProduct.add(linkTo(methodOn(OrderController.class).orderAccessory(orderId, orderedItemId, addOnProduct))
.reverseRel("isAccessoryOrSparePartFor", "extras", new TypedResource("Product", productSelfRel))
.build());
return addOnOffer;
}
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Resources<Order>> getOrders(@RequestParam(required = false) OrderStatus orderStatus) {
List<OrderModel> orders;
if (orderStatus == null) {
orders = orderBackend.getOrders();
} else {
orders = orderBackend
.getOrdersByStatus(orderStatus);
}
Resources<Order> orderResources = new Resources<Order>(
orderAssembler.toResources(orders));
return new ResponseEntity<Resources<Order>>(orderResources, HttpStatus.OK);
}
@RequestMapping("/{orderId}/offers")
public HttpEntity<Resources<Offer>> getOffersForOrder(@PathVariable int orderId) {
HttpEntity<Resources<Offer>> offers = storeController.getOffers();
for (Offer offer : offers.getBody()
.getContent()) {
Product itemOffered = offer.getItemOffered();
// we can determine the subject URI of the order
TypedResource subject = new TypedResource("Order",
linkTo(methodOn(this.getClass()).getOrder(orderId)).toString());
itemOffered.add(linkTo(methodOn(OrderController.class)
.orderAdditionalItem(orderId, itemOffered))
.rel(subject, "orderedItem")
.build());
}
return offers;
}
}