<?php

// define classes

class Protocol {
  var $id;
  var $name;

  function Protocol() {
  }

  function setName($n) {
    $this->name = $n;
  }

  function getName() {
    if ($this->name != NULL)
      return $this->name;
    else if ($this->getID() != NULL)
      return $this->getID();
  }

  function setID($i) {
    $this->id = $i;
  }

  function getID() {
    return $this->id;
  }
}

class Status {
  var $code;
  var $message;

  function Status() {
  }

  function setStatus($c) {
    $this->code = $c;
  }

  function getStatus() {
    return $this->code;
  }

  function setMessage($m) {
    $this->message = $m;
  }

  function getMessage() {
    return $this->message;
  }

  function getDescription() {
    switch ($this->code) {
      case 40071:
        return 'offline';
        break;
      case 40072:
        return 'online';
        break;
      case 40073:
        return 'away';
        break;
      case 40074:
        return 'DND';
        break;
      case 40075:
        return 'N/A';
        break;
      case 40076:
        return 'occupied';
        break;
      case 40077:
        return 'free for chat';
        break;
      case 40078:
        return 'invisible (error)';
        break;
      case 40079:
        return 'on the phone';
        break;
      case 40080:
        return 'out to lunch';
        break;
    }
    return "unknown";
  }
}

class ProtocolContact {
  var $protocol; // REF to a Protocol object
  var $protocolID; // ID on the protocol, UIN, e-mail, etc.
  var $nick;
  var $status; // REF to a Status object

  function ProtocolContact() {
  }

  function setProtocol(&$p) {
    $this->protocol =& $p;
  }

  function &getProtocol() {
    return $this->protocol;
  }
/* are the following 2 functions needed ??? */
  function setProtocolID($i) {
    $this->protocolID = $i;
  }

  function getProtocolID() {
    return $this->protocolID;
  }

  function setNick($n) {
    $this->nick = $n;
  }

  function getNick() {
    return $this->nick;
  }

  function setStatus(&$s) {
    $this->status = &$s;
  }

  function &getStatus() {
    return $this->status;
  }
}

class Contact {
  var $id;  // Miranda HANDLE to ID resolver
  var $pContacts;

  function Contact() {
    $this->pContacts = array();
  }

  function setID($i) {
    $this->id = $i;
  }

  function getID() {
    return $this->id;
  }

  function addPContact($p) {
    if ( ($pContacts != NULL) && (in_array($p, $pContacts)) ) {
      return;
    }
    $this->pContacts[] = $p;
  }
  
  function &getPContacts() {
    return $this->pContacts;
  }

  function &getLast(&$array) {
    if (!is_array($array)) return $array;
    if (!count($array)) return NULL;
    end($array);
    return $array[key($array)];
  }

  function &getLastPContact() {
    $last =& $this->getLast($this->pContacts);
    return $last;
  }
}


// parse the XML
define("PROTOCOL", "PROTOCOL");
define("PID", "ID");
define("PNAME", "NAME");
define("CONTACT", "CONTACT");
define("CID", "ID");
define("CPROTOCOL", "PROTOCOL");
define("CPROTOCOLID", "PROTOCOLID");
define("CNICK", "NICK");
define("CSTATUS", "STATUS");
define("SCODE", "CODE");
define("SMESSAGE", "MESSAGE");
define("UTIME", "UPDATETIME");
define("UINTERVAL", "UPDATEINTERVAL");

class MyXML {
  var $file;	/* file to parse */
  var $cur_path;	/* current path of XML tags */
  var $protocols;	/* array of so far read protocols */
  var $contacts;	/* array of so far read contacts */
  var $xml_parser;
  var $utime = 0;	/* update time */
  var $uinterval = 0;	/* update interval */

  function MyXML($f) {
    $this->cur_path = array();
    $this->protocols = array();
    $this->contacts = array();
    $this->setFile($f);
  }

  function setFile($f) {
    $this->file = $f;
  }

  function getFile() {
    return $this->file;
  }

  function &findProtocol($fProtocol) {
    if ($this->protocols == NULL) {
      return new Protocol();
    }
    foreach($this->protocols as $protocol) {
      if ($protocol->getID() == $fProtocol) {
        return $protocol;
      }
    }
    return NULL; // error: protocol not defined
  }

  function &getLast(&$array) {
    if (!is_array($array)) return $array;
    if (!count($array)) return NULL;
    end($array);
    return $array[key($array)];
  }
  
  function startElement($parser, $name, $attrs) {
    $this->cur_path[] = $name;
    if (count($this->cur_path) < 2) return;
    switch($name) {
      case PROTOCOL:
        $this->protocols[] =& new Protocol();
      break;
      case CONTACT:
        $newContact =& $this->getLast($this->contacts);
        if ($newContact != NULL) $thisID = $newContact->getID();
        if ( ($newContact == NULL) || ($newContact->getID() != $attrs[CONTACTID]) ) {
          $newContact = new Contact();
          $newContact->setID($attrs[CONTACTID]);
          $this->contacts[] =& $newContact;
        }
      break;
      case CSTATUS:
        $contact =& $this->getLast($this->contacts);
        $pContact =& $contact->getLastPContact();
        $newStatus = new Status();
        $pContact->setStatus(&$newStatus);
        break;
    }
  }

  function endElement($parser, $name) {
    array_pop($this->cur_path);
  }

