#!/usr/bin/perl -w

use strict;
use DBI;

my $dock_status;
my $this_rack_num = 1;

# get initial dock status from db
my $orig_dock_status = initialize_rack($this_rack_num);

print "\n\norig_dock_status: $orig_dock_status\n\n";


#my $i = 0;

# inifinitely scan the 1-wire bus
while(1)
{	
	# get ids on the 1-wire net bus
	my $bus_ids = get_all_bus_ids();
	
#	print "bus_ids: $bus_ids\n";
	
	
	# if the dock has a vehicle in it
	if($bus_ids =~ /([A-Z0-9]{16})-([A-Z0-9]{16})-/)
	{
		#set the dock status to the vehicle_id
		$dock_status = $1;
		
		print "dock: $2  dock_status: $dock_status (vehicle_id)\n";
	}
	# else if the dock is empty
	elsif($bus_ids =~ /([A-Z0-9]{16})-/)
	{
		# set the dock_status to empty
		$dock_status = "empty";

		print "Dock is empty\n";
	}
	
	# if a vehicle has been returned. ie:
	# if the original dock status has changed and the dock has a vehicle
	if(($orig_dock_status ne $dock_status) && ($dock_status ne "empty"))
	{
		#for right now the code only deals with one dock (1)
		my $dock_num = 1;
		
		print "vehicle returned.   vehicle_id: $dock_status\n";
		
		# update the orig_dock_status for comparison next time through the loop
		$orig_dock_status = $dock_status;
		
		# We've figured out $dock_status is a vehicle_id
		# now convert the long vehicle_id into a short friendly vehicle_num
		my $vehicle_num = convert_vehicle_id_to_num($dock_status);
		
		# Figure out who had the vehicle
		my $matched_user_id = match_user_to_vehicle($vehicle_num);
		
		my $full_name = match_user_id_to_user_name($matched_user_id);
		
		print "returned by: $full_name\n";
		
		# Set vehicle.availability back to available
		update_vehicle_availability($vehicle_num, "available");
		
		# update the dock.dock_status in the database to reflect the checked in vehicle
		# yikes... for right now dock is hard coded.
		update_dock_status($dock_num, $this_rack_num, $vehicle_num);
		
#		print "+++vehicle_num: $vehicle_num+++\n";
		
		# Update events table: date_time, action, user_id, rack_id, dock_id, 
		# vehicle_id
		#action, user_id, rack_num, dock_num, vehicle_num, [elapsed_time]
		add_events_entry("sign_in",$matched_user_id,$this_rack_num,$dock_num,$vehicle_num);
		
		# check if vehicle is overdue.  If vehicle is overdue
		
			# Take appropriate action
			# haven't decided what this is yet, but if any action is going to be 
			# taken it needs to be somewhat sophisiticated.  For example, if
			# it's the first offense and the bike is only a few minutes late, 
			# perhaps just send an email to the person urging them to return the 
			# vehicle in a more timely manner.  If it's a 3rd offense and the 
			# vehicle is an hour late, send a charge to their account.  There 
			# needs to be a method for storing, retrieving, and weighing the 
			# history of each individual.  We could possible have "karma" 
			# stored in the user database and as that number reaches certain 
			# thresholds different actions are taken.  Users could start at 0.  
			# Certain infractions add predetermined number of points to their 
			# karma.  If they finally reach 100, for example, their account 
			# would be set to disabled and they would no longer be able to use 
			# the system.  I think the Amsterdam program also rewarded people if 
			# they brought vehicles from a heavily loaded rack to an empty rack.  
			# Users could earn good karma for this and perhaps a very small 
			# amount of karma could be earned if they return the vehicle quickly 
			# to allow others to use the bikes
		
#		print "   orig_dock_status: $dock_status\n\n";
	}
	# if a vehicle has been checked out
	# check out code should already know about it
	elsif(($orig_dock_status ne $dock_status) && ($dock_status eq "empty"))
	{
		print "orig_dock_status: $orig_dock_status     dock_status: $dock_status\n";
		print "a bike has been removed from the dock\n";

		# update the orig_dock_status for comparison next time through the loop		
		$orig_dock_status = "empty";
	}
	# if nothing has changed since the last time through
	else
	{
#		print "nothing changed   ";
		
#		print "dock_status: $dock_status\n\n";
	}
	
	`sleep 2`;
	
#	$i++;
}


#my @binky;
#my $dock_id;
#my $vehicle_id;
#my %initial_rack_ids = initialize_rack();

#print "bla: $bus_ids";
#@binky = clean_rack_ids($bus_ids);

#print "dock_id: $binky[1]\n";
#print "vehicle_id: $binky[0]\n";

#while (($dock_id,$vehicle_id) = each(%initial_rack_ids))
#{
#	print "dock: $dock_id\nvehicle: $vehicle_id\n";
#}

