/// <summary>
        /// Saves an Order and it's Order Items
        /// </summary>
        /// <param name="orderDTO"></param>
        /// <returns>Returns the saved order and order items (including new Id's)</returns>
        public OrderDTO Save(OrderDTO orderDTO)
        {
            Order order;

            // transform an order DTO to an order (entity framework)

            if (orderDTO.Id == 0)
            {
                order = new Order { CreatedDate = orderDTO.CreatedDate };
                db.Orders.Add(order);
            }
            else
            {
                order = db.Orders.Find(orderDTO.Id);
                order.ModifiedDate = orderDTO.ModifiedDate;
            }

            order.Status = orderDTO.Status;

            foreach(OrderItemDTO orderItemDTO in orderDTO.OrderItems)
            {
                // transform an order item DTO to an order item (entity framework)

                OrderItem orderItem;

                if (orderItemDTO.Id == 0)
                {
                    orderItem = new OrderItem { CreateDate = orderItemDTO.CreateDate };
                    order.OrderItems.Add(orderItem);
                }
                else
                {
                    orderItem = order.OrderItems.First(i => i.Id == orderItemDTO.Id);
                    orderItem.ModifiedDate = orderItemDTO.ModifiedDate;
                }

                orderItem.ProductCode = orderItemDTO.ProductCode;
                orderItem.Price = orderItemDTO.Price;
                orderItem.PromotionApplied = orderItemDTO.PromotionApplied;
            }

            db.SaveChanges();

            // retrieve and return the saved dto (to get all Id's, including children)
            return Get(order.Id);
        }
        /// <summary>
        /// Get an Order by Id
        /// </summary>
        /// <param name="id"></param>
        /// <returns>Order and Order Items</returns>
        public OrderDTO Get(long id)
        {
            var result = db.Orders.Where(o => o.Id == id);

            if (result.Count() == 0)
                return null;

            Order order = result.ToList()[0];

            // transform the order DTO

            OrderDTO orderDTO = new OrderDTO()
            {
                Id = order.Id,
                CreatedDate = order.CreatedDate,
                ModifiedDate = order.ModifiedDate,
                Status = order.Status
            };

            // transform the order item DTOs

            orderDTO.OrderItems = new List<OrderItemDTO>();

            foreach(OrderItem orderItem in order.OrderItems)
            {
                OrderItemDTO orderItemDTO = new OrderItemDTO()
                {
                    Id = orderItem.Id,
                    CreateDate = orderItem.CreateDate,
                    ModifiedDate = orderItem.ModifiedDate,
                    OrderId = orderItem.OrderId,
                    Price = orderItem.Price,
                    ProductCode = orderItem.ProductCode,
                    PromotionApplied = orderItem.PromotionApplied 
                };
                orderDTO.OrderItems.Add(orderItemDTO);
            }

            return orderDTO;
        }
        public void ShouldCreateOrder()
        {

            // save the order

            OrderDTO orderDTO = new OrderDTO() { CreatedDate = DateTime.Now, Status = Constants.OrderStatus.Pending };

            orderDTO.OrderItems = new List<OrderItemDTO>();

            OrderItemDTO orderItemAppleDTO = new OrderItemDTO() { CreateDate = DateTime.Now, Price = 1, ProductCode = "apple" };
            orderDTO.OrderItems.Add(orderItemAppleDTO);

            OrderItemDTO orderItemNutellaDTO = new OrderItemDTO() { CreateDate = DateTime.Now, Price = 5, ProductCode = "nuttl" };
            orderDTO.OrderItems.Add(orderItemNutellaDTO);

            orderDTO = orderService.Save(orderDTO);

            Assert.IsTrue(orderDTO != null && orderDTO.Id > 0, "Order has not been saved");

            Assert.IsTrue(orderDTO.OrderItems != null && orderDTO.OrderItems.Count == 2, "Order items have not been saved");

        }
        public void CheckoutShoppingCart()
        {
            // get the shopping cart

            ShoppingCartDTO shoppingCart = GetShoppingCart();

            // get the list of products

            List<ProductDTO> products = productService.GetProducts();

            // turn the shopping cart into an order

            OrderDTO order = new OrderDTO { CreatedDate = DateTime.Now, Status = Constants.OrderStatus.Pending };
            order.OrderItems = new List<OrderItemDTO>();

            List<ShoppingCartItemDTO> orderedCart = shoppingCart.ShoppingCartItems.OrderBy(c => c.ProductCode).ToList();

            int productItemCount = 0;
            string lastProductCode = "";

            foreach (ShoppingCartItemDTO cartItem in orderedCart)
            {
                bool promotionApplied = false;

                // validate the product
                if (!products.Exists(p => p.Code == cartItem.ProductCode))
                {
                    Console.WriteLine("Product in cart is not valid: " + cartItem.ProductCode);
                    break;
                }

                // get the product

                ProductDTO product = products.First(p => p.Code == cartItem.ProductCode);

                if (product.Code != lastProductCode)
                {
                    productItemCount = 1;
                    lastProductCode = product.Code;
                }
                else
                    productItemCount++;

                // set the price (including a discount)

                decimal price = GetPrice(product, ref productItemCount, ref promotionApplied, order);

                // add the order item

                OrderItemDTO orderItem = new OrderItemDTO
                {
                    CreateDate = DateTime.Now,
                    ProductCode = cartItem.ProductCode,
                    Price = price,
                    PromotionApplied = promotionApplied
                };

                order.OrderItems.Add(orderItem);
            }

            if (order.OrderItems.Count == 0) return;

            // save the order

            order.Status = Constants.OrderStatus.Complete;

            order = orderService.Save(order);

            // print the invoice

            DisplayInvoice(order);

        }
        private void DisplayInvoice(OrderDTO order)
        {
            List<ProductDTO> products = productService.GetProducts();

            Console.WriteLine("".PadRight(30, '-') + "RECEIPT" + "".PadRight(30, '-'));

            Console.WriteLine();
            Console.WriteLine("Order Number: " + order.Id);
            Console.WriteLine("Date: " + order.CreatedDate.ToString());
            Console.WriteLine();
            Console.WriteLine();

            reportHandler.SetupColumns(30, 10, 30);

            reportHandler.AddColumns("Product", "Price", "Promotion Applied");

            reportHandler.AddColumns('-', "", "", "");

            decimal total = 0;

            foreach (OrderItemDTO orderItem in order.OrderItems)
            {
                ProductDTO product = products.First(p => p.Code == orderItem.ProductCode);

                reportHandler.AddColumns(product.Description, orderItem.Price.ToString("C"), 
                    (orderItem.PromotionApplied ? "Yes" : "No"));

                total += orderItem.Price;
            }

            reportHandler.AddRow();

            reportHandler.AddColumns("Total", total.ToString("C"));

            reportHandler.GenerateReport();

        }
        private decimal GetPrice(ProductDTO product, ref int productItemCount, ref bool promotionApplied, OrderDTO order)
        {
            // set the price (including a discount)

            decimal price = product.Price;

            if (product.Promotions.Count > 0)
            {
                // assume the service only return 1 active promotion
                PromotionDTO promotion = product.Promotions.First();

                if (promotion.PromotionCode == Constants.PromotionType.Discount)
                {
                    // we reset the productItemCount once the promotion reaches the max apply to count
                    if (promotion.ApplyTo != null && productItemCount > promotion.Quantity + promotion.ApplyTo)
                        productItemCount = 1;

                    // set the price by the percentage discount
                    if (productItemCount > promotion.Quantity)
                    {
                        price = price * promotion.Amount;
                        promotionApplied = true;
                    }
                }
                else if (promotion.PromotionCode == Constants.PromotionType.Price && productItemCount == promotion.Quantity)
                {
                    // for a price by a number of items, we set the price on the final item in the group, and set
                    // all other items in the group to a price of 0

                    for (int i = order.OrderItems.Count - 1; i >= order.OrderItems.Count - promotion.Quantity + 1; i--)
                    {
                        OrderItemDTO orderItemPriceAdjustment = order.OrderItems.ToList()[i];
                        orderItemPriceAdjustment.Price = 0;
                        orderItemPriceAdjustment.PromotionApplied = true;
                    }

                    price = promotion.Amount;
                    productItemCount = 0;
                    promotionApplied = true;
                }

            }

            return price;
        }