  function characterData($parser, $rdata) {
    $data = trim($rdata, " ");
    switch (count($this->cur_path)) {
      case 2:
        switch($this->cur_path[1]) {
          case UTIME:
            $this->utime = $data;
            break;
          case UINTERVAL:
	    $this->uinterval = $data;
            break;
        }
        break;
      case 3:
        switch($this->cur_path[1]) {
	  case PROTOCOL:
            switch ($this->cur_path[2]) {
              case PID:
                $protocol =& $this->getLast($this->protocols);
                $protocol->setID($data);
                break;
              case PNAME:
                $protocol =& $this->getLast($this->protocols);
                $protocol->setName($data);
                break;
            }
            break;
          case CONTACT:
            switch ($this->cur_path[2]) {
              case CPROTOCOL:
                $newPContact = new ProtocolContact();
                $newProtocol =& $this->findProtocol($data);
                $newPContact->setProtocol(&$newProtocol);
                $contact =& $this->getLast($this->contacts);
                $contact->addPContact(&$newPContact);
                break;
              case CPROTOCOLID:
                $contact =& $this->getLast($this->contacts);
                $pContact =& $contact->getLastPContact();
                $pContact->setProtocolID($data);
                break;
              case CNICK:
                $contact =& $this->getLast($this->contacts);
                $pContact =& $contact->getLastPContact();
                $pContact->setNick($data);
                break;
            }
            break;
        }
        break;
      case 4:
        switch ($this->cur_path[3]) {
          case SCODE:   
            $contact =& $this->getLast($this->contacts);
            $pContact =& $contact->getLastPContact();
            $status =& $pContact->getStatus();
            $status->setStatus($data);
            break;
          case SMESSAGE:
            $contact =& $this->getLast($this->contacts);
            $pContact =& $contact->getLastPContact();
            $status =& $pContact->getStatus();
            $status->setMessage($data);
            break; 
        }
        break;
    }
  }

  function parse() {
    $this->xml_parser = xml_parser_create();
    xml_set_object($this->xml_parser, $this);
    xml_set_element_handler($this->xml_parser, "startElement", "endElement");
    xml_set_character_data_handler($this->xml_parser, "characterData");
    if (!($fp = fopen($this->file, "r"))) {
      die("could not open XML input");
    }
    while ($data = fread($fp, 4096)) {
      if (!xml_parse($this->xml_parser, $data, feof($fp))) {
        die(sprintf("XML error: %s at line %d",
                   xml_error_string(xml_get_error_code($this->xml_parser)),
                   xml_get_current_line_number($this->xml_parser)));
      }
    }
    xml_parser_free($this->xml_parser);
  }
  
  function getContacts() {
    return $this->contacts;
  }

  /* interface functions */
  function &findProtocolContact($fProtocol, $fID) {
    if ($this->contacts == NULL) {
      return NULL;
    }
    foreach($this->contacts as $contact) {
      if ($contact->getID() == $fID) {
        $pContacts = $contact->getPContacts();
        if ($pContacts == NULL) {
          continue;
        }
        foreach($pContacts as $pContact) {
          $protocol = $pContact->getProtocol();
          $protocolid = $protocol->getID();
          if ($protocolid == $fProtocol) {
            return $pContact;
          }
        }
      }
    }
  }

  /*
   * returns TRUE if data is valid or no delay was set.
   * returns FALSE is data is outdated (this means the data is older than
   * CURRENT_DATE - UPDATE_INTERVAL.
   */
  function dataIsValid() {

    if ($this->uinterval == 0)
      return TRUE;
    if ((strtotime(gmdate("Y-m-d H:i:s", time())) - strtotime($this->utime)) <= $this->uinterval*60)
      return TRUE;

    return FALSE;
  }

  /*
   * returns the status (Object) or NULL if unknown or outdated
   */
  function getStatus($fProtocol) {

    if (!$this->dataIsValid())
      return NULL;
    $pContact = $this->findProtocolContact($fProtocol, 0);
    if ($pContact == NULL) {
echo 'pcontact not found';
      return NULL;
}
    return $pContact->getStatus();
  }
 
  /*
   * returns status code (from statusmodes.h) or NULL if unknown or outdated
   */
  function getStatusCode($fProtocol) {

    if (!$this->dataIsValid())
      return 40071;
    $status = $this->getStatus($fProtocol);
    if ($status == NULL)
      return NULL;
    return $status->getStatus();
  }

  /*
   * returns status description or NULL if unknown or outdated
   */
  function getStatusDescription($fProtocol) {

    if (!$this->dataIsValid()) {
      $status = new Status();
      $status->setStatus(40071);
    }
    else {
      $status = $this->getStatus($fProtocol);
      if ($status == NULL)
        return NULL;
    }
    return $status->getDescription();
  }

  /*
   * returns status message or NULL if unknown or outdated
   * note: the status message set is the (default) status message
   * set by Miranda. Any status message set by another plugin will *not*
   * send by WebAware.
   */
  function getStatusMessage($fProtocol) {
    
    $status = $this->getStatus($fProtocol);
    if ($status == NULL)
      return NULL;
    return $status->getMessage();
  }

  /*
   * returns filename of the status image
   */
  function getStatusImage($fProtocol) {

    $ext = '.png';
    if (!$this->dataIsValid())
      $sImage = sprintf("%s_offline%s", $fProtocol, $ext);
    else {
      $status = $this->getStatusCode($fProtocol);
      $sImage = $fProtocol."/".$status.$ext;
    }
    return $sImage;
  }

  /*
   * returns the last update time or NULL if unavailable 
   */
  function getUpdateTime() {

    return strtotime($this->utime);
  }    


}
?>