<?php

require_once 'ucdb/Enum.php';
class Api_UnifyController extends Zend_Controller_Action 
{
	
	public function init()
	{
		//TODO: extend authentication to support multiple user levels/roles/groups 
		require_once 'application/models/api.php';
		$this->api = new api();
		
		$this->baseUrl = $this->_request->getBaseUrl();
		$this->appBaseUrl = $this->_request->getBaseUrl() . (strpos($this->_request->getBaseUrl(),'index.php')?'':'/index.php');
		$this->siteBaseUrl = Zend_Registry::get('siteBaseUrl');
		$this->moduleBaseUrl = $this->appBaseUrl . ($this->_request->module == 'default'?'':DIRECTORY_SEPARATOR . $this->_request->module);
		
		$this->_helper->viewRenderer->setNoRender();
		
		$this->restserver = new Zend_Rest_Server();
		$this->restserver->setClass('api');

		// Load all the params
		$this->requestParams = $this->getRequest()->getParams();
        
		if( !Atmail_FormAuth::authenticated() )
			throw new Exception( "Access denied" );

		// Read from Zend-Auth
		$this->userData = Zend_Auth::getInstance()->getStorage()->read();
		$this->userData = Atmail_Password::processUser($this->userData);
			
		// Load the session
		$this->session = new Zend_Session_Namespace(ATMAIL_NAMESPACE);

		// Load the system settings
		$this->_globalConfig = Zend_Registry::get('config')->global;

		// Find the users settings ( used for theme )
		$this->UserSettings = Zend_Registry::get('UserSettings');

		$this->log = Zend_Registry::get('log');
		
		// Used to determine the contact in the global addressbook
		$this->Account = $this->userData['Account'];

		// Load the mail class
		//handle folder change requests here (allowing change from all actions/events) so that correct cache can be loaded (correct folder)
		$this->_currentConfig = array();
		$this->_contactCache = array();
        
		$this->_mailStoreConfig = array_merge($this->_globalConfig, $this->userData, $this->_currentConfig, $this->UserSettings);
		
		//check if SSL enabled in UserSettings
		$this->_mailStoreConfig['ssl'] = ($this->_mailStoreConfig['UseSSL']=='1'?'SSL':'');

		$this->AtmailMailStorage = Atmail_Mail_Storage::getInstance($this->_mailStoreConfig);
		$this->AtmailMailStorageMain = &$this->AtmailMailStorage->getMailStore($this->_mailStoreConfig['namespaceName']);

		$this->carrier = new serviceproviderapi( array('Account' => $this->Account) );
		$this->mobileAccounts = $this->carrier->listAccounts();
		
		//TODO: dynamically build list supported services from services folder having models to access such services
		$this->servicesAvailable = array('SMS' => false, 'Voicemail' => false, 'SIP' => false, 'SocialNetwork' => false, 'Chat' => false);
		$this->_firstSIPAccount = null;
		foreach( $this->mobileAccounts as $mobileAccount )
		{
			$this->servicesAvailable[$mobileAccount['Type']] = true;
			if( $mobileAccount['Type'] == "SIP" )
				$this->_firstSIPAccount = $mobileAccount;
			if( $mobileAccount['Provider'] == "Facebook" )
				$this->servicesAvailable['Chat'] = true;
		}
		
		//at least one service must be avaialble to current user to use Unify
		if( count($this->mobileAccounts) == 0 )
			throw new Exception('No Unify services available for this account');
			
		$this->mobileAccount = null;
		//pid = providerid or null
		$this->pid = null;
		if( array_key_exists('pid', $this->requestParams) )
		{
			
			$this->pid = $this->requestParams['pid'];
			foreach( $this->mobileAccounts as $mobileAccount )
			{
				
				if( $mobileAccount['id'] == $this->pid )
					$this->mobileAccount = $mobileAccount;
			}
			if( $this->mobileAccount === null )
				throw new Exception('invalid pid specified');
			
		}

		//if type specified find first matching type 
		//TODO refactor to support more logical usage
		$this->type = null;
		if( array_key_exists('type', $this->requestParams) )
		{
			
			
			if( !array_key_exists($this->requestParams['type'], $this->servicesAvailable) )
				throw new Exception('service type not available');
			
			if( $this->pid !== null )
			{
				
				if( $this->mobileAccount['Type'] == $this->requestParams['type'] )
					$this->type = $this->requestParams['type'];
				else
					throw new Exception('Error: Specified pid does not support specified type');
				
			}
			else // no specific pid mentioned so find first matching provider providing type
			{
				$matchingTypeC = 0;
				$firstMatchedAccount = null;
				foreach( $this->mobileAccounts as $mobileAccount )
				{
				
					if( $mobileAccount['Type'] == $this->requestParams['type'] )
					{
						
						$this->type = $this->requestParams['type'];
						if( $matchingTypeC == 0 )
							$firstMatchedAccount = $mobileAccount;
						$matchingTypeC++;
						
					}
				}
				if( $matchingTypeC == 0 )
					throw new Exception("Error: No matching provider found for specified type");
				else if( $matchingTypeC == 1 )
				{
					$this->pid = $firstMatchedAccount['id'];
					$this->mobileAccount = $firstMatchedAccount;
				}
				
			}
			
		}

			
		
		//by the end of init you have validated or default $this->pid, $this->mobileAccount, and $this->type or Exception
		//if pid is null then not restricted to single pid
		//if type = null then not restricted to specific type (e.g. Unified timeline requests)
		//if a specific pid or type is specified then instantiate providers with only that account, else instantiate with all users' available accounts
		$providerAccounts = array();
		if( $this->pid !== null ) 
			$providerAccounts[] = $this->mobileAccount;
		else if( $this->type !== null )
		{
			
			foreach( $this->mobileAccounts as $account )
				if( $account['Type'] == $this->type )
					$providerAccounts[] = $account;
			
		}
		else 
			$providerAccounts = $this->mobileAccounts;
				
		$this->providers = new Atmail_Providers($providerAccounts);
		
		
		$this->dbAdapter = Zend_Registry::get('dbAdapter'); //TMP until moved into propper models
		
		$this->getResponse()->setHeader('Content-Type','text/javascript; charset=UTF-8');
		$this->_helper->viewRenderer->setNoRender();
		if( !isset($this->_globalConfig['UCDBURL']) || $this->_globalConfig['UCDBURL'] == null || $this->_globalConfig['UCDBURL'] == "")
			$this->_globalConfig['UCDBURL'] = "localhost";
		
		$this->_client = new Zend_Rest_Client( "http://" . $this->_globalConfig['UCDBURL'] . ":33122" );
		$this->UCDBSIDFieldName = "ucdb_session";
		$ad = $this->_client->getHttpClient();
		$ad->setConfig(array('strict' => false));
		$ad->setHeaders('remote_addr', $_SERVER['REMOTE_ADDR']);

		$this->ucdbEnabled = ( $this->_globalConfig['UCDBEnabled'] == '1' );

	} 
	
