using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;
using PizzaDAO;
using PizzaModel;
// Stateless singleton object for admin part of service API.
// Each call to a service method gets a new context and transaction.
// If an exception (unhandled within the scope) occurs in the transaction
// scope, the transaction is automatically rolled back. The actual commit
// happens at the } at the end of the transaction scope.
// Since the catch is not needed for rollback, we could let the
// exception go to the presentation layer, but by catching it and
// re-throwing, we can attach an error message to it.
// All this repetitive code could be hidden away by using
// an interceptor via AOP.
namespace PizzaService
{
    public class AdminService
    {
        public AdminService()
        {
        }
        public int CurrentDay
        {
            get
            {
                int day;
                using (PizzaEntities context = new PizzaEntities())
                {
                    AdminDAO adminDAO = new AdminDAO(context);
                    try
                    {
                        using (TransactionScope tx = new TransactionScope())
                        {
                            day = adminDAO.FindCurrentDay();
                            tx.Complete();
                        } // tx commit here, can fail, cause exception
                        return day;
                    }
                    catch (Exception e)
                    {
                        throw new ApplicationException("DB access failed"
                             + e.InnerException.Message, e);
                    }
                }
            }
        }
        public void AddPizzaSize(String sizeName)
        {
            using (PizzaEntities context = new PizzaEntities())
            {
                PizzaOrderDAO orderDAO = new PizzaOrderDAO(context);
                AdminDAO adminDAO = new AdminDAO(context);
                try
                {
                    using (TransactionScope tx = new TransactionScope())
                    {
                        adminDAO.AddPizzaSize(sizeName);
                        context.SaveChanges();
                        tx.Complete();
                    }
                }
                catch (Exception e)
                {
                    throw new ApplicationException("AddPizzaSize: DB problem"
                        + e.InnerException.Message, e);
                }
            }
        }
        public void AddTopping(String toppingName)
        {
            using (PizzaEntities context = new PizzaEntities())
            {
                PizzaOrderDAO orderDAO = new PizzaOrderDAO(context);
                AdminDAO adminDAO = new AdminDAO(context);
                try
                {
                    using (TransactionScope tx = new TransactionScope())
                    {
                        adminDAO.AddTopping(toppingName);
                        context.SaveChanges();
                        tx.Complete();
                    }
                }
                catch (Exception e)
                {
                    throw new ApplicationException("AddTopping: DB problem: "
                    + e.InnerException.Message, e);
                }
            }
        }
        public void DeleteTopping(String toppingID)
        {
            using (PizzaEntities context = new PizzaEntities())
            {
                PizzaOrderDAO orderDAO = new PizzaOrderDAO(context);
                AdminDAO adminDAO = new AdminDAO(context);
                try
                {
                    using (TransactionScope tx = new TransactionScope())
                    {
                        Topping top = orderDAO.FindToppingByID(toppingID);
                        adminDAO.DeleteTopping(top);
                        context.SaveChanges();
                        tx.Complete();
                    }
                }
                catch (Exception e)
                {
                    throw new ApplicationException("DeleteTopping: DB problem: "
                       + e.InnerException.Message, e);
                }

            }

        }
        public void DeletePizzaSize(String sizeID)
        {
            using (PizzaEntities context = new PizzaEntities())
            {
                PizzaOrderDAO orderDAO = new PizzaOrderDAO(context);
                AdminDAO adminDAO = new AdminDAO(context);
                try
                {
                    using (TransactionScope tx = new TransactionScope())
                    {
                        PizzaSize size = orderDAO.FindPizzaSizeByID(sizeID);
                        adminDAO.DeletePizzaSize(size);
                        context.SaveChanges();
                        tx.Complete();
                    }
                }
                catch (Exception e)
                {
                    throw new ApplicationException("DeletePizzaSize: DB problem: "
                          + e.InnerException.Message, e);
                }
            }
        }
        // find the oldest pizza (lowest order id) that is now marked "Preparing"
        // and make it marked Baked
        public void MarkNextOrderReady()
        {
            using (PizzaEntities context = new PizzaEntities())
            {
                PizzaOrderDAO orderDAO = new PizzaOrderDAO(context);
                AdminDAO adminDAO = new AdminDAO(context);
                try
                {
                    using (TransactionScope tx = new TransactionScope())
                    {

                        PizzaOrder order =
                            orderDAO.FindFirstOrder(
                                (int)PizzaOrder.OrderStatus.Preparing);
                        order.Status = (int)PizzaOrder.OrderStatus.Baked;
                        context.SaveChanges();
                        tx.Complete();
                    }
                }
                catch (System.Data.ObjectNotFoundException e)
                {
                    throw new ApplicationException("no orders there for today", e);
                }
                catch (Exception e)
                {
                    throw new ApplicationException(
                        "Error in marking the next order ready" 
                        +e.InnerException.Message, e);
                }
            }
        }
        // advance day for end-of-day work: finish the pizzas (throw them away),
        // advance the day number
        public void AdvanceDay()
        {
            using (PizzaEntities context = new PizzaEntities())
            {
                PizzaOrderDAO orderDAO = new PizzaOrderDAO(context);
                AdminDAO adminDAO = new AdminDAO(context);
                try
                {
                    using (TransactionScope tx = new TransactionScope())
                    {

                        int today = adminDAO.FindCurrentDay();
                        IEnumerable<PizzaOrder> pizzaOrders =
                            orderDAO.FindOrdersByDays(today, today);
                        // day is done, so mark today's pizzas as "finished"
                        foreach (PizzaOrder order in pizzaOrders)
                        {
                            order.Status = (int)PizzaOrder.OrderStatus.Finished;
                        }
                        adminDAO.AdvanceDay();
                        context.SaveChanges();
                        tx.Complete();
                    }
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Unsuccessful advance day"
                         + e.InnerException.Message, e);
                }
            }
        }
        // get report on today's pizzas
        public List<PizzaOrder> GetDailyReport()
        {
            using (PizzaEntities context = new PizzaEntities())
            {
                PizzaOrderDAO orderDAO = new PizzaOrderDAO(context);
                AdminDAO adminDAO = new AdminDAO(context);
                List<PizzaOrder> orders;
                try
                {
                    using (TransactionScope tx = new TransactionScope())
                    {
                        int today = adminDAO.FindCurrentDay();
                        // make sure orders are put in objects during Tx: run query
                        orders =
                            orderDAO.FindOrdersByDays(today, today).ToList<PizzaOrder>();   
                        tx.Complete();
                    }
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Error while getting daily report"
                         + e.InnerException.Message, e);
                }
                return orders;
            }
        }
        // get report since last admin report
        public List<PizzaOrder> GetAdminReport()
        {
            using (PizzaEntities context = new PizzaEntities())
            {
                List<PizzaOrder> report;
                PizzaOrderDAO orderDAO = new PizzaOrderDAO(context);
                AdminDAO adminDAO = new AdminDAO(context);
                try
                {
                    using (TransactionScope tx = new TransactionScope())
                    {
                        int prevLastReportDay = adminDAO.FindLastReportDay();
                        int today = adminDAO.FindCurrentDay();
                        report =
                            orderDAO.FindOrdersByDays(prevLastReportDay + 1, today).ToList<PizzaOrder>();
                        if (today > prevLastReportDay)
                            adminDAO.UpdateLastReportDay(today);	// advance past reported days
                        context.SaveChanges();
                        tx.Complete();
                    }
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Error in admin report"
                         + e.InnerException.Message, e);
                }
                return report;
            }
        }
    }
}