/* File:     validation.js
   Abstract: Client-side validation library
   Created:  January, 2003
*/

/* Model objects */
function Accommodation(checkinDate, checkoutDate, nights) {
  this.checkinDate = checkinDate || '';
  this.checkoutDate = checkoutDate || '';
  this.nights = nights || -1;
  this.rooms = new Array();
}

function CarRole() {
  if (this.instance == null) {
    this.instance = new Object();
  }
  this.instance.driver = 'DRIVER';
  this.instance.passenger = 'PASSENGER';
  
  return this.instance;
}
CarRole.instance = null;

function Car() {
  function car_addPassenger(pax, role) {
    this.passengers[this.passengers.length] = pax;
    this.passengerRoles[this.passengerRoles.length] = role;
  }
  
  function car_clearPassengers() {
    this.passengers.length = 0;
    this.passengerRoles.length = 0;
  }

  function car_driverCount() {
    var count = 0;
    
    for (var paxIndex = 0; paxIndex < this.passengers.length; ++paxIndex) {
      if (this.passengerRoles[paxIndex] == CarRole().driver) {
        count++;
      }  
    }
    return count;
  }
  
  this.passengers = new Array();
  this.passengerRoles = new Array();
  this.clearPassengers = car_clearPassengers;
  this.addPassenger = car_addPassenger;
  this.driverCount = car_driverCount;
}

function Flight() {
  function flight_setData(departureDate, departureTime, aFromLocation, aToLocation) {
    this.departureDate = departureDate || '';
    this.departureTime = departureTime || '';
    this.fromLocation = aFromLocation;
    this.toLocation = aToLocation;
  }
  
  this.departureDate = '';
  this.departureTime = '';
  this.fromLocation = -1;
  this.toLocation = -1;
  
  this.setData = flight_setData;
}

function Air() {
  function air_setPreferences(airline, fareType, seatType) {
    this.airline = airline || -1;
    this.fareType = fareType || -1;
    this.seatType = seatType || -1;  
  }
  this.airline = -1;
  this.fareType = -1;
  this.seatType = -1;
  this.departureFlight = new Flight();
  this.returnFlight = new Flight();
  
  this.setPreferences = air_setPreferences;
}

function Destination(locationId, nights) {
  function destination_addAccommodation(acc) {
    this.accommodations[this.accommodations.length] = acc;
  }
  function destination_addRentalCar(car) {
    this.rentalCars[this.rentalCars.length] = car;
  }
  
  function destination_clearRentalCars() {
    this.rentalCars.length = 0;
  }  
  
  this.id = -1;
  this.description = '';
  this.locationId = locationId || -1;
  this.checkInDate = '';
  this.checkOutDate = '';
  this.arrivalDate = '';
  this.departureDate = '';
  this.nights = nights || -1;
  this.accommodations = new Array();
  this.rentalCars = new Array();
  
  this.addAccommodation = destination_addAccommodation;
  this.addRentalCar = destination_addRentalCar;
  this.clearRentalCars = destination_clearRentalCars;
}

function PassengerType() {
  if(PassengerType.instance == null) {
  	PassengerType.instance = new Object();
  }
  PassengerType.instance.adult = 'ADULT';
  PassengerType.instance.child = 'CHILD';
  PassengerType.instance.infant = 'INFANT';
  
  return PassengerType.instance;
}
PassengerType.instance = null;

function Passenger(paxtype, age, id) {
  debug(DEBUG, 'paxtype', paxtype);
  this.type = paxtype || '';
  this.age = age || -1;
  this.id = id || -1;
}

function createAdult(id) {
  return new Passenger(PassengerType().adult, -1, id);
}

function createInfant(id) {
  return new Passenger(PassengerType().infant, -1, id);
}

function createChild(age, id) {
  return new Passenger(PassengerType().child, age, id);
}