	public function indexAction() { throw new Exception('Invalid Call'); }
	
	public function listservicesAction() {
	
		$data = array();
		foreach( $this->mobileAccounts as $v )
		{
			if( $this->type !== null && $v['Type'] != $this->type)
				continue;
			if( $this->pid !== null && $v['id'] != $this->pid)
				continue;
			
			unset($v['Account']);
			unset($v['SIP']);
			unset($v['SMS']);
			unset($v['Voicemail']);
			unset($v['Password']);
			$data[] = $v;
			
		}
		//$this->getResponse()->setHeader('Content-Type','application/json; charset=UTF-8');
		if( array_key_exists('pretty', $this->requestParams) )
			echo Zend_Json::prettyPrint(Zend_Json::encode($data), array("indent" => "    "));
		else
			echo Zend_Json::encode($data);
	
	}
	
	// Request a stream for a specified userId	
	public function streamidAction() {
	
		$id = $this->requestParams['id'];

		$stream = array();
		$contacts = new contacts( array('Account' => $this->Account) );

		$contact = $contacts->getContact($id);

		if($contact['UserEmail'] !== null)	{
			
			// Fetch emails first
			$connectResult = $this->AtmailMailStorageMain->connect();
			$this->AtmailMailStorageMain->selectFolder("INBOX");
			
			// Loop through all the contacts email addresses
			foreach( array('UserEmail', 'UserEmail2', 'UserEmail3', 'UserEmail4') as $email ) {

				if($contact[$email] !== null) {
					$preparedSearchQuery[] = array('field' => 'from', 'value' => $contact[$email]);						
				}
				
			}
			
			// Run the actual search
			$searchResults = $this->AtmailMailStorageMain->search($preparedSearchQuery);

			$messages = $this->AtmailMailStorageMain->getBasicHeaders($searchResults);
			
			foreach($messages as $message) 
			{

				$ts = UnixGMTToISO8601TZ(strtotime( $message['INTERNALDATE']));
				
				// Now fetch the data into the UC JSON format
				$seen = array_key_exists("\Seen", $message['FLAGS']);
				$stream[] = array(
					'pid' => 'Email', 
					'providerType' => 'Email', 
					'providerName' => 'Email', 
					'providerDescription' => 'Main Email Account', 
					'id' => $message['UID'], 
					'receivedDateISO8601' => $ts,
					'timeAgo' => timeago(strtotime($ts)),
					'seen' => $seen, 
					'from' => array(
						'id' => $message['_basicHeaders']['from_email'],
						'name' => $message['_basicHeaders']['from_name'], 
						'photoUrl' => (empty($contact[0])?'':$this->siteBaseUrl . 'index.php/mail/contacts/viewphoto/id/' .$contact[0]['id']), //'/resize/1/width/37/height/37/nocrop/1';
					),
					'subject' => $message['_basicHeaders']['subject'],
					'messageType' => 'Email', 
					'summary' => $message['_preview']
				);
				
			}
			
		}

		$results = array();
		
		foreach( $this->mobileAccounts as $providerAccount )
		{
			
			//if( !in_array($providerAccount['Type'], array('SocialNetwork')) ) //, 'SMS', 'Voicemail)) )
			//	continue;
			if( $providerAccount['Provider'] == 'Facebook' && empty($contact['FacebookId']) )
				continue;
			else if( $providerAccount['Provider'] == 'Twitter' && empty($contact['TwitterId']) )
				continue;
			else if( $providerAccount['Type'] == 'SMS' || $providerAccount['Type'] == 'Voicemail' ) //dont include SMS or VM in stream yet.
				continue;
			else if( $providerAccount['Type'] == 'SMS' && (empty($contact['UserHomePhone']) && empty($contact['UserHomeMobile']) && empty($contact['UserWorkPhone']) && empty($contact['UserWorkMobile'])) )
				continue;
			else if( $providerAccount['Type'] == 'Voicemail' && (empty($contact['UserHomePhone']) && empty($contact['UserHomeMobile']) && empty($contact['UserWorkPhone']) && empty($contact['UserWorkMobile'])) )
				continue;
			
			$this->requestParams['pid'] = $providerAccount['id'];
			$this->requestParams['raw'] = true;
				
			if( $providerAccount['Provider'] == 'Facebook' ) 
			{
				$id = $contact['FacebookId'];				
				$folder = 'feed';
			}
			else if( $providerAccount['Provider'] == 'Twitter' ) 
			{
				$id = $contact['TwitterId'];
				$folder = 'statuses_user_timeline';
			}
			else if( $providerAccount['Type'] == 'SMS' || $providerAccount['Type'] == 'Voicemail' )
			{
				
				$id = array();
				if( !empty($contact['UserHomePhone']) )
					$id[] = $contact['UserHomePhone'];
				if( !empty($contact['UserHomeMobile']) )
					$id[] = $contact['UserHomeMobile'];
				if( !empty($contact['UserWorkPhone']) )
					$id[] = $contact['UserWorkPhone'];
				if( !empty($contact['UserWorkMobile']) )
					$id[] = $contact['UserWorkMobile'];
				
			}
			
			$stream[] = $this->providers->getList( array('id' => $id, 'pid' => $providerAccount['id'], 'limit' => 5, 'folder' => $folder, 'trim' => 1) );
		}

		//TODO: sort into one stream array
		if( array_key_exists('pretty', $this->requestParams) )
			echo Zend_Json::prettyPrint(Zend_Json::encode($stream), array("indent" => "    "));
		else
			echo Zend_Json::encode($stream);
		
	}
	
