/**
  * validate
  * Run all the validation rules we have defined against a (hopefully
  * normalized) DonationInterface data set.
  * @param GatewayType $gateway
  * @param array $data Normalized donation data.
  * @param array $check_not_empty An array of fields to do empty validation
  * on. If this is not populated, no fields will throw errors for being empty,
  * UNLESS they are required for a field that uses them for more complex
  * validation (the 'calculated' phase).
  * @throws BadMethodCallException
  * @return array An array of errors in a format ready for any derivative of
  * the main DonationInterface Form class to display. The array will be empty
  * if no errors were generated and everything passed OK.
  */
 public static function validate(GatewayType $gateway, $data, $check_not_empty = array())
 {
     //return the array of errors that should be generated on validate.
     //just the same way you'd do it if you were a form passing the error array around.
     /**
      * We need to run the validation in an order that makes sense.
      *
      * First: If we need to validate that some things are not empty, do that.
      * Second: Do regular data type validation on things that are not empty.
      * Third: Do validation that depends on multiple fields (making sure you
      * validated that all the required fields exist on step 1).
      *
      * How about we build an array of shit to do,
      * look at it to make sure it's complete, and in order...
      * ...and do it.
      */
     // Define all default validations.
     $validations = array('not_empty' => array('country', 'currency_code', 'gateway'), 'valid_type' => array('_cache_' => 'validate_boolean', 'account_number' => 'validate_numeric', 'anonymous' => 'validate_boolean', 'contribution_tracking_id' => 'validate_numeric', 'currency_code' => 'validate_alphanumeric', 'gateway' => 'validate_alphanumeric', 'numAttempt' => 'validate_numeric', 'optout' => 'validate_boolean', 'posted' => 'validate_boolean', 'recurring' => 'validate_boolean'), 'calculated' => array('gateway' => 'validate_gateway', 'address' => 'validate_address', 'city' => 'validate_address', 'country' => 'validate_country_allowed', 'email' => 'validate_email', 'street' => 'validate_address', 'currency_code' => 'validate_currency_code', 'fname' => 'validate_name', 'lname' => 'validate_name', 'name' => 'validate_name'));
     // Additional fields we should check for emptiness.
     if ($check_not_empty) {
         $validations['not_empty'] = array_unique(array_merge($check_not_empty, $validations['not_empty']));
     }
     $errors = array();
     $results = array();
     $language = DataValidator::guessLanguage($data);
     if (empty($data['country'])) {
         $country = null;
     } else {
         $country = $data['country'];
     }
     foreach ($validations as $phase => $fields) {
         foreach ($fields as $key => $custom) {
             // Here we decode list vs map elements.
             if (is_numeric($key)) {
                 $field = $custom;
                 $validation_function = "validate_{$phase}";
             } else {
                 $field = $key;
                 $validation_function = $custom;
             }
             if (!isset($data[$field])) {
                 if ($phase !== 'not_empty') {
                     // Skip if not required and nothing to validate.
                     continue;
                 } else {
                     // Stuff with nothing.
                     $data[$field] = null;
                 }
             }
             // Skip if we've already determined this field group is invalid.
             $errorToken = self::getErrorToken($field);
             if (array_key_exists($errorToken, $errors)) {
                 continue;
             }
             // Prepare to call the thing.
             $callable = array('DataValidator', $validation_function);
             if (!is_callable($callable)) {
                 throw new BadMethodCallException(__FUNCTION__ . " BAD PROGRAMMER. No function {$validation_function} for {$field}");
             }
             $result = null;
             // Handle special cases.
             switch ($validation_function) {
                 case 'validate_currency_code':
                     $result = call_user_func($callable, $data[$field], $gateway->getCurrencies($data));
                     break;
                 default:
                     $result = call_user_func($callable, $data[$field]);
                     break;
             }
             // Store results.
             $results[$phase][$field] = $result;
             if ($result === false) {
                 // We did the check, and it failed.
                 $errors[$errorToken] = self::getErrorMessage($field, $phase, $language, $country);
             }
         }
     }
     return $errors;
 }