/*Returns the total cost of all items in the supplied order.*/
        public double getOrderCost(Order order)
        {
            double totalCost = 0;

            try
            {
                List <OrderLine> orderLines = new OrderLineService().getByOrder(order);            //Throws NoRecordException
                //set order total cost
                foreach (OrderLine orderLine in orderLines)
                {
                    if (orderLine.orderLineType.id == 1) //rent
                    {
                        TimeSpan duration = orderLine.enddate - orderLine.startdate;
                        totalCost += orderLine.dvdInfo.rent_price * duration.Days;
                    }
                    else if (orderLine.orderLineType.id == 2) //buy
                    {
                        totalCost += orderLine.dvdInfo.buy_price;
                    }
                }
            }
            catch (NoRecordException)
            {
            }
            return(Math.Round(totalCost, 2));
        }
        /*Returns a dictionary of (dvd_info_id, number_of_times_bought).*/
        public Dictionary <String, int> getItemsBoughtByCustomer(Customer customer)
        {
            Dictionary <String, int> dvdAmount = new Dictionary <String, int>();

            try
            {
                List <OrderLine> orderLines = new OrderLineService().getByCustomer(customer);

                foreach (OrderLine orderLine in orderLines)
                {
                    if (!dvdAmount.ContainsKey(orderLine.dvdInfo.dvd_info_id))
                    {
                        dvdAmount.Add(orderLine.dvdInfo.dvd_info_id, 1);
                    }
                    else
                    {
                        dvdAmount[orderLine.dvdInfo.dvd_info_id] = dvdAmount[orderLine.dvdInfo.dvd_info_id] + 1;
                    }
                }
            } catch (NoRecordException ex)
            {
                //user has never purchased anything
            }

            return(dvdAmount);
        }
        /*Returns the number of items the user has purchased.*/
        public int getNumberOfOrderLinesForCustomer(Customer customer)
        {
            List <OrderLine> orderLines;

            try
            {
                orderLines = new OrderLineService().getByCustomer(customer);
            }
            catch (NoRecordException ex)
            {
                //user has never purchased anything
                orderLines = new List <OrderLine>();
            }

            return(orderLines.Count);
        }
        /*Converts the customer's shopping cart to an order and returns it*/
        public Order createOrder(Customer user)
        {
            try
            {
                //get all items currently in cart
                List <ShoppingcartItem> cartContent = new ShoppingCartService().getCartContentForCustomer(user);           //Throws NoRecordException

                //create new order for this user
                int   orderID   = new OrderService().addOrderForCustomer(user);       //Throws NoRecordException
                Order thisOrder = new OrderService().getByID(orderID.ToString());

                OrderLineService orderLineService = new OrderLineService();

                //add cart items to newly created order
                foreach (ShoppingcartItem item in cartContent)
                {
                    OrderLine orderline = new OrderLine
                    {
                        order   = new OrderService().getByID(orderID.ToString()),                    //Throws NoRecordException
                        dvdInfo = new DvdInfoService().getByID(item.dvdInfo.dvd_info_id.ToString()), //Throws NoRecordException
                        //dvd_copy_id = copy.dvd_copy_id, //don't add a copy_id yet, only do this when a user has paid (id will be set in admin module)
                        startdate = item.startdate,
                        enddate   = item.enddate,
                        //order_line_type_id is verhuur/verkoop? dan kunnen we dat eigenlijk via join ophalen via dvd_copy tabel
                        orderLineType = new OrderLineTypeService().getByID(item.dvdCopyType.id.ToString())          //Throws NoRecordException
                    };

                    if (orderLineService.add(orderline))
                    {
                        //succes
                    }
                }

                //clear cart
                if (new ShoppingCartService().deleteByCustomer(user))
                {
                    //success
                }

                return(thisOrder);
            }
            catch (NoRecordException)
            {
                return(null);
            }
        }
        /*Returns the number of items currently being rented by the user.*/
        public int getNumberOfActiveRentOrdersCopiesForCustomer(Customer customer)
        {
            int numberOfCurrentlyRentedItems = 0;

            try
            {
                List <OrderLine> orderLines = new OrderLineService().getActiveRentOrderLinesByCustomer(customer);          //Throws NoRecordException
                foreach (OrderLine orderLine in orderLines)
                {
                    numberOfCurrentlyRentedItems++;
                }
            }
            catch (NoRecordException ex)
            {
                //user has no active rent orders
            }
            return(numberOfCurrentlyRentedItems);
        }
        /*Deletes the supplied orderLine from its order. Also deletes the order itself if it no longer contains any orderLines after deleting the supplied one.*/
        public bool deleteItemFromOrder(OrderLine orderLine)
        {
            Order order = orderLine.order;

            if (new OrderLineService().delete(orderLine))
            {
                try
                {
                    List <OrderLine> orderLines = new OrderLineService().getByOrder(order);
                }
                catch (NoRecordException ex)
                {
                    //this order no longer contains any orderlines, delete the entire order
                    new OrderService().delete(order);
                }

                return(true);
            }
            else
            {
                return(false);
            }
        }
        /*Sets the supplied order as paid and assigns a dvd copy to each orderLine (if possible).*/
        public void payOrder(Order selectedOrder)
        {
            //set the order status to PAID
            selectedOrder.orderstatus = new OrderStatusService().getByID("2");          //Throws NoRecordException
            new OrderService().updateOrder(selectedOrder);

            //assign copies to the orderlines
            List <OrderLine> orderLines = new OrderLineService().getByOrder(selectedOrder);            //Throws NoRecordException

            foreach (OrderLine orderLine in orderLines)
            {
                if (orderLine.orderLineType.id == 1) //rent copy
                {
                    DvdInfo thisDVD = orderLine.dvdInfo;

                    //get all unavailable dates starting from TODAY

                    Dictionary <int, List <DateTime> > dicCopyUnavailableDates = new AvailabilityModel().getAllUnavailableDaysPerCopyForDvdInfo(thisDVD, DateTime.Today); //Throws NoRecordException
                    Dictionary <int, int> dicDaysFreeForCopy = new Dictionary <int, int>();                                                                               //contains the number of free days for the dvdCopy (copy-id, freedays)

                    int selectedCopyId = -1;                                                                                                                              //id of the copy that will be assigned to this orderLine

                    //loop over the list of occupied dates per copy, check how many free dates exist around the chosen period, store that amount in dicDaysFreeForCopy
                    foreach (KeyValuePair <int, List <DateTime> > entry in dicCopyUnavailableDates)
                    {
                        //determine how many days the copy will be rented
                        int  rentPeriodDays  = (orderLine.enddate - orderLine.startdate).Days;
                        bool copyIsAvailable = true;

                        for (int i = 0; i <= rentPeriodDays; i++)
                        {
                            //loop over the occupied days, check if the copy is available on all of the requested dates
                            if (entry.Value.Contains(orderLine.startdate.AddDays(i)))
                            {
                                //one of the requested days is not free, this copy can not be used
                                copyIsAvailable = false;
                            }
                        }

                        if (copyIsAvailable)
                        {
                            //we now know this copy has the needed time available, now to determine the size of this free period
                            dicDaysFreeForCopy.Add(entry.Key, 0);

                            //check the amount of free days between start date and the first unavailable date (going backwards)
                            bool unavailableFound = false;
                            int  offset           = 0;
                            do
                            {
                                if (entry.Value.Contains(orderLine.startdate.AddDays(-offset)) || orderLine.startdate.AddDays(-offset) == DateTime.Today)
                                {
                                    //the copy is reserved on this date, break out of loop
                                    unavailableFound = true;
                                }
                                else
                                {
                                    dicDaysFreeForCopy[entry.Key]++;
                                    offset++;
                                }
                            } while (!unavailableFound);

                            //check the amount of free days between end date and the first unavailable date (going forward)
                            unavailableFound = false;
                            offset           = 0;
                            do
                            {
                                if (entry.Value.Contains(orderLine.enddate.AddDays(offset)) || orderLine.enddate.AddDays(offset) >= DateTime.Today.AddDays(14))
                                {
                                    //the copy is reserved on this date, break out of loop
                                    unavailableFound = true;
                                }
                                else
                                {
                                    dicDaysFreeForCopy[entry.Key]++;
                                    offset++;
                                }
                            } while (!unavailableFound);
                        }
                    }

                    int daysFree = 1000;

                    //loop over all copies, select the one with the smallest free window
                    foreach (KeyValuePair <int, int> entryDays in dicDaysFreeForCopy)
                    {
                        if (entryDays.Value < daysFree)
                        {
                            daysFree       = entryDays.Value;
                            selectedCopyId = entryDays.Key;
                        }
                    }

                    //assign the copy if it has been found
                    if (selectedCopyId > 0)
                    {
                        try
                        {
                            orderLine.dvdCopy = new DvdCopyService().getByID(selectedCopyId.ToString());            //Throws NoRecordException || DALException
                        }
                        catch (NoRecordException)
                        {
                            //No dvd's were found
                        }
                        new OrderLineService().updateOrderLine(orderLine);

                        //update the amount_sold field of the dvdInfo record
                        orderLine.dvdInfo.amount_sold = orderLine.dvdInfo.amount_sold + 1;
                        new DvdInfoService().update(orderLine.dvdInfo);                                                             //Throws NoRecordException
                    }
                    else
                    {
                        //no copies with future orderLines are available for this reservation/rent order
                        //get all fully available copies (=copies with no orderLines in the future) to assign to this orderline
                        try
                        {
                            List <DvdCopy> dvdCopies = new DvdCopyService().getAllFullyAvailableCopies(thisDVD, orderLine.startdate);      //Throws NoRecordException || DALException

                            //set the first available copy as the copy for this orderline
                            orderLine.dvdCopy = dvdCopies[0];
                            //update database
                            new OrderLineService().updateOrderLine(orderLine);
                            //update the amount_sold field of the dvdInfo record
                            dvdCopies[0].dvdinfo.amount_sold += 1;
                            new DvdInfoService().update(dvdCopies[0].dvdinfo);
                        }
                        catch (Exception ex)
                        {
                            //no copy can be assigned at this time, this means someone else received the last copy between the time where
                            //this user added the item to his cart and when he payed for the order.
                        }
                    }
                }
                else if (orderLine.orderLineType.id == 2) //sale copy
                {
                    //get all available copies
                    //get this list again for every item so you can add a new, available copy to the order
                    List <DvdCopy> availableCopies = new DvdCopyService().getAllInStockBuyCopiesForDvdInfo(orderLine.dvdInfo);           //Throws NoRecordException || DALException

                    //get the first available copy
                    //set the found copy as the copy for this orderline
                    orderLine.dvdCopy = availableCopies[0];
                    new OrderLineService().updateOrderLine(orderLine);

                    //update the amount_sold field of the dvdInfo record
                    DvdInfo dvdInfo = availableCopies[0].dvdinfo;
                    dvdInfo.amount_sold = dvdInfo.amount_sold + 1;
                    new DvdInfoService().update(dvdInfo);         //Throws NoRecordException

                    //mark the found copy as NOT in_stock
                    availableCopies[0].in_stock = false;
                    if (new DvdCopyService().updateCopy(availableCopies[0]))            //Throws NoRecordException || DALException
                    {
                        //Success, record updated
                    }
                }
            }
        }
        /**Returns a list of recommendations based on the user's order history.
         * 1) Gets all past orders and determines the user's 3 favourite genres.
         * 2) Gets a list of DVDs that have the most matches with the user's favourites.
         * A dvd that contains all 3 favourite genres will rank higher than a dvd that only matches 1 favourite genre.
         * 3) Removes dvds that the user has already rented or bought before.
         * 4) If no dvd's are left (or if the user had no orderlines), a list of DVDs is generated based on the pages the user has visited.
         */
        public List <DvdInfo> getRecommendations(Customer customer, int amount)
        {
            List <DvdInfo>   dvdList = new List <DvdInfo>();
            List <Genre>     genres  = new List <Genre>();
            List <OrderLine> orderLines;

            try
            {
                orderLines = new OrderLineService().getByCustomer(customer);          //Throws NoRecordException
            } catch (Exception error)
            {
                orderLines = new List <OrderLine>();
            }

            orderLinesDvdIds = new List <String>(); //list that contains the DVDids of the orderlines (=movies that the user has rented before)

            GenreService   genreService   = new GenreService();
            DvdInfoService dvdInfoService = new DvdInfoService();

            //customer has at least 1 order, base recommendations on orderLines
            if (orderLines.Count > 0)
            {
                foreach (OrderLine line in orderLines)
                {
                    String dvdID = line.dvdInfo.dvd_info_id;
                    genres.AddRange(genreService.getGenresForDvd(dvdID));            //Throws NoRecordException

                    //fill orderLinesDvdIds
                    if (!orderLinesDvdIds.Contains(dvdID))
                    {
                        orderLinesDvdIds.Add(dvdID);
                    }
                }

                //put genres in a dictionary (genreID, numberOfOccurrances)
                Dictionary <int, int> genreCountMap = new Dictionary <int, int>();
                foreach (Genre genre in genres)
                {
                    if (genreCountMap.ContainsKey(genre.genre_id))
                    {
                        genreCountMap[genre.genre_id]++;
                    }
                    else
                    {
                        genreCountMap.Add(genre.genre_id, 1);
                    }
                }

                //loop over dictionary and find the 3 most occuring genres
                int[] maxGenres = { 0, 0, 0 };
                int[] maxCounts = { 0, 0, 0 };

                foreach (KeyValuePair <int, int> pair in genreCountMap)
                {
                    if (maxCounts[0] < pair.Value)
                    {
                        maxCounts[2] = maxCounts[1];
                        maxCounts[1] = maxCounts[0];
                        maxCounts[0] = pair.Value;

                        maxGenres[2] = maxGenres[1];
                        maxGenres[1] = maxGenres[0];
                        maxGenres[0] = pair.Key;
                    }
                    else if (maxCounts[1] < pair.Value)
                    {
                        maxCounts[2] = maxCounts[1];
                        maxCounts[1] = pair.Value;

                        maxGenres[2] = maxGenres[1];
                        maxGenres[1] = pair.Key;
                    }
                    else if (maxCounts[2] < pair.Value)
                    {
                        maxCounts[2] = pair.Value;

                        maxGenres[2] = pair.Key;
                    }
                }

                //we now have the user's 3 favourite genres in maxGenres, use that info to get the dvds that match those 3 genres the most
                //get max 16 dvds. Index will always show maximum 4, but catalog can show up to 16
                List <String> dvdIds = dvdInfoService.getRecommendations(maxGenres, 16);            //Throws NoRecordException

                List <String> dvdTempIds = new List <String>();

                //only accept dvd's that the user had not watched yet
                foreach (String id in dvdIds)
                {
                    if (!orderLinesDvdIds.Contains(id))
                    {
                        dvdTempIds.Add(id);
                    }
                }

                if (dvdTempIds.Count > 0)
                {
                    foreach (String id in dvdTempIds)
                    {
                        dvdList.Add(dvdInfoService.getByID(id.ToString()));            //Throws NoRecordException
                    }
                }
                else
                {
                    //customer has already bought or rented every suggestion, recommend the movies whose pages the user has viewed most often
                    dvdList = getMostViewedDvdInfos(customer);          //Throws NoRecordException
                }
            }
            else
            {
                //customer has no orderlines, recommend the movies whose pages the user has viewed the most
                dvdList = getMostViewedDvdInfos(customer);              //Throws NoRecordException
            }

            //remove elements until the list size is the requested size (4 for index row, 16 for catalog page)
            //loop deletes a random item until the size is ok so the user gets different recommendations on every visit/refresh
            Random rnd = new Random();

            while (dvdList.Count > amount)
            {
                dvdList.RemoveAt(rnd.Next(dvdList.Count));
            }

            return(dvdList);
        }