	// Request the broad data stream
	public function streamAction() {
			
		//first get email list
		$previousFolder = $this->AtmailMailStorageMain->getCurrentFolder();
		$raw = (array_key_exists('raw', $this->requestParams) && $this->requestParams['raw'] == 'true');
		$folderMetrics = $this->AtmailMailStorageMain->selectFolder("INBOX");
		$connectResult = $this->AtmailMailStorageMain->connect();

		$contacts = new contacts( array('Account' => $this->Account) );

		// Fetch emails
		$origionalRequestedAbookId = null;
		if( !empty($this->requestParams['id']) )
		{
			
			$origionalRequestedAbookId = $this->requestParams['id'];
			$contact = $contacts->getContact($this->requestParams['id']);
			if( $contact == null || empty($contact) )
				throw new Exception('Contact id does not exist');

			// Loop through all the contacts email addresses
			$preparedSearchQuery = array();
			foreach( array('UserEmail', 'UserEmail2', 'UserEmail3', 'UserEmail4') as $email ) 
			{

				if( $contact[$email] != null) 
				{
					
					$preparedSearchQuery[] = array( 'field' => 'from', 'value' => $contact[$email] );
				
				}
				
			}
			if( count($preparedSearchQuery) > 0)
			{
				// Run the actual search
				$searchResults = $this->AtmailMailStorageMain->search($preparedSearchQuery);
				if( count($searchResults) > 0 )
					$messages = $this->AtmailMailStorageMain->getBasicHeaders($searchResults);
				else
					$messages = array();
			}
			else
				$messages = array();
		}
		else
			$messages = $this->AtmailMailStorageMain->getBasicHeaders(1, 25);

		if( $previousFolder != "INBOX" )
			$this->AtmailMailStorageMain->selectFolder($previousFolder);
		
		$stream = array();
		
		foreach( $messages as $message )
		{
			
			$ts = UnixGMTToISO8601TZ( strtotime($message['INTERNALDATE']) );
			if( empty($this->requestParams['id']) )
				$contact = $contacts->searchFuzzyContact( array('UserEmail' => $message['_basicHeaders']['from_email']), null );
			
			$stream[] = array(
				'pid' => 'Email', 
				'providerType' => 'Email', 
				'providerName' => 'Email', 
				'providerDescription' => 'Main Email Account', 
				'id' => $message['UID'], 
				'receivedDateISO8601' => $ts,
				'timeAgo' => timeago(strtotime($ts)),
				'seen' => array_key_exists("\Seen", $message['FLAGS']), 
				'from' => array(
					'id' => $message['_basicHeaders']['from_email'],
					'name' => $message['_basicHeaders']['from_name'], 
					'photoUrl' => (empty($contact[0])?'':$this->siteBaseUrl . 'index.php/mail/contacts/viewphoto/id/' .$contact[0]['id'] . '/resize/1/width/37/height/37/nocrop/1'), //'/resize/1/width/37/height/37/nocrop/1';
				),
				'subject' => $message['_basicHeaders']['subject'],
				'messageType' => 'Email', 
				'summary' => $message['_preview'], 
				'avater' => (empty($contact[0])?'':$this->siteBaseUrl . 'index.php/mail/contacts/viewphoto/id/' .$contact[0]['id'] . '/resize/1/width/37/height/37/nocrop/1'), //'/resize/1/width/37/height/37/nocrop/1';
			);
			
		}
		//TODO: threads instead of plain list
		
		foreach( $this->mobileAccounts as $providerAccount )
		{
			
			if( !in_array($providerAccount['Type'], array('SocialNetwork', 'SMS', 'Voicemail')) )
				continue;
			
			$this->requestParams['pid'] = $providerAccount['id'];
			$this->requestParams['raw'] = true;
			
			//dont include SMS or VM in stream yet.
			if( $providerAccount['Type'] == 'SMS' || $providerAccount['Type'] == 'Voicemail' ) 
				continue;
				
			if( $origionalRequestedAbookId != null )
			{
				//we have $contact
				if( $providerAccount['Provider'] == 'Facebook' && empty($contact['FacebookId']) )
						continue;
				else if( $providerAccount['Provider'] == 'Twitter' && empty($contact['TwitterId']) )
						continue;
				else if( $providerAccount['Type'] == 'SMS' || $providerAccount['Type'] == 'Voicemail' )
				{
			
					if( empty($contact['UserHomePhone']) && empty($contact['UserHomeMobile']) && empty($contact['UserWorkPhone']) && empty($contact['UserWorkMobile']) )
						continue;
				
				}
			}

			$results = $this->getStreamAction();
			$stream = array_merge($stream, $results);
			
		}

		//fill in blanks if row id set and blanks found
		foreach( $stream as &$row )
		{
			if( array_key_exists('from',$row) && ($row['from']['id'] == null || $row['from']['name'] == null) && array_key_exists('phoneNumber', $row['from']) && !empty($row['from']['phoneNumber']) )
			{
				if( $origionalRequestedAbookId == null )
				{
					$contact = null;
					$contactIds = $contacts->searchFuzzyContact( array('UserHomePhone' => $row['from']['phoneNumber']), null );

					if( count($contactIds) == 0 )
						continue;
					$contact = $contacts->getContact($contactIds[0]['id']);
					
				}

				if( $contact != null )
				{

					$row['from']['id'] = $contact['id'];
					$row['from']['name'] = $contact['UserFirstName'] . (!empty($contact['UserLastName'])?" " . $contact['UserLastName']:"");
					if( $row['avatar'] == null)
			 			$row['avatar'] = $this->siteBaseUrl . 'index.php/mail/contacts/viewphoto/id/' . $contact['id'] . '/resize/1/width/37/height/37/nocrop/1';

				}

			}	
			
		}

		usort($stream, array($this,"sortByreceivedDateISO8601"));
		
		if( !empty($this->requestParams['limit']) )
		{
			
			$chunks = array_chunk($stream, $this->requestParams['limit']);
			$stream = $chunks[0];
			
		}
		if( array_key_exists('pretty', $this->requestParams) )
			echo Zend_Json::prettyPrint(Zend_Json::encode($stream), array("indent" => "    "));
		else
			echo Zend_Json::encode($stream);

	}
		
	
	
