<?php
/**
 * @file
 * Provides a services resource which is used by the bassets server.
 */

define('BASSETS_SERVER_RESOURCE', 'bassetsserver');

/**
 * Implements hook_services_resources().
 */
function bassets_client_services_resources() {
  $resources['bassetsclient'] = array(
    '#api_version' => 3002,
    'actions' => array(
      'ping' => array(
        'help' => 'Lets the a Bassets client ping the server. Used by Bassets setup',
        'file' => array(
          'type' => 'inc',
          'module' => 'bassets_client',
          'name' => 'resources/bassetsclient',
        ),
        'callback' => '_bassets_server_ping',
        'access arguments' => array('access content'),
      ),
      'pushuuid' => array(
        'help' => 'Lets the a Bassets server push a uuid for a remote file',
        'file' => array(
          'type' => 'inc',
          'module' => 'bassets_client',
          'name' => 'resources/bassetsclient',
        ),
        'callback' => '_bassets_file_pushuuid',
        'args' => array(
          array(
            'name' => 'uuid',
            'type' => 'string',
            'optional' => FALSE,
            'source' => array('data' => 'uuid'),
            'description' => 'A uuid which was pushed from the remote server.',
          ),
          array(
            'name' => 'uri',
            'type' => 'string',
            'optional' => FALSE,
            'source' => array('data' => 'uri'),
            'description' => 'The uri of the file.',
          ),
          array(
            'name' => 'services_client_id',
            'type' => 'string',
            'optional' => FALSE,
            'source' => array('data' => 'services_client_id'),
            'description' => 'The service_client_id of the remote site.',
          ),
        ),
        'access arguments' => array('access content'),
      ),
      'fileusage' => array(
        'help' => 'Lets the a Bassets server fetch the file usage.',
        'file' => array(
          'type' => 'inc',
          'module' => 'bassets_client',
          'name' => 'resources/bassetsclient',
        ),
        'callback' => '_bassets_file_fileusage',
        'args' => array(
          array(
            'name' => 'uuid',
            'type' => 'string',
            'optional' => FALSE,
            'source' => array('data' => 'uuid'),
          ),
        ),
        'access arguments' => array('access content'),
      ),
    ),
    'operations' => array(
      'delete' => array(
        'help' => 'Lets the a Bassets server delete a bassets file.',
        'file' => array(
          'type' => 'inc',
          'module' => 'bassets_client',
          'name' => 'resources/bassetsclient',
        ),
        'callback' => '_bassets_file_delete',
        'args' => array(
          array(
            'name' => 'uuid',
            'type' => 'string',
            'optional' => FALSE,
            'source' => array('path' => 0),
            'description' => 'The uuid of the bassets file.',
          ),
        ),
        'access arguments' => array('access content'),
      ),
    ),
  );
  return $resources;
}

/**
 * Implements hook_file_delete().
 */
function bassets_client_file_delete($file) {
  $scheme = file_uri_scheme($file->uri);

  // Is not our file scheme.
  if ($scheme !== BASSETS_SCHEME) {
    return;
  }
  $bassets_file = bassets_client_load_bassets_file($file->uri);
  if (!$bassets_file) {
    return;
  }
  $context = _bassets_client_static_context('delete', $file->uuid);
  // Do not call the service when the bassets server already deleted the file.
  if (empty($context)) {
    $client = services_client_connection_get($bassets_file->bassets_server);
    if (!$client) {
      return;
    }
    try{
      $client->action(BASSETS_SERVER_RESOURCE, 'deleted', array(
        'uuid' => $file->uuid,
        'services_client_id' => services_client_get_id(),
      ));
    }
    catch (Exception $e) {
      watchdog_exception('bassets_client', $e);
      return;
    }
  }
  bassets_client_delete_bassets_file($file->uri);
}

/**
 * Saves the uuid for a Bassets server.
 */