function Room() {
  function room_addOccupant(occ) {
    this.occupants[this.occupants.length] = occ; 
  }
  
  function room_clearOccupants() {
    this.occupants.length = 0;
  }  
  
  function room_adultCount() {
    var count = 0;  
    for (var paxIndex = 0; paxIndex < this.occupants.length; paxIndex++) {
      var pax = this.occupants[paxIndex];
      if (pax.type == PassengerType().adult) {
        count++;
      }
    }
    return count;
  }
  
  this.occupants = new Array();
  this.addOccupant = room_addOccupant;
  this.adultCount = room_adultCount;
  this.clearOccupants = room_clearOccupants;
}

function Trip(region, duration, aDepartDate, aReturnDate) {
  function trip_addPassenger(pax) {
  	this.passengers[this.passengers.length] = pax;
  }

  function trip_addDestination(dest) {
  	this.destinations[this.destinations.length] = dest;	
  }

  function trip_clearDestinations() {
    trip.destinations.length = 0;
  }
  
  function trip_clearPassengers() {
    this.passengers.length = 0;
  }
      
  this.region = region || -1;
  this.duration = duration || -1;
  this.departDate = aDepartDate || ''; 
  this.returnDate = aReturnDate || ''; 
  this.destinations = new Array();
  this.passengers = new Array();
  this.roundTripAir = new Air();
  
  this.addPassenger = trip_addPassenger;
  this.addDestination = trip_addDestination;
  this.clearDestinations = trip_clearDestinations;
  this.clearPassengers = trip_clearPassengers;
}

/* Validator framework */

function Validator(algorithm) {
  function validator_default_validate(obj) {
    this.messages.length = 0;
    return false;
  }
  
  function validator_addMessage(msg) {
    this.messages[this.messages.length] = msg;
  }
  
  this.messages = new Array();
  this.validate = algorithm || validator_validate;
  this.addMessage = validator_addMessage;
}

function validateDate(entry) {
	  var mo, day, yr;
    var reShort = /\b\d{1,2}[\/]\d{1,2}[\/]\d{2}\b/;
    var reLong = /\b\d{1,2}[\/]\d{1,2}[\/]\d{4}\b/;
    var valid = (reLong.test(entry) || reShort.test(entry));
    if (valid) {
        var delimChar = "/";
        var delim1 = entry.indexOf(delimChar);
        var delim2 = entry.lastIndexOf(delimChar);
        mo = parseInt(entry.substring(0, delim1), 10);
        day = parseInt(entry.substring(delim1+1, delim2), 10);
        yr = parseInt(entry.substring(delim2+1), 10);
        
        // handle two-digit year
        if (yr < 100) {
          var today = new Date();
          var currCent = parseInt(today.getFullYear() / 100) * 100;
          var threshold = (today.getFullYear() + 15) - currCent;
          if (yr > threshold) {
            yr += currCent - 100;
          }
          else {
            yr += currCent;
          }
        }
                
        var testDate = new Date(yr, mo-1, day);

        if ((testDate.getDate() == day) &&
            ((testDate.getMonth() + 1) == mo) &&
            (testDate.getFullYear() == yr)) {
          return true;
        } 
    } 
    return false;
}

function validateInt(value) {
  var re = /\d+/;
  if(re.test(value)) {
    return true;
  }
  else {
    return false;
  }   
}

function ruleDatePair(validator, startDate, endDate) {
  if ((startDate == null) || (endDate == null)) {
    validator.addMessage('Please complete each date field');
    return false;
  }
  if (!validateDate(startDate) || !validateDate(endDate)) {
    validator.addMessage('Please enter all dates using the mm/dd/yy format.');    
    return false;
  }
  if ((!isFuture(startDate)) || (!isFuture(endDate))) {
    validator.addMessage('Please enter dates occuring at least one day in the future.');
    return false;
  }  
  
  if (daysBetween(new Date(startDate), new Date(endDate)) < 1) {
    validator.addMessage('Please enter dates occuring at least one day apart.');
    return false;
  }
  
  return true;
}