	private function sortByreceivedDateISO8601($a,$b)
	{
		
		if( $a['receivedDateISO8601'] == $b['receivedDateISO8601']) 
			return 0;
		return ( strtotime($a['receivedDateISO8601']) > strtotime($b['receivedDateISO8601']) ) ? -1 : 1;
		
	}
	
	public function __call( $name, $arguments )
	{
		
		
		$args = $this->requestParams;
		unset($args['module']);
		unset($args['controller']);
		unset($args['action']);
		if( $this->type !== null )
			$args['type'] = $this->type;
		if( $this->pid !== null )
			$args['pid'] = $this->pid;
		
		//TODO: make sure that this lib enforces not allowing users to access pid they dont own
		//if arguments specifies service type and provider then instantiate object and pass the requested action over to class if if impliments the requested method, else fail
		$methodName = substr($name,0,-6); //remove Action off the end
		$account = $this->carrier->getAccount($args['pid']);

		//if id supplied resolve Abook.id to provider.id value - else resolve result ids to Abook.id
		$contacts = new contacts( array('Account' => $this->Account) );
		$contact = null;
		//resolve AbookId to Provider Id if in Abook
		if( !empty($args['AbookId'])  )
		{
			
			$contact = $contacts->getContact($args['AbookId']);

			if( $contact == null && empty($args['id']) )
			{
				echo Zend_Json::encode( 'Contact id does not exist.' );
				return;
			}
			if( $account['Provider'] == "Twitter" )
				$args['id'] = $contact['TwitterId'];
			else if( $account['Provider'] == 'Facebook' )
				$args['id'] = $contact['FacebookId'];
			else if( $account['Type'] == 'SMS' || $account['Type'] == 'Voicemail' )
			{
			
				$froms = array();
				if( !empty($contact['UserHomePhone']) )
					$froms[] = $contact['UserHomePhone'];
				if( !empty($contact['UserHomeMobile']) )
					$froms[] = $contact['UserHomeMobile'];
				if( !empty($contact['UserWorkPhone']) )
					$froms[] = $contact['UserWorkPhone'];
				if( !empty($contact['UserWorkMobile']) )
					$froms[] = $contact['UserWorkMobile'];
				$args['from'] = $froms;
				
			}
			
		}
		
		$cache = Zend_Cache::factory('Output', 'File', array('lifetime' => 600, 'automatic_serialization' => true), array('cache_dir' => APP_ROOT . 'tmp/'));
		$hashKey = md5( $name . serialize($args));
		$forceFreshMethods = array('like');
		$forceFreshFolder = array('like');
		$fresh = false;
		if( true || array_key_exists('message', $args)  || (!empty($args['folder']) && in_array($args['folder'], $forceFreshFolder)) || in_array($methodName, $forceFreshMethods) ) //then dont cache
		{

			$results = $this->providers->$methodName($args);
			$fresh = true;
		
		}
		else if( !$results = $cache->load($hashKey) )
		{
            //allowed cache load failed

			$results = $this->providers->$methodName($args);
			$cache->save($results, $hashKey);
			$fresh = true;
		
		}
		//N.B. at this point, id is provider id (e.g. screen_name) NOT Abook.id
		//Take results and try update contacts on TwitterId, or FacebookId
		//for now only one pid
		if( ($account['Type'] == "Voicemail" || $account['Type'] == "SMS") && array_key_exists('id', $results) )
		{
			
			if( array_key_exists('id', $results) )
			{
				
				$results['id'] = $contact['id'];
				
			}
		}
		else if( $account['Provider'] == "Facebook" )
		{
			
			if( is_array($results) && array_key_exists('serviceData', $results) )
			{
				
				foreach( $results['serviceData'] as &$row )
				{
					$contactId = $contacts->searchContact( array('FacebookId' => $row['from']['id']), null );
					//continue to next row if no contact matched
					if( $contactId == null || $contactId == 0 )
						continue;
					$row['AbookId'] = $contactId;
					$contact = $contacts->getContact($contactId);

					//update contact record with last status/message
					if( !empty($row['created_time']) && strtotime($contact['LastMessageDatetime']) < strtotime($row['created_time']) )
					{

						$message = (!empty($row['message'])?$row['message']:(!empty($row['caption'])&&!empty($row['description'])?$row['caption'] . " " . $row['description']:(!empty($row['name'])?$row['name']:'')));
						$newMessageData = array('id' => $contactId, 'LastMessage' => $message, 'LastMessageType' => 'Facebook', 'LastMessageDatetime' => Unix_to_MySQL_DATETIME_Locale(strtotime($row['created_time'])) );
						$result = $contacts->updateContact($newMessageData);

					}
					else if( !empty($row['LastMessageDatetime']) && strtotime($contact['LastMessageDatetime']) < strtotime($row['LastMessageDatetime']) )
					{

						$newMessageData = array( 'id' => $contactId, 'LastMessage' => $row['LastMessage'], 'LastMessageType' => $row['LastMessageType'], 'LastMessageDatetime' => $row['LastMessageDatetime'] );
						$result = $contacts->updateContact($newMessageData);

					}
					
				}
				
			}
			//CONSIDER updating last message in Abook from single record queries through unifyController
			
		}
		else if( $account['Provider'] == "Twitter" )
		{
			
			if( array_key_exists('serviceData', $results) )
			{
				
				foreach( $results['serviceData'] as &$row )
				{
					$contactId = $contacts->searchContact( array('TwitterId' => $row['user']['screen_name']), null );
					if( $contactId == null || $contactId == 0 )
						continue;
					$row['AbookId'] = $contactId;
					$contact = $contacts->getContact($contactId);

					if( !empty($row['created_at']) && strtotime($contact['LastMessageDatetime']) < strtotime($row['created_at']) )
					{
						
						$newMessageData = array('id' => $contactId, 'LastMessage' => $row['text'], 'LastMessageType' => 'Twitter', 'LastMessageDatetime' => Unix_to_MySQL_DATETIME_Locale(strtotime($row['created_at'])) );
						$result = $contacts->updateContact($newMessageData);
						
					}
					else if( !empty($row['LastMessageDatetime']) && strtotime($contact['LastMessageDatetime']) < strtotime($row['LastMessageDatetime'] ) )
					{
					
						$newMessageData = array('id' => $contactId, 'LastMessage' => $row['LastMessage'], 'LastMessageType' => $row['LastMessageType'], 'LastMessageDatetime' => $row['LastMessageDatetime'] );
						$result = $contacts->updateContact($newMessageData);
					
					}
				}
			}
			//CONSIDER updating last message in Abook from single record queries through unifyController
		}
		//if related to a contact, then resolve back to Abook.id
		if( !empty($results['id']) && $contact != null )
		{
			$results['id'] = $contact['id'];
		}
		
		//allow returning results instead of echoing out so that internal apps can use UC
		if( array_key_exists('raw', $args) && $args['raw'] === true )
			return $results;
		else if( array_key_exists('pretty', $args) )
			echo Zend_Json::prettyPrint(Zend_Json::encode($results), array("indent" => "    "));
		else
			echo Zend_Json::encode($results);

	}
	
