package com.uca.flights;

import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Iterator;

/**
 * Represent an trip, several jumps 
 * @Author Cesano Ugo, Colmerauer Clément
 * 
 */
public class Trip implements Cloneable
{
	/** Trip is modifiable, used to optimize the flyweight of regular trip and not clone for nothing*/
	private boolean modifiable;
	/** Trip first jump*/
	private Jump    start;

	/**
     * Constructor
     * @param j a jump
     * @param modifiable if trip is modifiable
     * @throws IllegalArgumentException on null parameter
     */
	public Trip(Jump j, boolean modifiable)
	{
		if(j == null)
		{
			throw new IllegalArgumentException("Jump can't be null.");
		}
		this.start = j;
		this.modifiable = modifiable;
	}

	/**
     * Constructor
     * @param j a jump
     * @throws IllegalArgumentException on null parameter
     */
	public Trip(Jump j)
	{
		if(j == null)
		{
			throw new IllegalArgumentException("Jump can't be null.");
		}
		new Trip(j,false);
	}

	/**
     * Redefinition of clone, return clone or this depending on modifiable
     * @return new step with same attributes
     */
	@Override
	public Object clone()
	{
		//Optimization on the flyweight to avoid recloning something that was already
		if(this.modifiable == true)
		{
			return this;
		}
		else
		{
			return new Trip((Jump)this.start.clone(),true);
		}
	}

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

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

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

	/**
     * Getter
     * @return the last step of trip
     */
	Step getLastStep()
	{
		Step s = this.start.getDeparture();
		while(s.hasNext())
		{
			s = s.next();
		}
		return s;
	}

	/**
     * Getter
     * @return duration of trip
     */
	Duration getDuration()
	{
		Jump j = this.start;
		while(j.hasNext())
		{
			j = j.next();
		}
		return j.getArrival().getDuration();
	}

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

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

	/**
     * Getter
     * @return the first step
     */
	Jump getStart()
	{
		return this.start;
	}

	/**
     * Redefinition of toString
     * @return a string with starting and last airport and duration in hours
     */
	@Override
	public String toString()
	{
		return this.start.getDeparture().getAirport().toString() + " to " + this.getLastStep().getAirport().toString() + " in " + String.valueOf(this.getDuration().toHours()) + "hours.";
	}
}