package com.uca.flights;

import java.util.Collection;
import java.util.ArrayList;
import java.util.HashMap;
import java.time.ZonedDateTime;

/**
 * Represent a flight company
 * @Author Cesano Ugo, Colmerauer Clément
 * 
 */
public class Company {
    /** Company name*/
    private String                   name;
    /** Company prefix*/
    private String                   prefix;
    /** Company list of flight*/
    private HashMap<FlightId,Flight> flights;
    /** Company personal flight id generator*/
    private FlightIdGenerator        gen;
    /** Company registry of regular flight*/
    private RegularFlightRegistry    regularsFlights;

    //Insert other relevent fields

    /**
     * Constructor
     * @param prefix the company prefix
     * @param name the company name
     * @throws IllegalArgumentException on null or empty parameter
     */
    public Company(String prefix, String name) 
    {
        if(prefix == null)
        {
            throw new IllegalArgumentException("Prefix can't be null.");
        }
        if(prefix.trim().isEmpty())
        {
            throw new IllegalArgumentException("Prefix can't be uniquely space characters.");
        }
        if(name == null)
        {
            throw new IllegalArgumentException("Name can't be null.");
        }
        if(name.trim().isEmpty())
        {
            throw new IllegalArgumentException("Name can't be uniquely space characters.");
        }

        this.name            = name;
        this.prefix          = prefix;
        this.gen             = new FlightIdGenerator(this.prefix);
        this.flights         = new HashMap<FlightId,Flight>();
        this.regularsFlights = new RegularFlightRegistry();
    }

    /**
     * Getter
     * @return the company name
     */
    public String getName() 
    {
        return name;
    }
    
    /**
     * Setter
     * @param name the company name
     * @throws IllegalArgumentException on null or empty parameter
     */
    public void setName(String name) 
    {
        if(name == null)
        {
            throw new IllegalArgumentException("Name can't be null.");
        }
        if(name.trim().isEmpty())
        {
            throw new IllegalArgumentException("Name can't be uniquely space characters.");
        }
        this.name = name;
    }

    /**
     * Getter
     * @return the company flight ids
     */
    public ArrayList<FlightId> getFlights() 
    {
        return new ArrayList<FlightId>(this.flights.keySet());
    }

    /**
     * Getter
     * @param id the flight id
     * @return the flight associated
     * @throws IllegalArgumentException on null parameter
     */
    public Flight getFlight(FlightId id)
    {
        if(id == null)
        {
            throw new IllegalArgumentException("Flight id can't be null.");
        }
        if(!this.flights.containsKey(id))
        {
            throw new IllegalArgumentException("This flight " + id.toString() + "isn't referenced for this company " + this.toString() + ".");
        }
        return this.flights.get(id);
    }

    /**
     * Create and add a flight
     * @param trip the flight trip as a tripbuilder
     * @param departure the flight departure date
     * @throws IllegalArgumentException on null parameter
     */
    public void addFlight(ZonedDateTime departure,TripBuilder trip)
    {
        if(departure == null)
        {
            throw new IllegalArgumentException("Departure date can't be null.");
        }
        if(trip == null)
        {
            throw new IllegalArgumentException("Tripbuilder can't be null.");
        }
        this.addFlight(departure, trip.toTrip());
    }
    
    /**
     * Create and add a flight
     * @param trip the flight trip
     * @param departure the flight departure date
     * @throws IllegalArgumentException on null parameter
     */
    public void addFlight(ZonedDateTime departure,Trip trip)
    {
        if(departure == null)
        {
            throw new IllegalArgumentException("Departure date can't be null.");
        }
        if(trip == null)
        {
            throw new IllegalArgumentException("Trip can't be null.");
        }
        Flight f = new Flight(this.gen,departure,trip);
        this.flights.put(f.getId(),f);
    }

    /**
     * Create and add a regular flight from the registry
     * @param i regular flight index in the registry
     * @param departure the flight departure date
     * @throws IllegalArgumentException on null parameter or negative index
     */
    public void addRegularFlight(ZonedDateTime departure,int i)
    {
        if(departure == null)
        {
            throw new IllegalArgumentException("Departure date can't be null.");
        }
        if(i<0)
        {
            throw new IllegalArgumentException("Not a valid regular flight index.");
        }
        this.addFlight(departure, this.regularsFlights.get(i));
    }

    /**
     * Remove a flight
     * @param id the flight id
     * @throws IllegalArgumentException on null or non existent parameter
     */
    public void removeFlight(FlightId id){
        if(id == null)
        {
            throw new IllegalArgumentException("Flight id can't be null.");
        }
        if(!this.flights.containsKey(id))
        {
            throw new IllegalArgumentException("This flight " + id.toString() + "isn't referenced for this company " + this.toString() + ".");
        }
        this.flights.remove(id);
    }

    /**
     * Open a flight to reservation
     * @param id the flight id
     * @throws IllegalArgumentException on null or non existent parameter
     */
    public void open(FlightId id)
    {
        if(id == null)
        {
            throw new IllegalArgumentException("Flight id can't be null.");
        }
        if(!this.flights.containsKey(id))
        {
            throw new IllegalArgumentException("This flight " + id.toString() + "isn't referenced for this company " + this.toString() + ".");
        }
        this.flights.get(id).open();
    }

    /**
     * Close a flight to reservation
     * @param id the flight id
     * @throws IllegalArgumentException on null or non existent parameter
     */
    public void close(FlightId id)
    {
        if(id == null)
        {
            throw new IllegalArgumentException("Flight id can't be null.");
        }
        if(!this.flights.containsKey(id))
        {
            throw new IllegalArgumentException("This flight " + id.toString() + "isn't referenced for this company " + this.toString() + ".");
        }
        this.flights.get(id).close();
    }