function bassets_client_save_bassets_file($uuid, $services_client_id) {
  $bassets_server = NULL;
  $connections = _bassets_scc_connections_load('server');
  foreach ($connections as $connection) {
    if (property_exists($connection, 'services_client_id') && $connection->services_client_id === $services_client_id) {
      $bassets_server = $connection->name;
      break;
    }
  }
  if (!empty($bassets_server)) {
    return db_merge('bassets_files')
      ->key(array('uuid' => $uuid))
      ->fields(array(
        'uuid' => $uuid,
        'bassets_server' => $bassets_server,
    ))
    ->execute();
  }
  return FALSE;
}

/**
 * Loads the entry for a file.
 */
function bassets_client_load_bassets_file($file_uri) {
  $uuid = pathinfo($file_uri, PATHINFO_FILENAME);
  $query = db_select('bassets_files', 't');
  $query->fields('t');
  $query->condition('uuid', $uuid);
  return $query->execute()->fetchObject();
}

/**
 * Loads the entries for all files of a server.
 */
function bassets_client_load_bassets_files($bassets_server) {
  $query = db_select('bassets_files', 't');
  $query->fields('t');
  $query->condition('bassets_server', $bassets_server);
  $results = $query->execute();
  $files = array();
  if (!empty($results)) {
    $uuids = array();
    foreach ($results as $result) {
      $uuids[] = $result->uuid;
    }
    $files = entity_uuid_load('file', $uuids);
  }
  return $files;
}

/**
 * Fetches the base64 encoded raw data of the file.
 */
function _bassets_client_fetch_file_content($file_uri) {
  $lock_acquired = lock_acquire($file_uri);
  $response_data = $file_content = NULL;
  if (!$lock_acquired) {
    // Tell client to retry again in 3 seconds. Currently no browsers are known
    // to support Retry-After.
    drupal_add_http_header('Status', '503 Service Unavailable');
    drupal_add_http_header('Retry-After', 3);
    print t('File fetching in progress. Try again shortly.');
    drupal_exit();
  }
  $bassets_file = bassets_client_load_bassets_file($file_uri);
  if (!$bassets_file) {
    return $response_data;
  }
  $client = services_client_connection_get($bassets_file->bassets_server);
  if (!$client) {
    return $response_data;
  }
  try{
    $client->action(BASSETS_SERVER_RESOURCE, 'fetch_raw', array('uuid' => $bassets_file->uuid));
    $response = $client->getResponse();
    if (!empty($response->data) && $response->data['status'] == WATCHDOG_NOTICE) {
      $response_data = $response->data['rawurl'];
    }
    else {
      watchdog('bassets_client', print_r($response->data['message'], TRUE), array(), $response->data['status']);
    }
  }
  catch (Exception $e) {
    watchdog_exception('bassets_client', $e);
  }
  if (!empty($lock_acquired)) {
    lock_release($file_uri);
  }
  if (!empty($response_data)) {
    $request = drupal_http_request($response_data, array('method' => 'HEAD'));
    if (!isset($request->error)) {
      return file_get_contents($response_data);
    }
  }
  return $file_content;
}

/**
 * Deletes the entry for a file.
 */
function bassets_client_delete_bassets_file($file_uri) {
  $uuid = pathinfo($file_uri, PATHINFO_FILENAME);
  if ($uuid) {
    $query = db_delete('bassets_files');
    $query->condition('uuid', $uuid);
    $query->execute();
  }
}

/**
 * Helper function to store data in memory.
 */
function _bassets_client_static_context($context, $data = NULL) {
  static $contexts;
  if (!isset($contexts[$context])) {
    // Initial.
    $contexts[$context] = $context;
    // Could be duplicate, therefore it sets data to make this unique.
    if (!empty($data)) {
      $contexts[$context] = $data;
    }
    return FALSE;
  }
  if (!empty($data)) {
    if ($contexts[$context] === $data) {
      return $contexts[$context];
    }
    else {
      return FALSE;
    }
  }
  return $contexts[$context];
}

/**
 * Replaces the uri scheme with our scheme.
 */
function _bassets_client_replace_uri_scheme($uri) {
  return BASSETS_SCHEME . '://' . file_uri_target($uri);
}