	private function ucdbLogin()
	{
		//SIP password
		if( $this->_firstSIPAccount == null )
			throw new Exception("No valid UCDB user credentials found.");

		$sipPassword = $this->userData['password'];
		$params = array("username" => $this->Account, "password" => $sipPassword, "vendor_token" => "ATMAIL");
		$restResult = $this->_client->restPost("login", $params);
		$resultBodyString = $restResult->getBody();
		$this->_clientResultObject = json_decode($resultBodyString);

		if( isset($this->_clientResultObject->error) && $this->_clientResultObject->error == 0 && isset($this->_clientResultObject->data) )
		{
			$this->session->UCDBSID = $this->_clientResultObject->data;
			return true;
		
		}
		else
		{	
			return false;

		}
		
	}

	public function ucdbcheckloginAction()
	{
		if(!$this->ucdbEnabled) {
			echo "{ error: 'ucdb not enabled' }";
			return;
		}
		
		$result = $this->ucdbLogin()? 'success' : 'fail';
		echo json_encode( array("result"=>$result) );
	}

	public function ucdbdeleteAction()
	{
		$args = $this->requestParams;

		//set up remote request.
		//if not remote SID stored in User Session, then initiate remote login and acquire remote SID
		if( empty($this->session->UCDBSID) || !isset($this->session->UCDBSID) || $this->session->UCDBSID == null || $this->session->UCDBSID == "" )
		{
			if( !$this->ucdbLogin() )
			{
				echo $this->_clientResultObject;
				return;
			}
			
		}
		
		if( empty($args['folder']) )
			return;
		
		
		$putData = array(
			$this->UCDBSIDFieldName => $this->session->UCDBSID,
			"device_id" => "WEBPHONE1", 
		);
		

		$logId = array_key_exists('log_id', $args) ? $args['log_id'] : (array_key_exists('id', $args) ? $args['id'] : '');
		
		
		if( empty($logId) )
		{
			echo json_encode( array("status"=>"failed","message" => "id field missing") );
			return;
		}
		
		$putData['log_id'] = $logId;
		
		// delete 
		$restResult = $this->_client->restPost("log/delete/" . $args['folder'] , $putData);
		
		$resultBodyString = $restResult->getBody();
		
		$resultObject = json_decode($resultBodyString);
		
		if( isset($resultObject->error) && ($resultObject->error & UcdbEnum::ERROR) == UcdbEnum::ERROR ) // if there was an error e.g. ERROR_PERMISSION_DENIED
		{
			//authentication failed, suspect timeout, so try login again
			if( !$this->ucdbLogin() )
			{
				echo json_encode($this->_clientResultObject);
				return;
			}
			$putData[$this->UCDBSIDFieldName] = $this->session->UCDBSID;
			
			//login appeared to succeed so try to run query again (once more)
			$restResult = $this->_client->restPost("log/delete/" . $args['folder'], $putData);
			$resultBodyString = $restResult->getBody();
			$resultObject = json_decode($resultBodyString);
		}
		
		echo $resultBodyString;
		
	}
		
