/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package web;

import ejb.BestPaths;
import ejb.Trains;
import java.sql.Time;
import java.util.List;
import java.util.ListIterator;
import java.util.Stack;
import java.util.TreeMap;
import java.util.Vector;

/**
 *
 * @author zwergnase
 */
class PathFilter{

    // holds the max waiting hours
    private int wait;
    //holds the max cost
    private float cost;
    // holds the max changes
    private int change;
    // time limitation
    private Time depTime;
    private Time arrTime;
    // holds the paths to be examined
    private Vector<Path> availablePaths;
    // holds the bestPaths table but this time reduced
    private List<BestPaths> bestPaths;
    // will hold probable ways to go to target
    Stack<Way> probableWays;
    // will hold the completed ways
    private Vector<Way> availableWays;

    private List<Trains> trains;

    public Vector<Way> getAvailableWays() {
        return availableWays;
    }



    PathFilter(int w,float co,int ch,String dt,String at,Vector<Path> ap,List<BestPaths> bp,List<Trains> trs)
    {
        wait=w;
        cost=co;
        change=ch;
        String[] temp=dt.split(":");
        depTime=new Time(Integer.parseInt(temp[0]),Integer.parseInt(temp[1]),0);
        temp=at.split(":");
        arrTime=new Time(Integer.parseInt(temp[0]),Integer.parseInt(temp[1]),0);
        availablePaths=ap;
        bestPaths=bp;
        probableWays=new Stack<Way>();
        availableWays=new Vector<Way>();
        trains=trs;
        System.out.println(wait+" "+cost+" "+change+" ");
    }


        public void findAllWays()
    {
        filterTheBestPaths();
        generateWays();
    }

    private void filterTheBestPaths() {
        // here we eliminate the unnecessarry cities from the best path list so that we can make searches faster
        // find all distinct cities in all paths and save them
        Vector cityids=new Vector();
        int numOfPaths=availablePaths.size();
        for(int i=0;i<numOfPaths;i++){
            Vector citiesinpath=availablePaths.get(i).nodes;
            int numOfCities=citiesinpath.size();
            for(int j=0;j<numOfCities;j++)
            {
                if(!cityids.contains(citiesinpath.get(j)))
                {
                    cityids.addElement(citiesinpath.get(j));
                }
            }
        } // end of for
        //now trace all best paths eliminate cities and times out of range.

        ListIterator bpit=bestPaths.listIterator();
        while(bpit.hasNext())
        {
            BestPaths currentPath=(BestPaths)bpit.next();
            //System.out.print("şimdiki best path: ");
            //currentPath.printBestPath();
            //if one of the cities (departure or arival is not in the list delete it from the list
            if(!cityids.contains(currentPath.getArrivalPlace())|| !cityids.contains(currentPath.getDeparturePlace()))
            {
                bpit.remove();
                //System.out.println("tümden gitti");

            }
            else // check the times and delete them
            {
                TreeMap<Long,Vector<Time> > arrivalMap=currentPath.getArrivalTimes();
                TreeMap<Long,Vector<Time> >  departureMap=currentPath.getDepartureTimes();
                Object[] keys=arrivalMap.keySet().toArray();
                int numOfKeys=keys.length;

                for(int i=0;i<numOfKeys;i++)
                {
                    Vector<Time> arrivalTimes=arrivalMap.get(keys[i]);
                    Vector<Time> departureTimes=departureMap.get(keys[i]);
                    for(int j=0;j<arrivalTimes.size();j++)
                    {
                        Time currentDepTime=departureTimes.get(j);
                        Time currentArrTime=arrivalTimes.get(j);
                        //System.out.println("current time: "+currentDepTime);
                        // filter that time in the vector

                        if(currentDepTime.before(depTime)|| currentDepTime.after(arrTime)||
                                currentArrTime.before(depTime)||currentArrTime.after(arrTime))
                        {
                            //System.out.println("Time elendi");
                            arrivalTimes.remove(j);
                            departureTimes.remove(j);
                            j--;
                        }
                        //else
                           //System.out.println("kaldi");
                    }// end of for each time of each train

                    // filter the mapped train id and all it times cuz the none of the time is available
                    if(arrivalTimes.size()==0)
                    {
                        arrivalMap.remove(keys[i]);
                        departureMap.remove(keys[i]);
                        //System.out.println("Map boşaldı diye silindi");
                    }


                }// end of for each key

                // if all trains are deleted. then remove it from the best path vector
              if(arrivalMap.size()==0)
              {
                  //System.out.println("Entry boşaldı diye silindi");
                      bpit.remove();
              }

              else{
                        //System.out.print("yeni best: ");
                        //currentPath.printBestPath();
                   }
            }// end of else



        }
    }


