<?php
// $Id: messaging_message.class.inc,v 1.1.2.2 2010-03-09 15:52:45 jareyero Exp $
/**
 * @file
 * Drupal Messaging Framework - Default class file
 */

/**
 * Message class
 * 
 * This is a message with all the information, ready for sending
 */
class Messaging_Message {
  // Unique id
  public $mqid;
  // Message type: outgoing, incoming
  public $type;
  // Sending method
  public $method;
  // Message source, mixed data
  public $source;
  // Language code
  public $language;
  // Sending method specific params
  public $params;
  // Message destination
  public $uid;
  public $destination;
  public $user = NULL;
  // Used when sending to multiple destinations
  public $destinations;
  // Rendered message parts
  public $subject;
  public $body;
  public $files;
  // Sender information
  public $sender;
  public $sender_name;
  public $sender_address;
  public $sender_account = NULL;
  // Timestamps
  public $created = 0;
  public $sent = 0;
  // Processing parameters
  public $queue = 0;
  public $cron = 0; 
  public $log;
  // Error code, error text
  public $error = 0;
  public $error_msg;
  // Temporary variables, not stored
  public $discard;
  // Message status
  public $prepared = FALSE;
  public $rendered = FALSE;
  public $queued = FALSE;
  public $redirect = FALSE;
  public $retry = FALSE;
  // Result after each operation
  public $process;
  public $result;
  // Test message, not really for sending
  public $test = FALSE;
  public $success = TRUE;
  // Template used to build this message
  // @see Messaging_Message_Template
  protected $template;
  
  /**
   * Constructor, with predefined array of data
   */ 
  function __construct($data = array()) {
    foreach ($data as $key => $value) {
      $this->$key = $value;
    }
    // Set logging option
    if (!isset($this->log)) {
      $this->log = variable_get('messaging_log', 0);
    }
    // If retrieved from store, populate some data
    if (!empty($this->mqid)) {
      $this->prepared = $this->method;
      $this->rendered = $this->method;
      if ($this->uid && empty($this->account)) {
        $this->account = messaging_load_user($this->uid);       
      }
      if ($this->sender && empty($this->sender_account)) {
        $this->sender_account = messaging_load_user($this->sender);
      }
    }
  }
  /**
   * Build this message for method, destination
   */
  function build($method, $destination) {
    $this->method = $method;
    $this->destination = $destination;
    return $this;
  }
  /**
   * Prepare message for sending method, before rendering
   */
  function prepare() {
    // If the sending method has changed, prepare again
    if (!$this->prepared || $this->prepared != $this->method) {
      $this->method_invoke('prepare');
      // Provides a hook for other modules to modify the message
      drupal_alter('message', $this);
      $this->prepared = $this->method;
      $this->rendered = FALSE;
    }
  }
  /**
   * Render the message for sending method
   */
  function render() {
    if (!$this->rendered) {
      if (isset($this->template)) {
        $this->subject = $this->template->get_subject($this->method, $this->get_language());
        $this->body = $this->template->get_body($this->method, $this->get_language());
      }
      $this->method_invoke('render');
      $this->rendered = TRUE;
    }
  }

  /**
   * Send message using method, destination
   */
  function send() {
    if (!empty($this->test)) {
      // We are doing a test run so we don't send the message
      $this->test();
      $this->result = TRUE;  
    }
    else {
      // Run full processing, will end up on send, queue or error
      $this->process();
      $this->done();
    }
    return $this->result;
  }
  
  /**
   * Testing message, just log data
   */
  function test() {
    $this->prepare();
    $this->render();
    messaging_log('Emulating message sending (test run)', array('message' => $this));
  }
  /**
   * Process message to the end (queue, send, log
   */
  function process() {
    $this->result = FALSE;
    // We try more than once if message is redirected
    do {
      $this->process = TRUE;
      $this->redirect = FALSE;
      $this->retry = FALSE;
      $this->prepare();
      if ($this->queue) {
        $this->queue();
      }
      elseif ($this->process && !$this->redirect) {
        $this->post();
      }        
    }
    while ($this->redirect || $this->retry);

    return $this->result;
  }

  /**
   * Actual message sending through sending method
   */
  function post() {
    // Disable queueing, but it can be restored by next operations
    $this->render();
    $this->queue = 0;
    // Prepare operations to invoke on send method
    $this->result = $this->method_process('presend', 'send', 'aftersend');
    return $this->result;
  }