	public function ucdbputAction()
	{
		if(!$this->ucdbEnabled) {
			echo "{ error: 'ucdb not enabled' }";
			return;
		}

		$args = $this->requestParams;
		//set up remote request.
		//if not remote SID stored in User Session, then initiate remote login and acquire remote SID
		if( empty($this->session->UCDBSID) || !isset($this->session->UCDBSID) || $this->session->UCDBSID == null || $this->session->UCDBSID == "" )
		{
			if( !$this->ucdbLogin() )
			{
				echo $this->_clientResultObject;
				return;
			}
			
		}

		//UCDB status: INITIATED, SENT, DELIVERED, READ, FAILED, DELETED
		//SIP state: ACCEPTED, SENT, DELIVERED, READ, FAILED
		//{"log_id" : messageObject.id, "message" : messageObject.body, src : messageObject.from, dst : messageObject.recipients, state : messageObject.state }
		
		$logId = array_key_exists('log_id', $args) ? $args['log_id'] : (array_key_exists('id', $args) ? $args['id'] : (array_key_exists('callId', $args) ? $args['callId'] : ''));
		
		$putData = array(
			$this->UCDBSIDFieldName => $this->session->UCDBSID,
			"device_id" => "WEBPHONE1", 
			"log_id" => $logId, 
		);
		
		//name matching from Abook
		$contacts = new contacts( array('Account' => $this->Account) );
		if( $args['state'] == "SENT" )
		{
			
			if( empty($args['source_name']) || $args['source_name'] == "Unknown" )
			{
				$src = substr( $args['src'], -9 );	// just compare last 9 digits
				
				$contactIds = $contacts->searchFuzzyContact( array('UserHomePhone' => $src, 'UserHomeMobile' => $src,'UserWorkPhone' => $src, 'UserWorkMobile' => $src), null );
				if( count($contactIds) != 0 )
				{
					$contact = $contacts->getContact($contactIds[0]['id']);
					$fullName = $contact["UserFirstName"] . (mb_strlen($contact["UserFirstName"])>0 && mb_strlen($contact["UserLastName"])>0?" ":"") . $contact["UserLastName"];
					$args['source_name'] = $fullName;
				}
				else 
				{
					$args['source_name'] = "Unknown";
				}
			}
			if( empty($args['target_name']) || $args['target_name'] == "Unknown" )
			{
				$dst = substr( $args['dst'], -9 );	// just compare last 9 digits
				
				$contactIds = $contacts->searchFuzzyContact( array('UserHomePhone' => $dst, 'UserHomeMobile' => $dst,'UserWorkPhone' => $dst, 'UserWorkMobile' => $dst), null );
				if( count($contactIds) != 0 )
				{
					$contact = $contacts->getContact($contactIds[0]['id']);
					$fullName = $contact["UserFirstName"] . (mb_strlen($contact["UserFirstName"])>0 && mb_strlen($contact["UserLastName"])>0?" ":"") . $contact["UserLastName"];
					$args['target_name'] = $fullName;
				}
				else 
				{
					$args['target_name'] = "Unknown";
				}
			}
			
			
		}

		$convert_dict = array(
			'source'		=> 'src',
			'source_name'	=> 'source_name',
			'target'		=> 'dst',
			'target_name'	=> 'target_name',
			'message'		=> 'message',
			'duration'		=> 'duration',
			'answer'		=> 'answer',
			'start'			=> 'start',
			'end'			=> 'end',
			'read'			=> 'readUnixGMT',
			'status'		=> 'state',
			'extra'			=> 'extra',
		);

		foreach ($convert_dict as $key => $value) {
			if( isset($args[$value]) )
			$putData[$key] = $args[$value];
		}
		
		$now = time();

		foreach ($putData as $key => $value) {
			if(in_array($key, array('answer', 'start', 'end', 'read'))) {
				$putData[$key] = $now;	// replace time with server time
			}
		}
			
		if( $args['state'] == "SENT" && isset($args['init']) )
		{
			$putData['transaction_stamp'] = $now; //$args['sent'];  here replace with server time
			$putData['status'] = "INITIATED";
		}
		else if( $args['state'] == "ACCEPTED" )
		{
			if( empty($args['sent']) && !empty($args['init']) )
				$putData['sent'] = $args['init'];
			$putData['status'] = "SENT";
		}
		else if( $args['state'] == "DELIVERED" && isset($args['delivered']) )
			$putData['end'] = $args['delivered'];
		else if( $args['state'] == "READ" && isset($args['read']) )
			$putData['read'] = $args['read'];
		else if( $args['state'] == "FAILED" && isset($args['failed']) )
			$putData['end'] = $args['end'];
		else if( $args['state'] == "NOANSWER" && isset($args['end']) )
			$putData['end'] = $args['end'];
		else if( $args['state'] == "MISSED" && isset($args['end']) )
			$putData['end'] = $args['end'];

		if(!isset($args['itemtype']))
		{
			$args['itemtype'] = 'sms';
		}


		if(!$putData['source']) unset($putData['source']);
		if(!$putData['target']) unset($putData['target']);


		$restResult = $this->_client->restPost("log/put/" . $args['itemtype'] , $putData);

		$resultBodyString = $restResult->getBody();
		$resultObject = json_decode($resultBodyString);
		
		if( isset($resultObject->error) && ($resultObject->error & UcdbEnum::ERROR) == UcdbEnum::ERROR ) // if there was an error e.g. ERROR_PERMISSION_DENIED
		{
			Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Access denied. Did session timeout? Trying to login again to get valid session.");
			//authentication failed, suspect timeout, so try login again
			if( !$this->ucdbLogin() )
			{
				Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Re-login attempt failed: " . print_r($this->_clientResultObject,true));
				echo json_encode($this->_clientResultObject);
				return;
			}
			$putData[$this->UCDBSIDFieldName] = $this->session->UCDBSID;
			
			//login appeared to succeed so try to run query again (once more)
			$restResult = $this->_client->restPost("log/put/" . $args['itemtype'] , $putData);
			$resultBodyString = $restResult->getBody();

			$resultObject = json_decode($resultBodyString);
		}
		
		echo $resultBodyString;

	}
	
