package TS;

import RDG.City;
import RDG.Class;
import RDG.PassengerType;
import RDG.Searchings;
import RDG.UsersDB;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.PriorityQueue;
import java.io.IOException;
import java.sql.SQLException;

/**
 *
 * @author Marcel
 */
public class FlightDijkstraFinder {

    /**
     * Return option for flying from city to city at some time, class, and for some passenger
     * @param departure_id departure city_id
     * @param arrival_id arrival city_id
     * @param departure_time earliest possible departure time
     * @param class_id class to find
     * @param passenger_type passenger type for whom count price
     * @param user_id user_id for logging searching
     * @return List of flight ids
     * @throws SQLException
     * @throws IOException
     */
    public static List<Integer> Dijkstra(Integer departure_id, Integer arrival_id, Timestamp departure_time, Integer class_id, Integer passenger_type, Integer user_id) throws SQLException, IOException, Exception {
        if (!City.exists(departure_id)) {
            throw new Exception("Departure city w/ this id don't exists");
        }
        if (!City.exists(arrival_id)) {
            throw new Exception("Arrival city w/ this id don't exists");
        }
        if (!Class.exists(class_id)) {
            throw new Exception("Class w/ this id don't exists");
        }
        if (!PassengerType.exists(passenger_type)) {
            throw new Exception("Passenger type w/ this id don't exists");
        }
        if (!UsersDB.exists(user_id)) {
            throw new Exception("User w/ this id don't exists");
        }
        HashMap<Integer, List<Integer>> dist = new HashMap();
        PriorityQueue<Zaznam> pq = new PriorityQueue<>();
        pq.add(new Zaznam(departure_id, departure_time, new ArrayList<>(), null));
        while (!pq.isEmpty()) {
            Zaznam top = pq.poll();
            System.out.println(top);
            while(!pq.isEmpty() && dist.containsKey(top.i)) {
                top=pq.poll();
            }
            if(dist.containsKey(top.i))
                break;
            dist.put(top.i, top.flight_list);
            
            if (top.flight_list.size() > 10 || top.i.equals(arrival_id)) {
                break;
            }
            for (Zaznam p : City.DirectCities(top.i, top.cas, class_id)) {
                List<Integer> tmp_flight_list = new ArrayList<>();
                tmp_flight_list.addAll(top.flight_list);
                tmp_flight_list.add(p.flight_id);
                pq.add(new Zaznam(p.i, p.cas, tmp_flight_list, p.flight_id));
            }
        }
        return dist.get(arrival_id);
    }
    /**
    * simple struct for saving flight options into heap and other.
    */
    public static class Zaznam implements Comparable{
        public Zaznam(Integer i, Timestamp cas, List<Integer> l, Integer flight_id){
            this.cas=cas;
            this.i=i;
            this.flight_list=l;
            this.flight_id=flight_id;
        }
        public Timestamp cas;
        public Integer i;
        public Integer flight_id;
        public List<Integer> flight_list;

        @Override
        public int compareTo(Object o) {
            int t = cas.compareTo(((Zaznam)o).cas);
            if(t!=0)
                return t;
            return Integer.compare(flight_list.size(), ((Zaznam)o).flight_list.size());
        }

        @Override
        public String toString() {
            return "Zaznam{" + "cas=" + cas + ", i=" + i + ", flight_id=" + flight_id + ", flight_list=" + flight_list + '}';
        }
        
    }
    /**
     * find flight
     * isolation level: repeatable read
     * - add searching count
     * - run dijkstra finder
     * @param departure_id from what city
     * @param arrival_id to what city
     * @param departure_time from what time
     * @param class_id in what class
     * @param passenger_type for what passenger type
     * @param user_id who finds this flight
     * @throws SQLException
     * @throws Exception
     * @return List of flight ids
     */
    public static List<Integer> transactionFlightFinder(Integer departure_id, Integer arrival_id, Timestamp departure_time, Integer class_id, Integer passenger_type, Integer user_id) throws SQLException, Exception{
        RDG.DbContext.getConnection().setAutoCommit(false);
        
        RDG.DbContext.getConnection().setTransactionIsolation(java.sql.Connection.TRANSACTION_REPEATABLE_READ);
        
        boolean podarilo = false;
        List<Integer> res=null;
        while(!podarilo){
            podarilo=true;
            try{
                Searchings.plus(departure_id, arrival_id, user_id);
                res=FlightDijkstraFinder.Dijkstra(departure_id, arrival_id, departure_time, class_id, passenger_type,user_id);
            }catch(Exception e){
                RDG.DbContext.getConnection().rollback();
                System.err.println(e);
                podarilo=false;
            }
        }
        
        RDG.DbContext.getConnection().commit();
        RDG.DbContext.getConnection().setAutoCommit(true);
        return res;
    }
}