  /*
   * Queue the message
   */
  function queue() {
    $this->result = TRUE;
    if (!$this->queued) {
      $this->queue = 1;
      $this->render();
      $this->result = $this->method_process('queue', 'afterqueue');
    }
    return $this->result;
  }

  /**
   * Wrapping up, store if we need logging
   */
  function done() {
    if (($this->queue && !$this->queued)) {
      $this->queue();
    }
    elseif($this->log || $this->error) {
      $this->store();
    }  
  }
  
  /**
   * Save to store if not saved yet
   */
  function store() {
    if (empty($this->mqid)) {
      $this->save();
    }
  }
  /**
   * Save to store / update
   */
  function save() {
    // Make sure this is rendered before saving
    $this->prepare();
    $this->render();
    if (empty($this->created)) {
      $this->created = time();
    }
    messaging_store('save', $this);
  }
  /**
   * Set destination user and find method's destination for this user if not set
   * 
   * @param $account
   *   User account object
   * @param $destination
   *   Optional destination
   */
  function set_user($account, $destination = NULL) {
    $this->uid = $account->uid;
    $this->account = $account;
    // Set destination if needed
    if (isset($destination)) {
      $this->destination = $destination;
    }
    elseif (!isset($this->destination)) {
      $this->destination = messaging_user_destination($account, $this->method);
    }
    // Set user language if not set
    if (!isset($this->language) && ($preferred = user_preferred_language($account))) {
      $this->language = $preferred->language;
    }
  }
  /**
   * Set sender account and related properties
   * 
   * @param $account
   *   Sender user account
   * @param $address
   *   Opational method's address for this account
   */
  function set_sender($account, $address = NULL) {
    $this->sender = $account->uid;
    $this->sender_name = $account->name;
    $this->sender_account = $account;
    if (isset($address)) {
      $this->sender_address = $address;
    }
    elseif (!isset($this->sender_address)) {
      $this->sender_address = messaging_user_destination($account, $this->method);
    }
  }
  /**
   * Get sending method parameters
   */
  function get_params($method = NULL) {
    $method = $method ? $method : $this->method;
    // First get specific parameters for this sending method
    $params = isset($this->params[$method]) ? $this->params[$method] : array();
    // Check for specific parameters for this method group
    $group = messaging_method_info($method, 'group');
    if ($group && !empty($this->params[$group])) {
      $params += $this->params[$group];
    }
    return $params;
  }
  /**
   * Get language as object
   */
  function get_language() {
    if (isset($this->language) && ($languages = language_list()) && isset($languages[$this->language])) {
      return $languages[$this->language];
    }
    else {
      return language_default();
    }
  }
  /**
   * Delete if already on store
   */
  function delete() {
    if (!empty($this->mqid)) {
      $result = messaging_store('delete', $this->mqid);
      unset($this->mqid);
      return $result;
    }
  }

  /**
   * Set error condition and stop processing
   * 
   * @param $text
   *   Error message to be stored
   */
  function set_error($text, $code = 1) {
    // This will stop processing if we are in the middle of anything
    $this->process = FALSE;
    $this->result = FALSE;
    $this->error = $code;
    $this->error_msg = $text;
  } 
  /**
   * Invoke multiple method callbacks
   */
  function method_process() {
    $callbacks = func_get_args();
    $this->process = TRUE;
    $this->result = TRUE;
    while ($this->process && ($key = array_shift($callbacks))) {
      $result = $this->method_invoke($key);
    }
    // Success if all callbacks processed and result is not FALSE
    if ($result === FALSE || !empty($callbacks)) {
      $this->result = FALSE;
    }
    return  $this->result;      
  }
  /**
   * Invoke method callback with this message
   */
  function method_invoke($op) {
    $function = 'message_' . $op;
    if ($method = messaging_send_method($this->method)) {
      return $method->$function($this);
    }
    else {
      $this->set_error('Sending method not available');
    }
  }
  /**
   * Get list of fields to serialize for storage
   */
  function data_fields() {
    return array('params', 'files', 'parts', 'params', 'sender_name', 'error_msg');
  }
  // Magic function, format as string
  public function __toString() {
    $subject = $this->subject ? check_plain($this->subject) : '<none>';
    return "Message: method=$this->method, destination=$this->destination, subject=$subject";
  }
}