	public function ucdbgetAction()
	{
		
		if(!$this->ucdbEnabled) {
			echo "{ error: 'ucdb not enabled' }";
			return;
		}

		$args = $this->requestParams;
		//set up remote request.
		//if not remote SID stored in User Session, then initiate remote login and acquire remote SID
		if( empty($this->session->UCDBSID) || !isset($this->session->UCDBSID) || $this->session->UCDBSID == null || $this->session->UCDBSID == "" )
		{
			if( !$this->ucdbLogin() )
			{
				echo json_encode($this->_clientResultObject);
				return;
			}
			
		}
		if( !empty($args['folder']) && $args['folder'] == "CDR" )
			$folder = "call";
		else
			$folder = "sms";
		$params = array( $this->UCDBSIDFieldName => $this->session->UCDBSID );
		$restResult = $this->_client->restPost("log/get/" . $folder, $params);

		$resultBodyString = $restResult->getBody();
		$resultObject = json_decode($resultBodyString);
		

		//echo $resultBodyString;
		//echo print_r($resultObject,true);
		if( isset($resultObject->error) && ($resultObject->error & UcdbEnum::ERROR) == UcdbEnum::ERROR ) // if there was an error e.g. ERROR_PERMISSION_DENIED
		{
			
			Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Access denied. Did session timeout? Trying to login again to get valid session.");
			//authentication failed, suspect timeout, so try login again
			if( !$this->ucdbLogin() )
			{
				Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Re-login attempt failed: " . print_r($this->_clientResultObject,true));
				echo json_encode($this->_clientResultObject);
				return;
			}
			
			//login appeared to succeed so try to run query again (once more)
			$params = array( $this->UCDBSIDFieldName => $this->session->UCDBSID );
			$restResult = $this->_client->restPost("log/get/" . $folder, $params);
			$resultBodyString = $restResult->getBody();
			$resultObject = json_decode($resultBodyString);
			//echo $resultBodyString;
			//echo print_r($resultObject,true);
			
		}
		else if( !isset($resultObject->error) || $resultObject->error != 0 ) //other errors
		{
			echo $resultBodyString;
			return;
		}
		$responseArray = array("status" => "success", "page" => 1, "serviceData" => array());

		$resultContacts = $this->dbAdapter->select()
			->from("Abook")
			->where("Account = ?" , $this->Account )
			->query()
			->fetchAll();

		foreach( $resultObject->data as $k => $row )
		{
			
			if( $row->status == "DELETED" )
				continue;
			
			//INITIATED, SENT, DELIVERED, READ, FAILED, DELETED
			$init = $row->transaction_stamp;
			$delivered = null;
			$read = $row->read;
			$failed = null;
			
			if( $row->status == "INITIATED" )
			{
				
			}
			else if( $row->status == "SENT" )
			{
				
				$sent = $row->sent;
				
			}
			else if( $row->status == "DELIVERED" )
			{
				
				$sent = $row->sent;
				$delivered = $row->end;

			}
			else if( $row->status == "READ" )
			{
				
				$sent = $row->sent;
				$delivered = $row->end;
				$read = $row->read;
				
			}
			else if( $row->status == "FAILED" )
			{
				
				$sent = $row->sent;
				$failed = $row->end;
				
			}
			else
			{

				$sent = $row->sent;
				$read = $row->read;
				$failed = $row->end;

			}
			//to calculate isLAtest, run through existing rows and see if already have a message to/from same person/src/dst
			$newRow = array(
				"id" => $row->id,
				"log_id" => $row->log_id,
				"src" => $row->source,
				"dst" => $row->target,
				"initUnixGMT" => $init, 
				"initLocale" => date('r',$init),
				"sentUnixGMT" => $sent, 
				"sentLocale" => date('r',$sent),
				"receivedUnixGMT" => $delivered, 
				"receivedLocale" => date('r', $delivered), 
				"readUnixGMT" => $read, 
				"readLocale" => date('r', $read), 
				"isLatest" => "1",
				"status" => $row->status,
				"message" => $row->message,
				"durationSecs" => $row->duration,
				"extra" => $row->extra
			);
			
			// update device  source_device target_device

			$newRow['source_device'] = $this->get_device_name( $newRow['src'],$resultContacts);
			$newRow['source_name'] = $this->get_display_name( $newRow['src'],$resultContacts, $row->source_name);
			$newRow['target_device'] = $this->get_device_name( $newRow['dst'],$resultContacts);
			$newRow['target_name'] = $this->get_display_name( $newRow['dst'],$resultContacts, $row->target_name);

			if( $row->status == 'FAILED' )
			{
				
				$newRow["failedUnixGMT"] = $failed;
				$newRow["failedLocale"] = date('r', $failed);
				
			}
			$responseArray['serviceData'][] = $newRow;
		}
		$responseArray["totalNumberOfItems"] = count($responseArray['serviceData']);
		$responseArray["itemsPerPage"] = count($responseArray['serviceData']);

		//swap log_id for id (templates depend on key field being called id)
		foreach( $responseArray['serviceData'] as $k => $row )
		{
			
			$responseArray['serviceData'][$k]['id'] = $responseArray['serviceData'][$k]['log_id'];
			
		}
		
		if( array_key_exists('pretty', $this->requestParams) )
			echo Zend_Json::prettyPrint(Zend_Json::encode($responseArray), array("indent" => "    "));
		else
			echo Zend_Json::encode($responseArray);
		
	}

