/**
* Name: Mix Drive City
* Description: Vehicles driving in a road graph
* Author: Duc Pham and Patrick Taillandier
* Tags: gis, shapefile, graph, agent_movement, skill, transport
*/

model simple_intersection


global {
	float size_environment <- 1#km;
	
	geometry shape <- envelope(size_environment);
	
	//the typical step for the advanced driving skill
	float step <- 0.5 #s;
	
	//use only for display purpose
	float lane_width <- 2.0;
	
	//number of cars
	int num_cars <- 300;
	
	float proba_block_node_car <- 1.0; 
	
	//graph used for the shortest path computation
	graph road_network;
	
	init {
		create intersection with: (location: {10,size_environment/2});
		create intersection with: (location: {size_environment/2,size_environment/2});
		create intersection with: (location: {size_environment / 2 + 30,size_environment/2}, is_traffic_signal: true);
		create intersection with: (location: {size_environment - 10,size_environment/2});
	
		create intersection with: (location: {size_environment/2, 10});
		create intersection with: (location: {size_environment/2,size_environment - 10});
		
		create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[0],intersection[1]]));
		create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[1],intersection[2]]));
		create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[2],intersection[3]]));
		create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[4],intersection[1]]));
		create road with:(num_lanes:1, maxspeed: 50#km/#h, shape:line([intersection[1],intersection[5]]));
		
	
		//build the graph from the roads and intersections
		road_network <- as_driving_graph(road,intersection);
		
		//for traffic light, initialize their counter value (synchronization of traffic lights)
		ask intersection where each.is_traffic_signal {
			do initialize;
		}
		
	}
	
	reflex add_car {
		create car with: (location: intersection[0].location, target: intersection[3]);
		create car with: (location: intersection[4].location, target: intersection[5]);
	}
}


//road species
species road skills: [skill_road]{
	string type;
	string oneway;
	
	aspect base_ligne {
		draw shape color: #white end_arrow:5; 
	}
	
} 

//intersection species
species intersection skills: [skill_road_node] {
	bool is_traffic_signal;
	float time_to_change <- 60#s ;
	float counter <- rnd(time_to_change);
	
	//take into consideration the roads coming from both direction (for traffic light)
	list ways1;
	list ways2;
	
	//if the traffic light is green
	bool is_green;
	rgb color <- #yellow;

	//initialize the traffic light
	action initialize {
		do compute_crossing;
		stop << [];
		if (flip(0.5)) {
			do to_green;
		} else {
			do to_red;
		}
	}

	action compute_crossing {
		if (length(roads_in) >= 2) {
			road rd0 <- road(roads_in[0]);
			list pts <- rd0.shape.points;
			float ref_angle <- last(pts) direction_to rd0.location;
			loop rd over: roads_in {
				list pts2 <- road(rd).shape.points;
				float angle_dest <- last(pts2) direction_to rd.location;
				float ang <- abs(angle_dest - ref_angle);
				if (ang > 45 and ang < 135) or (ang > 225 and ang < 315) {
					ways2 << road(rd);
				}
			}
		}

		loop rd over: roads_in {
			if not (rd in ways2) {
				ways1 << road(rd);
			}
		}
	}

	//shift the traffic light to green
	action to_green {
		stop[0] <- ways2;
		color <- #green;
		is_green <- true;
	}

	//shift the traffic light to red
	action to_red {
		stop[0] <- ways1;
		color <- #red;
		is_green <- false;
	}

	//update the state of the traffic light
	reflex dynamic_node when: is_traffic_signal {
		counter <- counter + step;
		if (counter >= time_to_change) {
			counter <- 0.0;
			if is_green {
				do to_red;
			} else {
				do to_green;
			}
		}
	}

	aspect base {
		draw circle(1) color: color;
	}
}


species car skills: [advanced_driving] {
	rgb color <- rnd_color(255);
	intersection target;
	
	init {
		vehicle_length <- 3.8 #m;
		//car occupies 2 lanes
		num_lanes_occupied <-1;
		max_speed <-150 #km / #h;
				
		proba_block_node <- proba_block_node_car;
		proba_respect_priorities <- 1.0;
		proba_respect_stops <- [1.0];
		proba_use_linked_road <- 0.0;

		lane_change_limit <- 2;
		linked_lane_limit <- 0;
		
	}
	//choose a random target and compute the path to it
	reflex choose_path when: final_target = nil {
		do compute_path graph: road_network target: target; 
	}
	reflex move when: final_target != nil {
		do drive;
		//if arrived at target, kill it and create a new car
		if (final_target = nil) {
			do unregister;
			do die;
		}
	}
	
	// Just use for display purpose
	// Shifts the position of the vehicle perpendicularly to the road,
	// in order to visualize different lanes
	point compute_position {
		if (current_road != nil) {
			float dist <- (road(current_road).num_lanes - current_lane -
				mean(range(num_lanes_occupied - 1)) - 0.5) * lane_width;
			if violating_oneway {
				dist <- -dist;
			}
		 	point shift_pt <- {cos(heading + 90) * dist, sin(heading + 90) * dist};	
		
			return location + shift_pt;
		} else {
			return {0, 0};
		}
	}
	
	
	aspect default {
		if (current_road != nil) {
			point pos <- compute_position();
				draw rectangle(vehicle_length, lane_width * num_lanes_occupied) 
				at: pos color: color rotate: heading border: #black;
			draw triangle(lane_width * num_lanes_occupied) 
				at: pos color: #white rotate: heading + 90 ;
		}
	}

}


experiment simple_intersection  type: gui {

	output synchronized: true {
		display city type: opengl background: #black axes: false{
			species road aspect: base_ligne;
			species intersection aspect: base;
			species car ;
		}
	}
}