function validateAir(trip) {
  trace('validateAir()');
  this.messages.length = 0;
  
  var outbound = trip.roundTripAir.departureFlight;
  var inbound = trip.roundTripAir.returnFlight;

  debug(DEBUG, 'outbound.fromLocation', outbound.fromLocation);
  debug(DEBUG, 'outbound.toLocation', outbound.toLocation); 
  debug(DEBUG, 'inbound.fromLocation', inbound.fromLocation);
  debug(DEBUG, 'inbound.toLocation', inbound.toLocation);

  if (outbound.fromLocation <= 0) {
  	this.addMessage('Please select a departure location for your outbound flight.');
  	return false;
  }
  
  if (outbound.toLocation == -1) {
  	this.addMessage('Please select a destination for your outbound flight.');
  	return false;
  }
  
  if (inbound.fromLocation == -1)  {
  	this.addMessage('Please select a departure location for your return flight.');
  	return false;
  }

  if (inbound.toLocation <= 0) {
    this.addMessage('Please select a destination for your return flight.');
    return false;
  }
  
  debug(DEBUG, 'trip.roundTripAir.departureFlight.departureDate', trip.roundTripAir.departureFlight.departureDate);
  debug(DEBUG, 'trip.roundTripAir.returnFlight.departureDate', trip.roundTripAir.returnFlight.departureDate);
  if (!ruleDatePair(this, trip.roundTripAir.departureFlight.departureDate, 
      trip.roundTripAir.returnFlight.departureDate)) {
    return false;
  }
  
  return true;
}

function validateRentalCars(trip) {
  trace('validateRentalCars()');           
  if (trip == null) { trace('trip is null'); }
  
  for (var destIndex = 0; destIndex < trip.destinations.length; ++destIndex) {  
    var dest = trip.destinations[destIndex];
    
    // Initialize passenger driver counts
    var driverCounts = new Array();        
    for (var paxIndex = 0; paxIndex < trip.passengers.length; ++paxIndex) {
      var pax = trip.passengers[paxIndex];
      driverCounts[pax.id] = 0;
    }

    for (var carIndex = 0; carIndex < dest.rentalCars.length; ++carIndex) {
      var car = dest.rentalCars[carIndex];
      
      // Check for no driver
      if (car.driverCount() < 1) {
        this.addMessage('Please assign at least one driver to each rental car.');
        return false;
      }
      
      // Check for one passenger designed as a driver for multiple cars
      for (paxIndex = 0; paxIndex < car.passengers.length; ++paxIndex) {
        pax = car.passengers[paxIndex];
        
        // If this passenger is a driver increment their count
        if (car.passengerRoles[paxIndex] == CarRole().driver) {
          driverCounts[pax.id] = driverCounts[pax.id] + 1;
          
          // Check if they are driver for more than one car
          if (driverCounts[pax.id] > 1) {
            this.addMessage('A person may not be designated as the driver of more than one rental car.');
            return false;
          }
        }         
      }            
    }      
  }     
  return true;
}

function validateAccommodations(trip) {
  trace('validateAccommodations()');
  if (trip == null) { trace('trip is null'); }

  for (var destIndex = 0; destIndex < trip.destinations.length; destIndex++) {
    var dest = trip.destinations[destIndex];
    
    trace('dest.checkInDate: ' + dest.checkInDate);
    trace('dest.checkOutDate: ' + dest.checkOutDate);
    
    if (dateBefore(dest.checkOutDate, dest.checkInDate)) {
      this.addMessage('The specified check-out date occurs before the specified check-in date');
      return false;
    }
    if (dateBefore(dest.checkInDate, trip.departDate)) {
      this.addMessage('The specified check-in date occurs before the trip departure date.');
      return false;
    }
    
    if (!ruleDatePair(this, dest.checkInDate, dest.checkOutDate)) {
      return false;
    }
    
    trace('room count: ' + dest.accommodations.length);
    for (var roomIndex = 0; roomIndex < dest.accommodations.length; roomIndex++) {
      var room = dest.accommodations[roomIndex];
      if (room.adultCount() < 1) {
        this.addMessage('Please assign at least one adult to each room.');
        return false;
      }
    }
  }
  
  return true;
}

