@SuppressWarnings("unchecked")
  @ResponseBody
  @RequestMapping(value = "/order/clearOrder.ajax", method = RequestMethod.POST)
  public ResponseEntity<byte[]> clearOrder(
      @RequestParam(value = "orderId") String orderId,
      @RequestParam(value = "restaurantId") String restaurantId)
      throws Exception {

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Clearing order for orderId: " + orderId);
    }

    Map<String, Object> model = new HashMap<String, Object>();

    try {
      Order order = orderRepository.findByOrderId(orderId);
      Restaurant restaurant = restaurantRepository.findByRestaurantId(restaurantId);
      order.setRestaurant(restaurant);
      order.getOrderItems().clear();
      order.getOrderDiscounts().clear();
      order = orderRepository.saveOrder(order);
      model.put("success", true);
      model.put("order", order);
    } catch (Exception ex) {
      LOGGER.error("", ex);
      model.put("success", false);
      model.put("message", ex.getMessage());
    }
    return buildOrderResponse(model);
  }
  @SuppressWarnings("unchecked")
  @ResponseBody
  @RequestMapping(value = "/order/getOrder.ajax", method = RequestMethod.POST)
  public ResponseEntity<byte[]> getOrder(HttpServletRequest request) throws Exception {

    Map<String, Object> model = new HashMap<String, Object>();

    try {
      HttpSession session = request.getSession(true);
      String orderId = (String) session.getAttribute("orderid");
      String restaurantId = (String) session.getAttribute("restaurantid");
      Order order = null;
      if (orderId != null) {
        order = orderRepository.findByOrderId(orderId);

        // If the current order has no items but is linked to another restauarant, update it now
        if (order.getOrderItems().size() == 0 && !order.getRestaurantId().equals(restaurantId)) {
          order.setRestaurant(restaurantRepository.findByRestaurantId(restaurantId));
        }
        order.updateCosts();

        // Update can checkout status of order
        session.setAttribute("cancheckout", order.getCanCheckout());
        session.setAttribute("cansubmitpayment", order.getCanSubmitPayment());
      }

      model.put("success", true);
      model.put("order", order);
    } catch (Exception ex) {
      LOGGER.error("", ex);
      model.put("success", false);
      model.put("message", ex.getMessage());
    }
    return buildOrderResponse(model);
  }
  @SuppressWarnings("unchecked")
  @ResponseBody
  @RequestMapping(value = "/order/updateItemQuantity.ajax", method = RequestMethod.POST)
  public ResponseEntity<byte[]> updateItemQuantity(
      HttpServletRequest request, @RequestParam(value = "body") String body) throws Exception {

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Updating order items: " + body);
    }

    Map<String, Object> model = new HashMap<String, Object>();

    try {
      // Extract request parameters
      Map<String, Object> params = (Map<String, Object>) jsonUtils.deserialize(body);
      String orderItemId = (String) params.get("orderItemId");
      Integer quantity = (Integer) params.get("quantity");

      HttpSession session = request.getSession(true);
      String orderId = (String) session.getAttribute("orderid");
      Order order = null;
      if (orderId != null) {
        order = orderRepository.findByOrderId(orderId);
        if (order != null) {
          order.updateItemQuantity(orderItemId, quantity);
          order = orderRepository.saveOrder(order);
          // Update can checkout status of order
          session.setAttribute("cancheckout", order.getCanCheckout());

          // Update order restaurant id session attribute if any items present
          if (order.getOrderItems().size() > 0) {
            session.setAttribute("orderrestaurantid", order.getRestaurantId());
            session.setAttribute("orderrestauranturl", order.getRestaurant().getUrl());
          } else {
            // If the restaurant session id does not match the order restaurant id, update the order
            String restaurantId = (String) session.getAttribute("restaurantid");
            if (!order.getRestaurantId().equals(restaurantId)) {
              Restaurant restaurant = restaurantRepository.findByRestaurantId(restaurantId);
              order.setRestaurant(restaurant);
              order = orderRepository.save(order);
            }
            session.removeAttribute("orderrestaurantid");
            session.removeAttribute("orderrestauranturl");
          }
        }
      }
      model.put("success", true);
      model.put("order", order);
    } catch (Exception ex) {
      LOGGER.error("", ex);
      model.put("success", false);
      model.put("message", ex.getMessage());
    }
    return buildOrderResponse(model);
  }
  @SuppressWarnings("unchecked")
  @ResponseBody
  @RequestMapping(value = "/order/updateFreeItem.ajax", method = RequestMethod.POST)
  public ResponseEntity<byte[]> updateFreeItem(
      HttpServletRequest request, @RequestParam(value = "body") String body) throws Exception {

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Updating free item: " + body);
    }

    Map<String, Object> model = new HashMap<String, Object>();

    try {
      // Extract request parameters
      Map<String, Object> params = (Map<String, Object>) jsonUtils.deserialize(body);
      String discountId = (String) params.get("discountId");
      String freeItem = (String) params.get("freeItem");

      HttpSession session = request.getSession(true);
      String orderId = (String) session.getAttribute("orderid");
      Order order = null;
      if (orderId != null) {
        order = orderRepository.findByOrderId(orderId);
        if (order != null) {
          OrderDiscount orderDiscount = order.getOrderDiscount(discountId);
          if (orderDiscount != null) {
            orderDiscount.setSelectedFreeItem(freeItem);
            order = orderRepository.save(order);
          }

          // Update order restaurant id session attribute if any items present
          if (order.getOrderItems().size() > 0) {
            session.setAttribute("orderrestaurantid", order.getRestaurantId());
            session.setAttribute("orderrestauranturl", order.getRestaurant().getUrl());
          } else {
            session.removeAttribute("orderrestaurantid");
            session.removeAttribute("orderrestauranturl");
          }

          // Update can checkout status of order
          session.setAttribute("cancheckout", order.getCanCheckout());
        }
      }

      model.put("success", true);
      model.put("order", order);
    } catch (Exception ex) {
      LOGGER.error("", ex);
      model.put("success", false);
      model.put("message", ex.getMessage());
    }
    return buildOrderResponse(model);
  }
  @SuppressWarnings("unchecked")
  @ResponseBody
  @RequestMapping(value = "/order/updateOrderDelivery.ajax", method = RequestMethod.POST)
  public ResponseEntity<byte[]> updateOrderDelivery(
      HttpServletRequest request, @RequestParam(value = "body") String body) throws Exception {

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Updating order delivery type");
    }

    Map<String, Object> model = new HashMap<String, Object>();

    try {
      // Extract request parameters
      Map<String, Object> params = (Map<String, Object>) jsonUtils.deserialize(body);
      String orderId = (String) params.get("orderId");
      String deliveryType = (String) params.get("deliveryType");
      Integer dayOffset = Integer.valueOf((String) params.get("dayIndex"));
      String time = (String) params.get("time");

      // Get the order and update the delivery type
      Order order = orderRepository.findByOrderId(orderId);
      order.setDeliveryType(deliveryType);

      // Clear existing expected delivery/collection times
      order.setExpectedCollectionTime(null);
      order.setExpectedDeliveryTime(null);

      // If the selected time is 'ASAP' no need to do any processing
      if (!"ASAP".equals(time)) {
        DateTime now = new DateTime().plusDays(dayOffset);
        MutableDateTime expectedDate =
            new MutableDateTime(
                now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), 0, 0, 0, 0);
        Integer hour = Integer.valueOf(time.split(":")[0]);
        Integer minute = Integer.valueOf(time.split(":")[1]);
        expectedDate.setHourOfDay(hour);
        expectedDate.setMinuteOfHour(minute);
        if (Order.DELIVERY.equals(deliveryType)) {
          order.setExpectedDeliveryTime(expectedDate.toDateTime());
        } else {
          order.setExpectedCollectionTime(expectedDate.toDateTime());
        }
      }

      order = orderRepository.saveOrder(order);

      // Update order restaurant id session attribute if any items present
      HttpSession session = request.getSession(true);
      if (order.getOrderItems().size() > 0) {
        session.setAttribute("orderrestaurantid", order.getRestaurantId());
        session.setAttribute("orderrestauranturl", order.getRestaurant().getUrl());
      } else {
        session.removeAttribute("orderrestaurantId");
        session.removeAttribute("orderrestauranturl");
      }

      // Update can checkout status of order
      session.setAttribute("cancheckout", order.getCanCheckout());

      // All worked ok
      model.put("success", true);
      model.put("order", order);
    } catch (Exception ex) {
      LOGGER.error("", ex);
      model.put("success", false);
      model.put("message", ex.getMessage());
    }
    return buildOrderResponse(model);
  }
  @SuppressWarnings("unchecked")
  @ResponseBody
  @RequestMapping(value = "/order/addSpecialOffer.ajax", method = RequestMethod.POST)
  public ResponseEntity<byte[]> addSpecialOfferToOrder(
      HttpServletRequest request, @RequestParam(value = "body") String body) throws Exception {

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Adding special offer to order: " + body);
    }

    Map<String, Object> model = new HashMap<String, Object>();

    try {

      // Extract request parameters
      Map<String, Object> params = (Map<String, Object>) jsonUtils.deserialize(body);
      String restaurantId = (String) params.get("restaurantId");
      String specialOfferId = (String) params.get("specialOfferId");
      List<String> itemChoices = (List<String>) params.get("itemChoices");
      List<String> itemChoiceCosts = (List<String>) params.get("itemChoiceCosts");
      Integer quantity = Integer.valueOf(params.get("quantity").toString());

      // Get the restaurant object
      Restaurant restaurant = restaurantRepository.findByRestaurantId(restaurantId);
      SpecialOffer specialOffer = restaurant.getSpecialOffer(specialOfferId);

      // Get the order out of the session
      HttpSession session = request.getSession(true);
      String orderId = (String) session.getAttribute("orderid");
      Order order;
      if (orderId == null) {
        order = buildAndRegister(session, restaurantId);
      } else {
        order = orderRepository.findByOrderId(orderId);
        if (order == null) {
          order = buildAndRegister(session, restaurantId);
        }
      }

      // Check if the special offer is applicable to this order
      if (!specialOffer.isApplicableTo(order)) {
        model.put("success", true);
        model.put("applicable", false);
      } else {
        // Wipe existing order if a new restaurant is selected
        if (!restaurantId.equals(order.getRestaurantId())) {
          order.setRestaurantId(restaurantId);
          order.setRestaurant(restaurant);
          order.getOrderItems().clear();
          order.getOrderDiscounts().clear();
        }

        // Build new order item
        OrderItem orderItem = new OrderItem();
        orderItem.setMenuItemNumber(specialOffer.getNumber());
        orderItem.setMenuItemId(specialOfferId);
        orderItem.setMenuItemTitle(specialOffer.getTitle());
        orderItem.setAdditionalItems(itemChoices);
        orderItem.setQuantity(quantity);
        double additionalCost = 0d;
        for (String itemChoiceCost : itemChoiceCosts) {
          additionalCost += Double.valueOf(itemChoiceCost);
        }
        orderItem.setCost(specialOffer.getCost() + additionalCost);

        // Add new order item to order and update
        order.addOrderItem(orderItem);
        order = orderRepository.saveOrder(order);

        // Update can checkout status of order
        session.setAttribute("cancheckout", order.getCanCheckout());

        // Update order restaurant id session attribute if any items present
        if (order.getOrderItems().size() > 0) {
          session.setAttribute("orderrestaurantid", order.getRestaurantId());
          session.setAttribute("orderrestauranturl", order.getRestaurant().getUrl());
        } else {
          session.removeAttribute("orderrestaurantId");
          session.removeAttribute("orderrestauranturl");
        }

        // Return success
        model.put("success", true);
        model.put("applicable", true);
        model.put("order", order);
      }
    } catch (Exception ex) {
      LOGGER.error("", ex);
      model.put("success", false);
      model.put("message", ex.getMessage());
    }
    return buildOrderResponse(model);
  }
  @SuppressWarnings("unchecked")
  @ResponseBody
  @RequestMapping(value = "/order/addItem.ajax", method = RequestMethod.POST)
  public ResponseEntity<byte[]> addToOrder(
      HttpServletRequest request, @RequestParam(value = "body") String body) throws Exception {

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Adding to order: " + body);
    }

    Map<String, Object> model = new HashMap<String, Object>();

    try {
      // Extract request parameters
      Map<String, Object> params = (Map<String, Object>) jsonUtils.deserialize(body);
      String restaurantId = (String) params.get("restaurantId");
      String itemId = (String) params.get("itemId");
      String itemType = (String) params.get("itemType");
      String itemSubType = (String) params.get("itemSubType");
      List<String> additionalItems = (List<String>) params.get("additionalItems");
      Integer quantity = Integer.valueOf(params.get("quantity").toString());

      // Get the restaurant object
      Restaurant restaurant = restaurantRepository.findByRestaurantId(restaurantId);
      MenuItem menuItem = restaurant.getMenuItem(itemId);

      // Build new order item
      OrderItem orderItem = new OrderItem();
      orderItem.setMenuItemNumber(menuItem.getNumber());
      orderItem.setMenuItemId(itemId);
      orderItem.setMenuItemTitle(menuItem.getTitle());
      orderItem.setMenuItemTypeName(itemType);
      orderItem.setMenuItemSubTypeName(itemSubType);
      orderItem.setAdditionalItems(additionalItems);
      orderItem.setQuantity(quantity);

      // Work out the cost of any additional Items
      double additionalItemCost = 0d;
      for (String additionalItemName : additionalItems) {
        if (StringUtils.hasText(itemType)) {
          MenuItemTypeCost menuItemTypeCost = menuItem.getMenuItemTypeCost(itemType);
          additionalItemCost +=
              menuItemTypeCost.getAdditionalItemCost() == null
                  ? 0d
                  : menuItemTypeCost.getAdditionalItemCost();
        } else if (menuItem.getAdditionalItemCost() != null) {
          additionalItemCost += menuItem.getAdditionalItemCost();
        } else {
          MenuItemAdditionalItemChoice additionalItemChoice =
              menuItem.getMenuItemAdditionalItemChoice(additionalItemName);
          additionalItemCost +=
              additionalItemChoice.getCost() == null ? 0d : additionalItemChoice.getCost();
        }
      }

      // Build the cost of the item
      if (StringUtils.hasText(itemType)) {
        MenuItemTypeCost menuItemTypeCost = menuItem.getMenuItemTypeCost(itemType);
        orderItem.setCost(menuItemTypeCost.getCost() + additionalItemCost);
      } else if (StringUtils.hasText(itemSubType)) {
        MenuItemSubType menuItemSubType = menuItem.getMenuItemSubType(itemSubType);
        orderItem.setCost(menuItemSubType.getCost() + additionalItemCost);
      } else {
        orderItem.setCost(menuItem.getCost() + additionalItemCost);
      }

      // Get the order out of the session
      HttpSession session = request.getSession(true);
      String orderId = (String) session.getAttribute("orderid");
      Order order;
      if (orderId == null) {
        order = buildAndRegister(session, restaurantId);
      } else {
        order = orderRepository.findByOrderId(orderId);
        if (order == null) {
          order = buildAndRegister(session, restaurantId);
        } else if (!restaurantId.equals(order.getRestaurantId())) {
          order.setRestaurant(restaurant);
          order.getOrderItems().clear();
          order.getOrderDiscounts().clear();
        }
      }

      // Add new order item to order and update
      order.addOrderItem(orderItem);
      order = orderRepository.saveOrder(order);

      // Update can checkout status of order
      session.setAttribute("cancheckout", order.getCanCheckout());

      // Update order restaurant id session attribute if any items present
      if (order.getOrderItems().size() > 0) {
        session.setAttribute("orderrestaurantid", order.getRestaurantId());
        session.setAttribute("orderrestauranturl", order.getRestaurant().getUrl());
      } else {
        session.removeAttribute("orderrestaurantid");
        session.removeAttribute("orderrestauranturl");
      }

      // Return success
      model.put("success", true);
      model.put("order", order);
    } catch (Exception ex) {
      LOGGER.error("", ex);
      model.put("success", false);
      model.put("message", ex.getMessage());
    }
    return buildOrderResponse(model);
  }