package com.kpl.CourierMgmt.CourierManager;
import com.kpl.CourierMgmt.GoogleMapClient.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

public class Scheduler
{
    private IGoogleMapApi mapApi;
    private IFareCalculator fareCalculator;
    private IPremiumCalculator premiumCalculator;

    /// <summary>
    /// Injectable constructor
    /// </summary>

    public Scheduler(IGoogleMapApi gma, IFareCalculator fc, IPremiumCalculator pc)
    {
        mapApi = gma;
        fareCalculator = fc;
        premiumCalculator = pc;
        Couriers = new ArrayList<Courier>();
    }

    /// <summary>
    /// The set of couriers registered in the system
    /// </summary>

    public List<Courier> Couriers; 

    /// <summary>
    /// Create a new courier at the given starting
    /// location and with the specified name
    /// </summary>
    /// <param name="name">The courier's name</param>
    /// <param name="startLocation">Where the courier starts from</param>
    /// <returns>The new courier object</returns>

    public Courier AddCourier(String name, String startLocation)
    {
        Couriers.add(new Courier(mapApi, name, startLocation));
        return Couriers.get(Couriers.size()-1);
    }

    /// <summary>
    /// Remove a courier from the set of couriers by name.
    /// NO further journeys will be allocated to this courier.
    /// </summary>
    /// <param name="name">The name of the courier to be removed</param>
    /// <returns>The courier being removed</returns>

    public Courier RemoveCourier(String name)
    {
    	Courier removalCandidate = null;
    	for(Courier c : Couriers)
    	{
    		if(c.Name.equals(name)) {
    			removalCandidate = c;
    			break;
    		}
    	}
        
        if (removalCandidate != null)
            Couriers.remove(removalCandidate);
        return removalCandidate;
    }
    
    /// <summary>
    /// The list of bookings in order of being placed
    /// </summary>

    public List<CourierBooking> GetBookings() {
    	List<CourierBooking> bookings = new ArrayList<CourierBooking>();
    	for(Courier c : Couriers)
    		for(CourierBooking cb : c.Schedule)
    			bookings.add(cb);
    	return bookings;
    }

    /// <summary>
    /// The list of bookings along with their respective assigned couriers
    /// </summary>

    public List<BookingDetails> GetBookingDetails() {
    	List<BookingDetails> bookings = new ArrayList<BookingDetails>();
    	for(Courier c : Couriers)
    		for(CourierBooking cb : c.Schedule)
    			bookings.add(new BookingDetails(c, cb));
    	return bookings;
    }

    /// <summary>
    /// Given a specific booking, find the booking details record that corresponds to it
    /// </summary>
    /// <param name="cb">The courier booking whose details we want</param>
    /// <returns>The details record for the booking</returns>

    public BookingDetails DetailsFromBooking(CourierBooking cb)
    {
    	for(BookingDetails bd : GetBookingDetails())
    		if(bd.Booking == cb)
    			return bd;
    	return null;
    }
    
    /// <summary>
    /// Which courier could arrive at a specified location first
    /// </summary>
    /// <param name="location">The desired location</param>
    /// <returns>The nearest courier who could get there first</returns>

    public Courier Closest(String location)
    {
    	Instant earliest = Instant.MAX;
    	Courier closest = null;
    	for(Courier c : Couriers) {
    		Instant i = c.GetTimeWhenNextFree();
    		if(i.isBefore(earliest)) {
    			earliest = i;
    			closest = c;
    		}
    	}
        return closest;
    }

    /// <summary>
    /// The main user story entry point for making a courier booking
    /// </summary>
    /// <param name="shippingRef">A string meaningful to the booker 
    /// that identifies the shipment</param>
    /// <param name="origin">The pickup point</param>
    /// <param name="destination">The drop-off point</param>
    /// <returns>The booking details</returns>

    public CourierBooking MakeBooking(String shippingRef, String origin, String destination, double weight)
    {
        // First find the nearest courier to the origin, i.e. the one who would be free first

        Courier closestCourier = Closest(origin);

        // Now schedule that courier to take this new booking. First define the route.

        IGoogleRoute route = mapApi.GetRoute(origin, destination);
        if (route.GetStatus() != "OK")
            throw new IllegalArgumentException("Route not found: " + route.GetStatus());
        CourierBooking newBooking = new CourierBooking(weight, route, fareCalculator, premiumCalculator);
        closestCourier.TakeBooking(newBooking);
        newBooking.ShippingRef = shippingRef;
        return newBooking;
    }
}
