package com.uca.flights;

import java.time.*;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import com.uca.bookings.Booking;

/**
 * Represent flight
 * @Author Cesano Ugo, Colmerauer Clément
 * 
 */
public class Flight {
    /** Flight identifier */
    private       FlightId           id;
    /** Flight departure date */
    private       ZonedDateTime      departure;
    /** Flight trip */
    private       Trip               trip;
    /** Flight list of booking */
    private       ArrayList<Booking> bookings;
    /** Flight reservability */
    private       boolean            isReservable;

    //Insert other relevent fields

    /**
     * Constructor
     * @param gen a flight id generator
     * @param dep the departure date
     * @param trip the trip
     * @throws IllegalArgumentException on null parameter
     */
    Flight(Company.FlightIdGenerator gen, ZonedDateTime dep, Trip trip)
    {
        if(gen == null)
        {
            throw new IllegalArgumentException("Flight id generator can't be null.");
        }
        if(dep == null)
        {
            throw new IllegalArgumentException("Departure date can't be null.");
        }
        if(trip == null)
        {
            throw new IllegalArgumentException("Trip can't be null.");
        }

        this.id           = gen.next();
        this.departure    = dep;
        this.trip         = trip;
        this.isReservable = false;
        this.bookings     = new ArrayList<Booking>();
    }

    /**
     * Getter
     * @return the flight identifier
     */
    public FlightId getId()
    {
        return this.id;
    }

    /**
     * Getter
     * @return the flight departure date
     */
    public ZonedDateTime getDeparture()
    {
        return this.departure;
    }

    /**
     * Getter
     * @return the flight arrival date
     */
    public ZonedDateTime getArrival()
    {
        return this.departure.plus(this.trip.getLastStep().getDuration());
    }

    /**
     * Getter
     * @return the flight last airport
     */
    public Airport getDestination()
    {
        return this.trip.getLastStep().getAirport();
    }

    /**
     * Getter
     * @return the flight first airport
     */
    public Airport getStartingAirport()
    {
        return this.trip.getStart().getDeparture().getAirport();
    }

    /**
     * Getter
     * @return the duration of the flight
     */
    public Duration getDuration()
    {
        return this.trip.getDuration();
    }

    /**
     * Getter
     * @return the flight trip
     */
    Trip getTrip()
    {
        return this.trip;
    }

    /**
     * Open the flight to reservation
     */
    void open()
    {
        this.isReservable = true;
    }

    /**
     * Close the flight to reservation
     */
    void close()
    {
        this.isReservable = false;
    }

    /**
     * Getter
     * @return a shallow copy of the list of booking concerning this
     */
    public List<Booking> getBookings()
    {
        return new ArrayList<Booking>(this.bookings);
    }

    /**
     * Add a booking to the flight
     * @param booking the booking
     * @throws IllegalArgumentException on null parameter, or booking for another flight
     */
    public void addBooking(Booking booking)
    {
        if(booking == null)
        {
            throw new IllegalArgumentException("Booking can't be null.");
        }
        if(booking.getFlight() != this)
        {
            throw new IllegalArgumentException("Booked for another flight.");
        }
        if(!this.bookings.contains(booking))
        {
            this.bookings.add(booking);
        }
    }

    /**
     * Add a booking to the flight
     * @param booking the booking
     * @throws IllegalArgumentException on null parameter, or booking for another flight
     */
    public void removeBooking(Booking booking)
    {
        if(booking == null)
        {
            throw new IllegalArgumentException("Booking can't be null.");
        }
        if(booking.getFlight() != this)
        {
            throw new IllegalArgumentException("Booked for another flight.");
        }
        if(this.bookings.contains(booking))
        {
            this.bookings.remove(booking);
            Booking.removeBooking(booking);
        }
    }



    /**
     * Getter
     * @return if flight is reservable
     */
    public boolean isReservable()
    {
        return this.isReservable;
    }

    /**
     * Delay the duration from another duration
     * @param duration the duration
     * @throws IllegalArgumentException on null parameter
     */
    public void delay(Duration duration)
    {
        if(duration == null)
        {
            throw new IllegalArgumentException("Duration can't be null.");
        }

        Trip t = (Trip)this.trip.clone();
        t.delay(duration);
    }

    /**
     * Delay the duration from another duration from a specific jump
     * @param duration the duration
     * @param j the jump
     * @throws IllegalArgumentException on null parameter
     */
    public void delay(Duration duration, Jump j)
    {
        if(duration == null)
        {
            throw new IllegalArgumentException("Duration can't be null.");
        }
        if(j == null)
        {
            throw new IllegalArgumentException("Jump can't be null.");
        }

        Trip t = (Trip)this.trip.clone();
        t.delay(duration,j);
    }  

    /**
     * Delay the duration from another duration from a specific step
     * @param duration the duration
     * @param s the step
     * @throws IllegalArgumentException on null parameter
     */
    public void delay(Duration duration, Step s)
    {
        if(duration == null)
        {
            throw new IllegalArgumentException("Duration can't be null.");
        }
        if(s == null)
        {
            throw new IllegalArgumentException("Step can't be null.");
        }

        Trip t = (Trip)this.trip.clone();
        t.delay(duration,s);
    }  

    /**
     * Getter
     * @return the first step
     */
    public Iterator<Step> getSteps()
    {
        return this.trip.getSteps();
    }

    /**
     * Getter
     * @return the first jump
     */
    public Iterator<Jump> getJumps()
    {
        return this.trip.getJumps();
    }

    /**
     * Redefinition of toString
     * @return a string with starting and last airport and duration in hours
     */
    @Override
    public String toString()
    {
        return "Flight " + this.id.toString() + " " + this.trip.toString() + " departure at " + this.departure.toString();
    }
}