function validateTrip(trip) {
  trace('validateTrip()');
  this.messages.length = 0;
  
  if (trip == null) {
    this.addMessage('null Trip passed to validateTrip()');    
    return false;
  }
  if (!validateInt(trip.duration)) {
    debug(DEBUG, 'trip.duration', trip.duration);
    this.addMessage('Please enter a valid number of nights.');
    return;
  }
  if (trip.duration < 1) {    
    debug(DEBUG, 'trip.duration', trip.duration);
    this.addMessage('Please select a trip duration of at least one day.');
    return false;
  }  
  if (trip.destinations.length < 1) {
    this.addMessage('Please select at least one destination.');
    return false;
  }  
  if ((trip.departDate == '') || (trip.returnDate == '')) {
    debug(DEBUG, 'trip.departDate', trip.departDate);
    debug(DEBUG, 'trip.returnDate', trip.returnDate);
    this.addMessage('Please specify trip departure and return dates.');
    return false;
  }    
  var days = daysBetween(new Date(trip.departDate), new Date(trip.returnDate));
  if (days != trip.duration) {
    debug(DEBUG, 'trip.departDate', trip.departDate);
    debug(DEBUG, 'trip.returnDate', trip.returnDate);
    debug(DEBUG, 'daysBetween()', days);
    debug(DEBUG, 'trip.duration', trip.duration);
    this.addMessage('Please adjust your trip parameters so that the number of nights selected and the departure and return dates match.');
    return false;
  }
  
  if (!ruleDatePair(this, trip.departDate, trip.returnDate)) {
    return false;
  }
  
  var totalNights = 0;
  for (var i = 0; i < trip.destinations.length; ++i) {
    totalNights = totalNights + trip.destinations[i].nights;
  }
  if (totalNights != trip.duration) {
    debug(DEBUG, 'totalNights', totalNights);
    debug(DEBUG, 'trip.duration', trip.duration);
    this.addMessage('Please adjust the number of nights at each destination to add up to the total number of nights specified for the trip.');
    return false;
  }
    
  if (trip.passengers.length < 1) {
    debug(DEBUG, 'trip.passengers', trip.passengers);
    this.addMessage('Please select at least one passenger.');
    return false;
  }  
  if (trip.passengers.length > 6) {
    debug(DEBUG, 'trip.passengers', trip.passengers);
    this.addMessage('A traveling party larger than six passengers. Please adjust your parameters accordingly or contact your travel agent by phone to book your trip.');
    return false;
  }
  
  var adultCount = 0;
  var infantCount = 0;

  debug(DEBUG, 'trip.passengers.length', trip.passengers.length);
  for (var paxIndex = 0; paxIndex < trip.passengers.length; ++paxIndex) {
    debug(DEBUG, 'paxIndex', paxIndex);
    pax = trip.passengers[paxIndex];
    if (pax == null) {
      this.addMessage('trip.passengers contains null passenger');
      return false;
    }
    
    debug(DEBUG, 'pax.type', pax.type);
    if (pax.type == 'ADULT') {
      adultCount++;
    }
    else if (pax.type == 'INFANT') {
      infantCount++;
    }
    else if (pax.type == 'CHILD') {
      debug(DEBUG, 'pax.age', pax.age);
      if ((pax.age == '') || (!validateInt(pax.age)) || (pax.age == -1)) {
        this.addMessage('Please enter a valid age for each child.');
        return false;
      }
      /*
      if (pax.age > 17) {
        this.addMessage('Please mark children over 17 years of age as adults.');
        return false;
      }
      */
    }
    else {
      this.addMessage('\"' + pax.type + '\" is not a valid passenger type.');
      return false;
    }
  }
  if (adultCount <= 0) {
    this.addMessage('Please select at least one adult in the traveling party.');
    return false;
  }
  if (infantCount > adultCount) {
    this.addMessage('The number of infants in the traveling party must be less than or equal to the number of adults.');
    return false;
  } 
  
  return true;
}

