<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
 
class PortaneoService implements ActivityService, PersonService, AppDataService, MessagesService  {

    private $portaneoConfig;
    
   /**
       * Returns a Person object for person with $id or false on not found
       *
       * @param container specific id $id
       * @param fields set of contact fields to return, as array('fieldName' => 1)
       * @param security token $token
       */
      function getPerson($userId, $groupId, $fields, SecurityToken $token) {
        if (! is_object($userId)) {
          $userId = new UserId('userId', $userId);
          $groupId = new GroupId('self', 'all');
        }
        $person = $this->getPeople($userId, $groupId, new CollectionOptions(), $fields, $token);
        if (is_array($person->getEntry())) {
          $person = $person->getEntry();
          if (is_array($person) && count($person) == 1) {   
            return array_pop($person);
          }
        }
        throw new SocialSpiException("Person not found", ResponseError::$BAD_REQUEST);           
      }

      /**
       * Returns a list of people that correspond to the passed in person ids.
       * @param ids The ids of the people to fetch.
       * @param options Request options for filtering/sorting/paging
       * @param fields set of contact fields to return, as array('fieldName' => 1)
       * @return a list of people.
       */
      function getPeople($userId, $groupId, CollectionOptions $options, $fields, SecurityToken $token) {
        $ids = $this->getIdSet($userId, $groupId, $token);
        $allPeople = PortaneoDbFetcher::get()->getPeople($ids, $fields, $options, $token);
        $totalSize = $allPeople['totalSize'];
        $people = array();
        
        foreach ($ids as $id) {
          $person = null;
          if (is_array($allPeople) && isset($allPeople[$id])) {
            $person = $allPeople[$id];
            if (! $token->isAnonymous() && $id == $token->getViewerId()) {
              $person->setIsViewer(true);
            }
            if (! $token->isAnonymous() && $id == $token->getOwnerId()) {
              $person->setIsOwner(true);
            }
            if (! in_array('@all', $fields)) {
              $newPerson = array();
              $newPerson['isOwner'] = $person->isOwner;
              $newPerson['isViewer'] = $person->isViewer;
              $newPerson['displayName'] = $person->displayName;
              // Force these fields to always be present
              $fields[] = 'id';
              $fields[] = 'displayName';
              //$fields[] = 'thumbnailUrl';
              //$fields[] = 'profileUrl';
              foreach ($fields as $field) {
                if (isset($person->$field) && ! isset($newPerson[$field])) {
                  $newPerson[$field] = $person->$field;
                }
              }
              $person = $newPerson;
            }
            array_push($people, $person);
          }
        }
        $sorted = $this->sortPersonResults($people, $options);
        $sorted = false;
        $collection = new RestfulCollection($people, $options->getStartIndex(), $totalSize);
        $collection->setItemsPerPage($options->getCount());
        if (! $sorted) {
          $collection->setSorted(false); // record that we couldn't sort as requested
        }
        if ($options->getUpdatedSince()) {
          $collection->setUpdatedSince(false); // we can never process an updatedSince request
        }
        return $collection;
      }
      
      
      function sortPersonResults(&$people, $options) {
        if (! $options->getSortBy()) {
          return true; // trivially sorted
        }
        if ($options->getSortBy() != 'displayName') {
          return false;
        }
        usort($people, array($this, 'comparator'));
        if ($options->getSortOrder() != CollectionOptions::SORT_ORDER_ASCENDING) {
          $people = array_reverse($people);
        }
        return true;
      }      
      
      function getIdSet($user, GroupId $group, SecurityToken $token) {
    $ids = array();
    if ($user instanceof UserId) {
      $userId = $user->getUserId($token);
      if ($group == null) {
        return array($userId);
      }
      switch ($group->getType()) {
        case 'all':
        case 'friends':
        case 'groupId':
          $friendIds = PortaneoDbFetcher::get()->getFriendIds($userId);
          if (is_array($friendIds) && count($friendIds)) {
            $ids = $friendIds;
          }
          break;
        case 'self':
          $ids[] = $userId;
          break;
      }
    } elseif (is_array($user)) {
      $ids = array();
      foreach ($user as $id) {
        $ids = array_merge($ids, $this->getIdSet($id, $group, $token));
      }
    }
    return $ids;
  }         
      
      
      
      /**  
       * Send a message to the chosen recipients.
       * $userId - the sender
       * $appId - the application Id
       * $message - an array containing the following fields:
       *  [id] => {msgid}
       *  [title] => You have an invitation from Joe
       *  [body] => Click <a href="http://app.example.org/invites/{msgid}">here</a> to review your invitation.
       *  [recipients] => Array
       *      (
       *          [0] => UserId1
       *          [1] => UserId2
       *      )
       * $optionalMessageId - if the REST action was a PUT containing an message id this is filled in
       * $token - the security token
       */
    public function createMessage($userId, $appId, $message, $optionalMessageId, SecurityToken $token) {
        $from = $userId->getUserId($token);
        if ($token->getOwnerId() != $token->getViewerId() || $token->getViewerId() != $from) {
          throw new SocialSpiException("Create message permission denied.", ResponseError::$UNAUTHORIZED);
        }
        if (in_array($from, $message['recipients'])) {
          throw new SocialSpiException("Can't send message to myself.", ResponseError::$BAD_REQUEST);
        }
        try {
          PartuzaDbFetcher::get()->createMessage($from, $token->getAppId(), $msgCollId, $message);
        } catch (SocialSpiException $e) {
          throw $e;
        } catch (Exception $e) {
          throw new SocialSpiException("Invalid create message request: " . $e->getMessage(), ResponseError::$INTERNAL_ERROR);
        }
    }  
      


      
      
      
    /*
                     Returns a list of activities that correspond to the passed in person ids.
          */
    public function getActivities($userIds, $groupId, $appId, $sortBy, $filterBy, $filterOp, $filterValue, $startIndex, $count, $fields, $activityIds, $token) {
    $ids = $this->getIdSet($userIds, $groupId, $token);
    $activities = PortaneoDbFetcher::get()->getActivities($ids, $appId, $sortBy, $filterBy, $filterOp, $filterValue, $startIndex, $count, $fields, $activityIds);
    if ($activities) {
      return $this->getRestfulCollection($activities);
    } else {
      throw new SocialSpiException("Invalid activity specified", ResponseError::$NOT_FOUND);
    }
    }      
      

