/**
  * Reduces the points balance for the user identified by $user_id
  *
  * @since 1.0
  * @param int $user_id the user identifier
  * @param int $points the points to reduce, ie 75
  * @param string $event_type the type of event responsible
  * @param mixed $data optional arbitrary data to associate with the log for this action
  * @param int $order_id optional order identifier, if this action is associated with a particular order
  * @return boolean true if the points are successfully reduced from the user's balance
  */
 public static function decrease_points($user_id, $points, $event_type, $data = null, $order_id = null)
 {
     global $wc_points_rewards, $wpdb;
     // ensure the user exists
     $user = get_userdata($user_id);
     if (false === $user) {
         return false;
     }
     $points = apply_filters('wc_points_rewards_decrease_points', $points, $user_id, $event_type, $data, $order_id);
     // get any existing points records
     $query = "SELECT * FROM {$wc_points_rewards->user_points_db_tablename} WHERE user_id = %d and points_balance != 0 ORDER BY date ASC";
     $user_points = $wpdb->get_results($wpdb->prepare($query, $user_id));
     // no non-zero records, so create a new one
     if (empty($user_points)) {
         $_data = array('user_id' => $user_id, 'points' => -$points, 'points_balance' => -$points, 'date' => current_time('mysql', 1));
         $format = array('%d', '%d', '%d', '%s');
         if ($order_id) {
             $_data['order_id'] = $order_id;
             $format[] = '%d';
         }
         // create the negative-balance user points record
         $wpdb->insert($wc_points_rewards->user_points_db_tablename, $_data, $format);
     } elseif (count($user_points) > 0) {
         // existing non-zero points records
         $points_difference = -$points;
         // the goal is to get each existing record as close to zero as possible, oldest to newest
         foreach ($user_points as $index => &$_points) {
             if ($_points->points_balance > 0 && $points_difference < 0) {
                 $_points->points_balance += $points_difference;
                 if ($_points->points_balance >= 0 || count($user_points) - 1 == $index) {
                     // used up all of points_difference, or reached the newest user points record which therefore receives the remaining balance
                     $points_difference = 0;
                     break;
                 } else {
                     // still have more points balance to distribute
                     $points_difference = $_points->points_balance;
                     $_points->points_balance = 0;
                 }
             } elseif (count($user_points) - 1 == $index && 0 != $points_difference) {
                 // if we made it here, assign all remaining points to the final record and we're done
                 $_points->points_balance += $points_difference;
                 $points_difference = 0;
             }
         }
         // update any affected rows
         for ($i = 0; $i <= $index; $i++) {
             $wpdb->update($wc_points_rewards->user_points_db_tablename, array('points_balance' => $user_points[$i]->points_balance), array('id' => $user_points[$i]->id), array('%d'), array('%d'));
         }
     }
     // update the current points balance user meta
     $points_balance = (int) get_user_meta($user_id, 'wc_points_balance');
     update_user_meta($user_id, 'wc_points_balance', $points_balance - $points);
     // log the points change
     $args = array('user_id' => $user_id, 'points' => -$points, 'event_type' => $event_type);
     // optional associated order
     if ($order_id) {
         $args['order_id'] = $order_id;
     }
     // optional associated data
     if ($data) {
         $args['data'] = $data;
     }
     // log the event
     WC_Points_Rewards_Points_Log::add_log_entry($args);
     do_action('wc_points_rewards_after_reduce_points', $user_id, $points_balance);
     // always return true for now
     return true;
 }