package controllers.api;

import static java.util.Collections.sort;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import models.VersionedDataStore;
import models.VersionedDataStore.AgencyTx;
import models.VersionedDataStore.GlobalTx;
import models.transit.Route;
import models.transit.StopTime;
import models.transit.Trip;
import models.transit.TripPattern;
import models.transit.TripPatternStop;
import models.transit.TripShape;

import org.geotools.geometry.jts.JTS;
import org.mapdb.Fun;
import org.mapdb.Fun.Tuple2;
import org.opengis.referencing.operation.MathTransform;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.PrecisionModel;
import com.vividsolutions.jts.linearref.LengthLocationMap;
import com.vividsolutions.jts.linearref.LinearLocation;
import com.vividsolutions.jts.linearref.LocationIndexedLine;

import controllers.Api;
import play.Logger;
import play.db.jpa.JPA;
import play.mvc.Controller;

public class TripPatternController extends Controller {
    public static void getTripPattern(String id, String routeId, String agencyId) {
    	if (agencyId == null)
    		agencyId = session.get("agencyId");
    	
    	if (agencyId == null) {
    		badRequest();
    		return;
    	}
    	
    	final AgencyTx tx = VersionedDataStore.getAgencyTx(agencyId);
    	
        try {
        	
            if(id != null) {
               if (!tx.tripPatterns.containsKey(id))
            	   notFound();
               else            	   
            	   renderJSON(Api.toJson(tx.tripPatterns.get(id), false));
            }
            else if (routeId != null) {
            	
            	if (!tx.routes.containsKey(routeId))
            		notFound();
            	else {
            		Set<Tuple2<String, String>> tpKeys = tx.tripPatternsByRoute.subSet(new Tuple2(routeId, null), new Tuple2(routeId, Fun.HI));
	            	
	            	Collection<TripPattern> patts = Collections2.transform(tpKeys, new Function<Tuple2<String, String>, TripPattern> () {
	
						@Override
						public TripPattern apply(Tuple2<String, String> input) {
							return tx.tripPatterns.get(input.b);
						}
	            	});
	            	
	            	renderJSON(Api.toJson(patts, false));     	
            	}
            }
            else {
            	badRequest();
            }
            
            tx.rollback();
            
        } catch (Exception e) {
        	tx.rollback();
            e.printStackTrace();
            badRequest();
        }
    }

    public static void createTripPattern() {
        TripPattern tripPattern;
        
        try {
            tripPattern = Api.mapper.readValue(params.get("body"), TripPattern.class);
            
            if (!VersionedDataStore.agencyExists(tripPattern.agencyId)) {
            	badRequest();
            	return;
            }
            
            AgencyTx tx = VersionedDataStore.getAgencyTx(tripPattern.agencyId);
            
            if(tripPattern.encodedShape != null) {
            	TripShape ts = new TripShape(tripPattern.encodedShape);
            	tx.shapes.put(ts.id, ts);
            	tripPattern.shapeId = ts.id;	
            }
            
            if (tx.tripPatterns.containsKey(tripPattern.id)) {
            	tx.rollback();
            	badRequest();
            }
            
            tx.tripPatterns.put(tripPattern.id, tripPattern);

            renderJSON(Api.toJson(tripPattern, false));
        } catch (Exception e) {
            e.printStackTrace();
            badRequest();
        }
    }

    public static void updateTripPattern() {
        TripPattern tripPattern;
        AgencyTx tx = null;
        try {
            tripPattern = Api.mapper.readValue(params.get("body"), TripPattern.class);
            
            if (!VersionedDataStore.agencyExists(tripPattern.agencyId)) {
            	badRequest();
            	return;
            }
            
            if (tripPattern.id == null) {
                badRequest();
                return;
            }
            
            tx = VersionedDataStore.getAgencyTx(tripPattern.agencyId);
            
            TripPattern originalTripPattern = tx.tripPatterns.get(tripPattern.id);
            
            if(originalTripPattern == null) {
            	tx.rollback();
                badRequest();
                return;
            }
                
            
            if (tripPattern.encodedShape != null) {
                if (originalTripPattern.shapeId != null) {
                	// get and save the shape
                	TripShape shape = tx.shapes.get(originalTripPattern.shapeId);
                    shape.updateShapeFromEncoded(tripPattern.encodedShape);
                    tripPattern.shapeId = originalTripPattern.shapeId;
                }
                else {
                	TripShape ts = new TripShape(tripPattern.encodedShape);
                	tx.shapes.put(ts.id, ts);
                	tripPattern.shapeId = ts.id;	
                }
            }
            else {
                tripPattern.shapeId = null;
                // need to remove old shapes...
            }
                        
            // update stop times
            try {
            	TripPattern.reconcilePatternStops(originalTripPattern, tripPattern, tx);
            } catch (IllegalStateException e) {
            	tx.rollback();
            	badRequest();
            	return;
            }
            	            
            if(tripPattern.encodedShape != null) {
            	TripShape ts = new TripShape(tripPattern.encodedShape);
            	tx.shapes.put(ts.id, ts);
            	tripPattern.shapeId = ts.id;	
            }
            
            if (tx.tripPatterns.containsKey(tripPattern.id)) {
            	tx.rollback();
            	badRequest();
            }
            
            tx.tripPatterns.put(tripPattern.id, tripPattern);

            renderJSON(Api.toJson(tripPattern, false));
        } catch (Exception e) {
        	if (tx != null) tx.rollback();
            e.printStackTrace();
            badRequest();
        }
    }

    public static void deleteTripPattern(String id, String agencyId) {
    	if (agencyId == null)
    		agencyId = session.get("agencyId");
    	
        if(id == null || agencyId == null) {
            badRequest();
            return;
        }

        AgencyTx tx = VersionedDataStore.getAgencyTx(agencyId);

	    try {
	        // first zap all trips on this trip pattern
	        for (Trip trip : tx.getTripsByPattern(id)) {
	        	tx.trips.remove(trip.id);
	        }
	        
	        tx.tripPatterns.remove(id);
	        tx.commit();
    	} finally {
    		tx.rollbackIfOpen();
    	}
    }
    
    public static void calcTripPatternTimes(Long id, Double velocity, int defaultDwell) {
    	/*
    	TripPattern tripPattern = TripPattern.findById(id);
    	
    	List<TripPatternStop> patternStops = TripPatternStop.find("pattern = ? ORDER BY stopSequence", tripPattern).fetch();
    	
    	Double distanceAlongLine = 0.0;
    	
        for(TripPatternStop patternStop : patternStops)
        {
        	patternStop.defaultTravelTime = (int) Math.round((patternStop.defaultDistance - distanceAlongLine) / velocity);
            patternStop.defaultDwellTime = defaultDwell;
        	
        	distanceAlongLine = patternStop.defaultDistance;
        	
        	patternStop.save();
        }*/
    
        ok();
    }
}

@@ -3733,32 +3733,57 @@
 d, tripPattern);
+%0A            tx.commit();
 %0A%0A            re
@@ -6076,36 +6076,32 @@
    %0A            
-if (
 tx.tripPatterns.
@@ -6096,35 +6096,27 @@
 ripPatterns.
-containsKey
+put
 (tripPattern
@@ -6122,154 +6122,46 @@
 n.id
-)) %7B%0A            %09tx.rollback();%0A            %09badRequest();%0A            %7D%0A            %0A            tx.tripPatterns.put(tripPattern.id, tripPattern
+, tripPattern);%0A            tx.commit(
 );%0A%0A

allow creation and updating of trip patterns. #166.