    public function getActivity($userId, $groupId, $appdId, $fields, $activityId, SecurityToken $token) {
        $activities = $this->getActivities($userId, $groupId, $appdId, null, null, null, null, 0, 20, $fields, array($activityId), $token);
        if ($activities instanceof RestFulCollection) {
          $activities = $activities->getEntry();
          foreach ($activities as $activity) {
            if ($activity->getId() == $activityId) {
              return $activity;
            }
          }
        }
        throw new SocialSpiException("Activity not found", ResponseError::$NOT_FOUND);
    }


    public function deleteActivities($userId, $groupId, $appId, $activityIds, SecurityToken $token) {
    }


    /**
            * Creates the passed in activity for the given user. Once createActivity is
            * called, getActivities will be able to return the Activity.
            */
    public function createActivity($userId, $groupId, $appId, $fields, $activity, SecurityToken $token) {        
        try {
          if ($token->getOwnerId() != $token->getViewerId() || $token->getViewerId() != $userId->getUserId($token)) {
            throw new SocialSpiException("Create activity permission denied.", ResponseError::$UNAUTHORIZED);
          }
          PortaneoDbFetcher::get()->createActivity($userId->getUserId($token), $activity, $token->getAppId());
        } catch (SocialSpiException $e) {
          throw $e;
        } catch (Exception $e) {
          throw new SocialSpiException("Invalid create activity request: " . $e->getMessage(), ResponseError::$INTERNAL_ERROR);
        }
    }


    private function getRestfulCollection($results) {
        $totalResults = $results['totalResults'];
        $startIndex = $results['startIndex'];
        $count = $results['count'];
        unset($results['totalResults']);
        unset($results['startIndex']);
        unset($results['count']);
        $ret = new RestfulCollection($results, $startIndex, $totalResults);
        $ret->setItemsPerPage($count);
        return $ret;
    }


  
      
      
      
    /**
    * Fetch data for a list of ids.
    * @param UserId The user id to perform the action for
    * @param GroupId optional grouping ID
    * @param fields The list of fields to fetch
    * @param token The SecurityToken for this request
    * @return ResponseItem a response item with the error code set if
    *     there was a problem
    */
    function getPersonData($userId, GroupId $groupId, $appId, $fields, SecurityToken $token) {
    }

    function deletePersonData($userId, GroupId $groupId, $appId, $fields, SecurityToken $token) {
    }

    /**
    * Updates the data key for the given person with the new value.
    *
    * @param id The person the data is for.
    * @param key The key of the data.
    * @param value The new value of the data.
    * @param token The SecurityToken for this request
    * @return ResponseItem a response item with the error code set if
    *     there was a problem
    */
  public function updatePersonData(UserId $userId, GroupId $groupId, $appId, $fields, $values, SecurityToken $token) {
    if ($userId->getUserId($token) == null) {
      throw new SocialSpiException("Unknown person id.", ResponseError::$NOT_FOUND);
    }
    foreach ($fields as $key) {
      if (! self::isValidKey($key)) {
        throw new SocialSpiException("The person app data key had invalid characters", ResponseError::$BAD_REQUEST);
      }
    }
    switch ($groupId->getType()) {
      case 'self':
        foreach ($fields as $key) {
          $value = isset($values[$key]) ? $values[$key] : null;
          if (! PortaneoDbFetcher::get()->setAppData($userId->getUserId($token), $key, $value, $appId)) {
            throw new SocialSpiException("Internal server error", ResponseError::$INTERNAL_ERROR);
          }
        }
        break;
      default:
        throw new SocialSpiException("We don't support updating data in batches yet", ResponseError::$NOT_IMPLEMENTED);
        break;
    }
  }
      
  /**
   * Determines whether the input is a valid key. Valid keys match the regular
   * expression [\w\-\.]+.
   *
   * @param key the key to validate.
   * @return true if the key is a valid appdata key, false otherwise.
   */
  public static function isValidKey($key) {
    if (empty($key)) {
      return false;
    }
    for ($i = 0; $i < strlen($key); ++ $i) {
      $c = substr($key, $i, 1);
      if (($c >= 'a' && $c <= 'z') || ($c >= 'A' && $c <= 'Z') || ($c >= '0' && $c <= '9') || ($c == '-') || ($c == '_') || ($c == '.')) {
        continue;
      }
      return false;
    }
    return true;
  }      
   


      
}