	private function make_display_name($contact, $defaultName)
	{

		$name = $contact['UserFirstName'];
		if( !empty($contact['UserLastName']) ) 
			$name = $name . ' ' . $contact['UserLastName'];

		return !empty($name)? $name : $defaultName;

	}

	/**
	 *
	 * @param type $val
	 * @param type $contacts
	 * @param type $contactCatch
	 * @return array
	 */
	private function cache_search_result($val, $contacts)
	{

		$unknownresult = array( 'deviceName' => 'Unknown', 'displayName' => 'Unknown' );

		if( empty($val) ) 
			return $unknownresult;

		$tmp = explode(';', $val);
		$val = $tmp[0];
		
		$tmp = explode('@', $val);
		$val = $tmp[0];
		unset($tmp);
		
		if( $val[3] == ':' )
			$val = substr($val, 4);

		if( isset( $this->_contactCache[$val] ) )
			return $this->_contactCache[$val];

		foreach ($contacts as $key => $contact)
		{

			if( empty($contact) )
				continue;
				
			foreach( array('UserHomePhone', 'UserHomeMobile', 'UserWorkPhone', 'UserWorkMobile', 'SIP', 'UserWorkFax', 'UserHomeFax') as $field )
			{

				if( empty($contact[$field]) )
					continue;
				
				// full match for yesID
				if( $field == 'SIP')
				{
					
					$tmp1 = explode('@',$contact[$field],2);
					if( $val == $tmp1[0] )
					{
					
						$this->_contactCache[$val] = array( 'deviceName' => $field, 'displayName' => $this->make_display_name($contact, $val) );
						return $this->_contactCache[$val];
					
					}
					
				}
				else if( substr($contact[$field],-9) == substr($val,-9) ) 
				{

					$this->_contactCache[$val] = array( 'deviceName' => $field, 'displayName' => $this->make_display_name($contact, $val) );
					return $this->_contactCache[$val];

				}

			}

		}

		$this->_contactCache[$val] = array( 'deviceName' => 'Unknown', 'displayName' => $val );
		return $this->_contactCache[$val];
		
	}
	
	private function get_device_name($val, $contacts)
	{

		$resultString = '';
		$recipients = explode(',', $val);

		foreach( $recipients as $recipient )
		{
			$result = $this->cache_search_result($recipient, $contacts);
			$resultString .= (($result['deviceName']=='Unknown' && !empty($ucdbvalue) ) ? $ucdbvalue : $result['deviceName']) . ", ";
		}
		return substr($resultString, 0, -2);

	}

	/**
	 * Try to get from Abook first, if fail then use value stored in ucdb
	 * 
	 * @param type $val
	 * @param type $contacts
	 * @param type $ucdbvalue
	 * @return type
	 */
	private function get_display_name($val, $contacts, $ucdbvalue)
	{
		$resultString = '';
		$recipients = explode(',', $val);

		foreach( $recipients as $recipient )
		{
			$result = $this->cache_search_result($recipient, $contacts);
			$resultString .= (($result['displayName']=='Unknown' && !empty($ucdbvalue) ) ? $ucdbvalue : $result['displayName']) . ", ";
		}
		return substr($resultString, 0, -2);
	}

	public function ucdbpingAction()
	{
		
		$restResult = $this->_client->restPost("ping");
		$resultBody = $restResult->getBody();
		echo $resultBody;
		
	}
	
}