    /**
     * Regularize a flight : make a copy of the trip in the registry
     * @param id the flight id
     * @throws IllegalArgumentException on null or non existent parameter
     */
    public void regularize(FlightId id)
    {
        if(id == null)
        {
            throw new IllegalArgumentException("Flight id can't be null.");
        }
        if(!this.flights.containsKey(id))
        {
            throw new IllegalArgumentException("This flight " + id.toString() + "isn't referenced for this company " + this.toString() + ".");
        }
        TripBuilder tb = new TripBuilder(this.flights.get(id).getTrip());
        this.regularsFlights.add(tb.toRegularTrip());
    }

    /**
     * Getter
     * @return a shallow copy of the list of regular trip
     */
    public ArrayList<Trip> getRegulars()
    {
        return (ArrayList<Trip>)this.regularsFlights.regulars.clone();
    }

    /**
     * Remove a regular from the registry
     * @param i regular flight index in the registry
     * @throws IllegalArgumentException on negative index
     */
    public void removeRegular(int i) throws IllegalArgumentException
    {
        if(i<0)
        {
            throw new IllegalArgumentException("Not a valid regular flight index.");
        }
        this.regularsFlights.remove(i);
    }

    /**
     * Remove a regular from the registry
     * @param t the regular trip
     * @throws IllegalArgumentException null or non existent parameter
     */
    public void removeRegular(Trip t) throws IllegalArgumentException
    {
        if(t == null)
        {
            throw new IllegalArgumentException("Trip can't be null.");
        }
        if(!this.regularsFlights.regulars.contains(t))
        {
            throw new IllegalArgumentException("This trip " + t.toString() + "isn't regular in this company " + this.toString() + ".");
        }
        this.regularsFlights.remove(t);
    }

    /**
     * Setter
     * @param t the regular trip list
     * @throws IllegalArgumentException null parameter
     */
    public void setRegulars(ArrayList<Trip> t)
    {
        if(t == null)
        {
            throw new IllegalArgumentException("Trips can't be null.");
        }
        this.regularsFlights.regulars = t;
    }

    /**
     * Add several regular trip
     * @param t a list of trip
     * @throws IllegalArgumentException on null parameter 
     */
    public void addRegulars(ArrayList<Trip> t)
    {
        if(t == null)
        {
            throw new IllegalArgumentException("Trips can't be null.");
        }
        for(int i = 0 ; i < t.size() ; i++)
        {
            this.regularsFlights.add(t.get(i));
        }
    }

    /**
     * Redefinition of toString
     * @return a string with name and prefix
     */
    @Override 
    public String toString()
    {
        return this.name + "-" + this.prefix;
    }

    /**
    * Represent a flight company
    * @Author Cesano Ugo, Colmerauer Clément
    * 
    */
    private class RegularFlightRegistry
    {
        /** The list of trip in the registry*/
        private ArrayList<Trip> regulars;

        /** Constructor*/
        private RegularFlightRegistry()
        {
            this.regulars = new ArrayList<Trip>();
        }

        /**
        * Add a trip to registry
        * @param trip the trip to add
        * @throws IllegalArgumentException on null parameter
        */
        private void add(Trip trip)
        {
            if(trip == null)
            {
                throw new IllegalArgumentException("Trip can't be null.");
            }
            this.regulars.add(trip);
        }

        /**
        * Remove a trip from registry
        * @param trip the trip to add
        * @throws IllegalArgumentException on null or non existent parameter
        */
        private void remove(Trip trip)
        {
            if(trip == null)
            {
                throw new IllegalArgumentException("Trip can't be null.");
            }
            if(!this.regulars.contains(trip))
            {
                throw new IllegalArgumentException("This trip isn't regular");
            }
            this.regulars.remove(trip);
        }

        /**
        * Add a trip to registry
        * @param i the index of trip
        * @throws IllegalArgumentException on negative index
        */
        private void remove(int i)
        {
            if(i<0)
            {
                throw new IllegalArgumentException("Not a valid regular index.");
            }
            this.regulars.remove(i);
        }

        /**
        * Getter
        * @param i the index of trip
        * @return a regular trip
        * @throws IllegalArgumentException on negative index
        */
        private Trip get(int i)
        {
            if(i<0)
            {
                throw new IllegalArgumentException("Not a valid regular index.");
            }
            return this.regulars.get(i);
        }

    }

    /**
    * Used to generate flight id
    * @Author Cesano Ugo, Colmerauer Clément
    * 
    */
    class FlightIdGenerator
    {
        /**The counter of flight*/
        private int    counter;
        /**Prefix of company*/
        private String prefix;

        /**
        * Constructor
        * @param prefix the company prefix
        * @throws IllegalArgumentException on null or empty parameter
        */
        private FlightIdGenerator(String prefix)
        {
            if(prefix == null)
            {
                throw new IllegalArgumentException("Prefix can't be null.");
            }
            if(prefix.trim().isEmpty())
            {
                throw new IllegalArgumentException("Prefix can't be uniquely space characters.");
            }

            this.prefix  = prefix;
            this.counter = 0;
        }

        /**
        * Create a new flight id
        * @return a flight id
        * @throws IllegalStateException if 4 digit exceded
        */
        synchronized FlightId next()
        {
            StringBuilder value = new StringBuilder(this.prefix);
            value.append("-");
            if(this.counter < 9)
            {
                value.append("0");
            }
            if(this.counter < 99)
            {
                value.append("0");
            }
            if(this.counter < 999)
            {
                value.append("0");
            }
            if(this.counter > 9999)
            {
                throw new IllegalStateException("Flight limit exceded.");
            }
            this.counter++;
            return new FlightId(value.toString());
        }

        /**
        * Reset the counter
        */
        public void reset()
        {
            this.counter = 0;
        }

    }
}