################################################################################
# Function: get_all_bus_ids
# Description: calls an external program which returns all ids on the 1-wire 
#              net bus 
# Arguments: none
# Returns: string raw ids (each id is separated by a - (there is a - at the 
#          end as well)
# Status: 80% (clean up.  Also abstract the port to read from)
################################################################################
sub get_all_bus_ids
{
	# get all the ids and store the raw string to $all_bus_ids
	# This assumes that the 1-wire net reader is connected to /dev/ttyS1 and
	# that get_ids is in the same directory that this program is being run from
	my $all_bus_ids = `./get_ids \/dev\/ttyS1`;

	return $all_bus_ids;
}

################################################################################
# Function: clean_rack_ids
# Description: takes the id dump for the bus and separates out into
#              vehicle_id/dock_id paired hash 
# Arguments: string raw_rack_ids
# Returns: hash cleaned_rack_ids
# Status: 5%
################################################################################
sub clean_rack_ids
{
	my $raw_rack_ids = $_[0];
	my @split_rack_ids;
	
	# remove the newline and the ending -
	chop $raw_rack_ids;
	chop $raw_rack_ids;
	
	@split_rack_ids = split /-/, $raw_rack_ids;
	
	#print "dock_id: $split_rack_ids[1]\n";
	#print "vehicle_id: $split_rack_ids[0]\n";

	return @split_rack_ids;
}

################################################################################
# Function: initialize_rack()
# Description: Before we get id's from the rack, the values need to be seeded 
#              from the database.  This function gets the status of each dock 
#              from the dock table to get a values to be used for comparison
# Arguments: int rack_id
# Returns: hash - vehicle_id/dock_id pair? 
# Prerequisite: none
# Status: 3% Need to figure out format of returned data and whole lot of other 
#         stuff.  Returned value is hard coded until I better understand what
#         is going on.
################################################################################
sub initialize_rack
{
	my $rack_num = $_[0];
	# temporary hack
	my $dock_num = 1;
	my %initial_rack_ids;
#	my %initial_rack_ids = ('9B000001A8CB4209','58000006377AD201');

	my ($dsn) = "DBI:mysql:bike_program:localhost";
	my ($user_name) = "bike";
	my ($password) = "5xMts6";
	my ($query);
	my ($dbh, $sth);
	my (@ary);

	# connect to database
	$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});

	#build query - find dock_ids matching vehicle_ids
#	my $query = "SELECT dock\.dock_id, vehicle\.vehicle_id FROM dock, vehicle WHERE dock\.dock_status = vehicle\.vehicle_num AND dock\.rack_id='$rack_id'";
	
	#this query only gets the status of rack 1 dock 1.  This obviously needs major modifications
	$query = "SELECT dock_status FROM dock WHERE dock_num='1'";
	
#	print $query;	

	#execute query
	$sth = $dbh->prepare($query);
	$sth->execute();

	#fetch results
	my $initial_dock_status = $sth->fetchrow_array();

#	return %initial_rack_ids;
	return $initial_dock_status;
}

################################################################################
# Function: convert_vehicle_id_to_num()
# Description: converts long vehicle_id's to user friendly vehicle_num
# Arguments: string vehicle_id
# Returns: int vehicle_num
# Prerequisite: none
# Status: 5%
################################################################################
sub convert_vehicle_id_to_num
{
	my $vehicle_id = $_[0];

	my ($dsn) = "DBI:mysql:bike_program:localhost";
	my ($user_name) = "bike";
	my ($password) = "5xMts6";
	my ($query);
	my ($dbh, $sth);
	my (@ary);

	# connect to database
	$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
	
	# create query
	$query = "SELECT vehicle_num FROM vehicle WHERE vehicle_id='$vehicle_id'";

#	print $query;	

	#execute query
	$sth = $dbh->prepare($query);
	$sth->execute();

	#fetch results
	my $vehicle_num = $sth->fetchrow_array();

	return $vehicle_num;
}

################################################################################
# Function: match_user_to_vehicle
# Description: checks the vehicle.availability field to get a user_id of the 
#              person who has the vehicle
# Arguments: string vehicle_id
# Returns: int matched_user_id
# Prerequisite: this function is intended to be used to check vehicles which 
#               have their availability set to the user_id of the person who 
#               has signed it out.
# Status: 5%
################################################################################
sub match_user_to_vehicle
{
	my $vehicle_num = $_[0];

	my ($dsn) = "DBI:mysql:bike_program:localhost";
	my ($user_name) = "bike";
	my ($password) = "5xMts6";
	my ($query);
	my ($dbh, $sth);
	my (@ary);

	# connect to database
	$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
	
	# create query
	$query = "SELECT availability FROM vehicle WHERE vehicle_num='$vehicle_num'";

#	print $query;	

	#execute query
	$sth = $dbh->prepare($query);
	$sth->execute();

	#fetch results
	my $matched_user_id = $sth->fetchrow_array();

	return $matched_user_id;
}