   private void generateWays() {
       /*ilk nodda tüm trenler var. sonrasında öncelikle aynı trenin ilk uyanı sonrasında farklı trenin ilk uyanları
       // eklerken max cost ve max aktarma da göz önüne alınacak yol kalmadığı anda silinecek
       // ayrıca 1 2 1 yasak..
       // we will use availablePaths: holds the city sequences--> vector<Path>
       // we will use best path (filtered train/time connections)--> List from database
       // we will use probableWays to hold the subways  --> stack
       // we will use availableWays to hold the completed ways --> vector*/

       Way firstEntry=null; // ilk trenler burda dolduruluyor.
       int numOfAvPaths=availablePaths.size();
       System.out.println("avpath size"+availablePaths.size());
       for(int i=0;i<availablePaths.size();i++)
       {
           Vector<Long> v=availablePaths.get(i).nodes;
           for(int j=0;j<v.size();j++)
                System.out.print(v.get(j)+"->");
       }



       // for each avaliable path
       for(int i=0;i<numOfAvPaths;i++)
       {
            Path path=availablePaths.get(i); // get the first available path : a->b->c->d
            Vector<Long> subsequentCities=path.nodes;         // get its subsequent cities
            System.out.println(subsequentCities.get(0)+" to  "+subsequentCities.get(1));
            BestPaths currentBestPath=findSpecificBestPath(subsequentCities.get(0),subsequentCities.get(1));
            if(currentBestPath==null)
            {
                // demek ki  bu yol/ yolu baplayan trenler elenmiş direk continue yap
                continue;
            }
            Long sourceId=subsequentCities.get(0);
            Long destId=subsequentCities.get(subsequentCities.size()-1);
            if(currentBestPath==null)
            {
                System.out.println("cannot find the path");
                return;
            }
            // initialization

            Object keys[]=currentBestPath.getDepartureTimes().keySet().toArray();
            int numOfTrains=keys.length;
            System.out.println("numOfTrains:"+ numOfTrains);
            for(int j=0;j<numOfTrains;j++)
            {
                Vector<Time> specificDepTimes=currentBestPath.getDepartureTimes().get(keys[j]);
                Vector<Time> specificArrTimes=currentBestPath.getArrivalTimes().get(keys[j]);
                int numOfTimes=specificDepTimes.size();
                for(int k=0;k<numOfTimes;k++)
                {
                    firstEntry=new Way();           // create a departureCity -> next city subway
                    firstEntry.changesSoFar=0;
                    firstEntry.totalDistance=new Float(path.distance);
                    firstEntry.sequence=1;
                    Trains T=findSpecificTrain((Long)keys[j]);
                    firstEntry.costSoFar=T.getCosts()*currentBestPath.getDistance();
                    System.out.println("CURRENT ARRIVAL PLACE!!!: "+currentBestPath.getArrivalPlace());
                    subpath firstSub=new subpath(currentBestPath.getArrivalPlace(),(Long)keys[j],
                            specificDepTimes.get(k),specificArrTimes.get(k));
                    System.out.println("FIRST SUB!!!!!!!!!!");
                    firstSub.printSubpath();
                    firstSub.partialCost=firstEntry.costSoFar;
                    firstEntry.pathSoFar.addElement(firstSub);
                    // add it to the stack
                    if(firstEntry.costSoFar<=cost)
                    {
                        probableWays.addElement(firstEntry);
                        System.out.println("first added:");
                        firstEntry.printWay();
                    }
                }// end of for all times
            }// end of for all trains

            // şimdi stack boş değilken.... vs.

            while(!probableWays.empty())
            {
                System.out.println("DESTINATION ID"+destId);
                Way currentWay=probableWays.pop();
                System.out.println("popped:");
                currentWay.printWay();

                System.out.println("SON ELEMAN!!!!!!!!!: "+currentWay.pathSoFar.lastElement().cityId);
                System.out.println("ILK ELEMAN!!!!!!!!!: "+currentWay.pathSoFar.firstElement().cityId);
                if(currentWay.pathSoFar.lastElement().cityId.equals(destId)) //artık varmışız direk normal way e ekle
                {
                    availableWays.addElement(currentWay);
                    System.out.println("bitti:");
                    currentWay.printWay();
                }

                else // we have to advance that subpath
                {
                    System.out.println("advance it");
                    subpath lastpath=currentWay.pathSoFar.lastElement();
                    System.out.println("subpath bu:");
                    lastpath.printSubpath();
                    Long currentTrainId=lastpath.trainId;
                    Time lastArrivalTime=lastpath.arrTime;
                    BestPaths bestPathForNextCity=findSpecificBestPath(path.nodes.get(currentWay.sequence),
                            path.nodes.get(currentWay.sequence+1)); // o aralıktakı yoldan geçen trenler burada
                     System.out.println("onun icinbestpath bu:");
                     bestPathForNextCity.printBestPath();

                    if(bestPathForNextCity.getDepartureTimes().containsKey(currentTrainId)) // aynı trenin devamı var
                    {
                        System.out.println("aynı trenin devamı var");
                        Vector<Time> sameTrainsDepTimes=bestPathForNextCity.getDepartureTimes().get(currentTrainId); //onun zamanını al
                        Vector<Time> sameTrainsArrTimes=bestPathForNextCity.getArrivalTimes().get(currentTrainId);
                        int numofsamett=sameTrainsDepTimes.size();
                        Time minTimeAfterArrival=null;
                        int index=0;
                        // find the earliest time after the arrival save the index in
                        for(int l=0;l<numofsamett;l++)
                        {
                            if((sameTrainsDepTimes.get(l).after(lastArrivalTime)||sameTrainsDepTimes.get(l).equals(lastArrivalTime))
                                    &&(minTimeAfterArrival==null || sameTrainsDepTimes.get(l).before(minTimeAfterArrival) ))
                            {
                                minTimeAfterArrival=sameTrainsDepTimes.get(l);
                                index=l;
                            }


                        }

                        // if the wait time is exceeded make the time null
                        if(minTimeAfterArrival!=null)
                        {
                            int mindiff=minTimeAfterArrival.getMinutes()-lastArrivalTime.getMinutes();
                            int hourdiff=minTimeAfterArrival.getHours()-lastArrivalTime.getHours();

                            if(!(hourdiff<wait||(hourdiff==wait&&mindiff<=0)))
                            {
                                minTimeAfterArrival=null;
                                System.out.println("waiting time exceeded");
                            }

                        }

                        if(minTimeAfterArrival!=null)
                        {
                            System.out.println("bu zaman seçildi"+minTimeAfterArrival);
                            Way nextWay=new Way();
                            Trains T=findSpecificTrain(currentTrainId);
                            float partialCost=T.getCosts()*bestPathForNextCity.getDistance();
                            nextWay.costSoFar=partialCost+currentWay.costSoFar;
                            System.out.println("cost bu tutuyor:"+ nextWay.costSoFar);
                            if(nextWay.costSoFar<cost)                              // we can still take this train
                            {
                                nextWay.changesSoFar=currentWay.changesSoFar;
                                nextWay.pathSoFar=(Vector<subpath>)currentWay.pathSoFar.clone();
                                nextWay.sequence=currentWay.sequence+1;
                                subpath nextsubpath=new subpath(path.nodes.get(currentWay.sequence+1),currentTrainId ,
                                        minTimeAfterArrival,sameTrainsArrTimes.get(index));
                                nextsubpath.partialCost=partialCost;
                                nextWay.pathSoFar.addElement(nextsubpath);
                                // insert et.
                                probableWays.add(nextWay);
                                System.out.println("trenin devamı eklendi:");
                                nextWay.printWay();
                            }
                            //else too expensive ignore it
                        }
                      else
                          System.out.println("aynı tren bulunamadı");
                    }

                    if(currentWay.changesSoFar<change) // we have the chance to change train && dont let them to return to the last train
                    {
                        System.out.println("hala değişim hakkımız var");
                        Object[] trainKeys=bestPathForNextCity.getDepartureTimes().keySet().toArray();
                        int numOfTraiKeySet=trainKeys.length;
                        System.out.println("bulunan trenler "+numOfTraiKeySet);
                        for(int k=0;k<numOfTraiKeySet;k++)
                        {
                            if(!trainKeys[k].equals(currentTrainId)&& !pathSoFarIncludes(currentWay.pathSoFar,(Long)trainKeys[k])) // ve pathsofar o idyi içermiyorsa
                            {
                                System.out.println("aynı yolda bulunmayan bir tren bulundu"+trainKeys[k]);
                                Vector<Time> diffTrainsDepTimes=bestPathForNextCity.getDepartureTimes().get(trainKeys[k]); //onun zamanını al
                                Vector<Time> diffTrainsArrTimes=bestPathForNextCity.getArrivalTimes().get(trainKeys[k]);
                                int numofdifftt=diffTrainsDepTimes.size();
                                Time minTimeAfterArrival=null;
                                int index=0;
                                for(int l=0;l<numofdifftt;l++)
                                {
                                    if((diffTrainsDepTimes.get(l).after(lastArrivalTime)||diffTrainsDepTimes.get(l).equals(lastArrivalTime))&&(minTimeAfterArrival==null ||
                                            diffTrainsDepTimes.get(l).before(minTimeAfterArrival)))
                                    {
                                        minTimeAfterArrival=diffTrainsDepTimes.get(l);
                                        index=l;

                                    }

                                }

                                if(minTimeAfterArrival!=null)
                                {
                                    int mindiff=minTimeAfterArrival.getMinutes()-lastArrivalTime.getMinutes();
                                    int hourdiff=minTimeAfterArrival.getHours()-lastArrivalTime.getHours();

                                    if(!(hourdiff<wait||(hourdiff==wait&&mindiff<=0)))
                                    {
                                         minTimeAfterArrival=null;
                                         System.out.println("waiting time exceeded");
                                    }
                                }



                               if(minTimeAfterArrival!=null)            // you can use this train
                                {
                                   System.out.println("bu saat seçildi");
                                    Way nextWay=new Way();
                                    Trains T=findSpecificTrain((Long)trainKeys[k]);
                                    float partialCost=T.getCosts()*bestPathForNextCity.getDistance();
                                    nextWay.costSoFar=partialCost+currentWay.costSoFar;
                                    System.out.println("cost bu kadar bulundu:"+nextWay.costSoFar);
                                    if(nextWay.costSoFar<cost)                              // we can still take this train
                                    {
                                        nextWay.changesSoFar=currentWay.changesSoFar+1;
                                        nextWay.pathSoFar=(Vector<subpath>)currentWay.pathSoFar.clone();
                                        nextWay.sequence=currentWay.sequence+1;
                                        subpath nextsubpath=new subpath(path.nodes.get(currentWay.sequence+1),(Long)trainKeys[k] ,
                                                minTimeAfterArrival,diffTrainsArrTimes.get(index));
                                        nextsubpath.partialCost=partialCost;
                                        nextWay.pathSoFar.addElement(nextsubpath);
                                        // insert et.
                                        probableWays.add(nextWay);
                                        System.out.println("diff train ekleniyor");
                                        nextWay.printWay();
                                    }
                                    //else ignore it
                                }
                            }
                        }// end of for each different train
                    }// change hakkımız var
                }// end of else-> henüz varmamışız
            }//end of while stack is not empty
       }// for each path -- en dışarda


       int numOfAvWays=availableWays.size();
       System.out.println("final available ways:"+numOfAvWays);
       for(int i=0;i<numOfAvWays;i++)
            availableWays.get(i).printWay();
    }//end of generate all ways

   boolean pathSoFarIncludes(Vector<subpath> subpaths, Long Tid)
   {
       int subsize=subpaths.size();
       for(int i=0;i<subsize;i++)
       {
           if(subpaths.get(i).trainId.equals(Tid))
               return true;
       }
       return false;
   }

   BestPaths findSpecificBestPath(long from,long to)
   {
       ListIterator<BestPaths> bpit=bestPaths.listIterator();
       BestPaths currentPath=null;
       while(bpit.hasNext())
       {
           currentPath=bpit.next();
           if(currentPath.getDeparturePlace()==from && currentPath.getArrivalPlace()==to)
               break;
           else
               currentPath=null;
       }
       return currentPath;
   }

   Trains findSpecificTrain(Long Tid)
   {
       ListIterator<Trains> tit=trains.listIterator();
       Trains currentTrain=null;
       while(tit.hasNext())
       {
           currentTrain=tit.next();
           if(currentTrain.getId().equals(tit))
               break;
       }
       return currentTrain;
   }



}//end of pathFilter