################################################################################
# Function: match_user_id_to_user_name
# Description: uses the user_id to get and return the users Name.
# Arguments: string user_id
# Returns: string full_name
# Prerequisite: none
# Status: 5%
################################################################################
sub match_user_id_to_user_name
{
	my $user_id = $_[0];

	my ($dsn) = "DBI:mysql:bike_program:localhost";
	my ($user_name) = "bike";
	my ($password) = "5xMts6";
	my ($query);
	my ($dbh, $sth);
	my (@ary);

	# connect to database
	$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
	
	# create query
	$query = "SELECT first_name, last_name FROM user WHERE user_id='$user_id'";

#	print $query;	

	#execute query
	$sth = $dbh->prepare($query);
	$sth->execute();

	#fetch results
	my @results = $sth->fetchrow_array();
	
	my $full_name = $results[0] . " " . $results[1];
	
	return $full_name;
}


### NOTE: This function already exists in check out code ###
################################################################################
# Function: update_dock_status
# Description: updates status of dock_status in the dock database
# Arguments: int dock_num, int rack_num, string dock_status (empty or vehicle_id)
# Returns: bool - true on success
# Status: 90% (functional needs cleanup)
################################################################################
sub update_dock_status
{
	my $dock_num = $_[0];
	my $rack_num = $_[1];
	my $dock_status = $_[2];
	my $rows;
	my $outcome;
	
	my ($dsn) = "DBI:mysql:bike_program:localhost";
	my ($user_name) = "bike";
	my ($password) = "5xMts6";
	my ($query);
	my ($dbh, $sth);
	my (@ary);
			
	# connect to database
	$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
	
	$query = "UPDATE dock SET dock_status='$dock_status' WHERE rack_num='$rack_num' AND dock_num='$dock_num'";
#	print "\nquery: $query\n";

	$rows = $dbh->do ($query);

	$dbh->disconnect();

	if(!defined($rows)) #if there was an error
	{
		$outcome = "0";  #return false
	}
	elsif($rows == "0E0") #if no rows were affected
	{
		$outcome = "0";
#		print "\ngot into the 0E0 Statement\n";
	}
	else
	{
		$outcome = "1";  # return true
	}

	return $outcome;
}

### NOTE: This function already exists in check out code ###
### I changed vehicle_id's to vehicle_num  but not in check out code  merge!!! ###
################################################################################
# Function: update_vehicle_availability
# Description: updates availability of specified vehicle
# Arguments: vehicle_id, availability (available, unavailable, or user_id)
# Returns: bool - 1 on success, 0 on failure
# Status: 90% (functional needs cleanup)
################################################################################
sub update_vehicle_availability
{
	my $vehicle_num = $_[0];
	my $availability = $_[1];
	
	my $outcome;
	my $rows;
	
	my ($dsn) = "DBI:mysql:bike_program:localhost";
	my ($user_name) = "bike";
	my ($password) = "5xMts6";
	my ($query);
	my ($dbh, $sth);
	my (@ary);
	
	# connect to database
	$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
	
	#build query
	$query = "UPDATE vehicle SET availability='$availability' WHERE vehicle_num='$vehicle_num'";
	
#	print "\nupdate_vehicle_availability: $query\n";

	#run query
	$rows = $dbh->do ($query);
	
	$dbh->disconnect();
	
#	print "\nrows: $rows\n";

	if(!defined($rows)) #if there was an error
	{
		$outcome = "0";  #return false
#		print "\nnot defined\n";
	}
	elsif($rows == "0E0") #if no rows were affected
	{
		$outcome = "0";
#		print "\ngot into the 0E0 Statement\n";
	}
	else
	{
		$outcome = "1";  # return true
	}

	return $outcome;
}

### NOTE: This function already exists in check out code ###
### I modified the sql statement since the time_interval is never used here
### have to generalize to check to see if the variable is present or not when
### building the statement
################################################################################
# Function: add_events_entry
# Description: Gathers all the variables and makes an entry in the events table
# Arguments: action, user_id, rack_num, dock_num, vehicle_num, [elapsed_time]
# Returns:
# Status: 75% (quasi-functional need to deal with the issue of optional arguments)
################################################################################
sub add_events_entry
{
	#arguments
	my $action = $_[0];
	my $user_id = $_[1];
	my $rack_num = $_[2];
	my $dock_num = $_[3];
	my $vehicle_num = $_[4];
#	my $time_interval = $_[5];

	my $rows;
	my $outcome;

	my ($dsn) = "DBI:mysql:bike_program:localhost";
	my ($user_name) = "bike";
	my ($password) = "5xMts6";
	my ($query);
	my ($dbh, $sth);
	my (@ary);

			
	# connect to database
	$dbh = DBI->connect ($dsn, $user_name, $password, { RaiseError => 1});
	
	$query = "INSERT INTO events SET action='$action', user_id='$user_id', rack_num='$rack_num', dock_num='$dock_num', vehicle_num='$vehicle_num'";

#	print "\nquery: $query\n";

	$rows = $dbh->do ($query);

	$dbh->disconnect();

	if(!defined($rows)) #if there was an error
	{
		$outcome = "0";  #return false
	}
	elsif($rows == "0E0") #if no rows were affected
	{
		$outcome = "0";
#		print "\ngot into the 0E0 Statement\n";
	}
	else
	{
		$outcome = "1";  # return true
	}

	return $outcome;
}