<?php

/**
 * @file
 * Converts node translations into field-based translations.
 */

/**
 * The number of node translations that should be processed for each batch step.
 */
define('ENTITY_TRANSLATION_UPGRADE_BATCH_SIZE', 10);

/**
 * Starts the batch process to perform the upgrade.
 */
function entity_translation_upgrade_start($types) {
  $batch = array(
    'operations' => array(
      array('entity_translation_upgrade_do', array($types)),
      array('entity_translation_upgrade_complete', array()),
    ),
    'finished' => 'entity_translation_upgrade_end',
    'title' => t('Entity Translation Upgrade'),
    'init_message' => t('Entity Translation Upgrade is starting.'),
    'error_message' => t('Entity Translation Upgrade has encountered an error.'),
    'file' => drupal_get_path('module', 'entity_translation_upgrade') . '/entity_translation_upgrade.admin.inc',
  );
  batch_set($batch);
  batch_process('admin/config/regional/entity_translation');
}

/**
 * Finshed batch callback.
 */
function entity_translation_upgrade_end($success, $results, $operations, $elapsed) {
  if (!empty($results)) {
    $message = format_plural($results['total'], '1 node translation successfully upgraded.', '@count node translations successfully upgraded.');
    watchdog('entity translation upgrade', '@count node translations successfully upgraded.', array('@count' => $results['total']), WATCHDOG_INFO);
  }
  else {
    $message = t('No node translation available for the upgrade.');
  }

  drupal_set_message($message);
}

/**
 * Batch process to convert node translations to field-based translations.
 */
function entity_translation_upgrade_do($types, &$context) {
  $query = db_select('node', 'n');
  $query->addJoin('LEFT', 'entity_translation_upgrade_history', 'etuh', 'n.nid = etuh.nid');
  $query
    ->fields('n', array('nid', 'tnid'))
    ->where('etuh.complete IS NULL OR etuh.complete = 0')
    ->condition('n.tnid', 0, '<>')
    ->condition('n.tnid <> n.nid', array(), '')
    ->condition('n.type', $types)
    ->orderBy('n.nid');

  // Initialize the batch process.
  if (empty($context['sandbox'])) {
    $total = $query
      ->countQuery()
      ->execute()
      ->fetchField();

    $context['sandbox']['count'] = 0;
    $context['sandbox']['total'] = $total;
    $context['finished'] = $total == 0;
  }
  else {
    $batch_size = variable_get('entity_translation_upgrade_batch_size', ENTITY_TRANSLATION_UPGRADE_BATCH_SIZE);
    $result = $query
      ->range($context['sandbox']['count'], $batch_size)
      ->execute()
      ->fetchAllKeyed();

    // Here we load original nodes and translations all together, but the batch
    // size is determined only by node translations.
    $nids = array_keys($result);
    $nodes = node_load_multiple($nids + array_unique($result));

    $updated_nodes = array();
    $node_translations = array();
    $node_translation_sets = array();
    $instances = array();
    $field_info = array();

    foreach ($nids as $nid) {
      $node = $nodes[$nid];
      $original = $nodes[$node->tnid];
      $handler = entity_translation_get_handler('node', $original);
      // Instantiate the data and original properties of the translations.
      if (empty($handler->getTranslations()->data)) {
        $handler->initTranslations();
      }

      if (!isset($instances[$node->type])) {
        $instances[$node->type] = field_info_instances('node', $node->type);
      }

      reset($instances[$node->type]);

      foreach ($instances[$node->type] as $instance) {
        $field_name = $instance['field_name'];
        $field = isset($field_info[$field_name]) ? $field_info[$field_name] : $field_info[$field_name] = field_info_field($field_name);

        // Copy field data.
        if ($field['translatable']) {
          $langcode = isset($node->{$field_name}[$node->language]) ? $node->language : LANGUAGE_NONE;
          if (isset($node->{$field_name}[$langcode])) {
            $original->{$field_name}[$node->language] = $node->{$field_name}[$langcode];
          }
        }
      }

      // Add the new translation.
      $handler->setTranslation(array(
        'translate' => $node->translate,
        'status' => $node->status,
        'language' => $node->language,
        'source' => $original->language,
        'uid' => $node->uid,
        'created' => $node->created,
        'changed' => $node->changed,
      ));

      // Build a list of updated nodes. They will be saved after all the node
      // translation conversions.
      $updated_nodes[$original->nid] = $original;

      // Build a list of obsolete node translations to be unpublished.
      $node_translations[$node->nid] = $node;

      // Build a list of obsolete translations sets to be passed to module hook
      // implementations.
      $node_translation_sets[$original->nid][$node->nid] = $node;

      $context['sandbox']['count']++;
    }

    // Ensure that the multilingual support configuration is set to the right
    // value for the current node type.
    foreach ($instances as $type_name => $data) {
      variable_set("language_content_type_$type_name", ENTITY_TRANSLATION_ENABLED);
    }

    // Save field data and translations for the updated nodes.
    foreach ($updated_nodes as $nid => $node) {
      field_attach_presave('node', $node);
      field_attach_update('node', $node);
      entity_translation_get_handler('node', $node)->saveTranslations();

      foreach ($node_translation_sets[$nid] as $translation) {
        // Allow modules to upgrade their node additions, if possible.
        module_invoke_all('entity_translation_upgrade', $node, $translation);
      }
    }

    if (!empty($node_translations)) {
      $nids = array_keys($node_translations);

      // Unpublish the obsolete node translations.
      db_update('node')
        ->fields(array('status' => 0))
        ->condition('nid', $nids)
        ->execute();

      db_update('node_revision')
        ->fields(array('status' => 0))
        ->condition('nid', $nids)
        ->execute();

      // Populate history table.
      $columns = array('nid', 'tnid', 'language');
      $query = db_insert('entity_translation_upgrade_history')->fields($columns);

      foreach ($node_translations as $node) {
        $query->values((array) $node);
      }

      $query->execute();

      // Flush the modified nodes from the entity load cache.
      entity_get_controller('node')->resetCache($nids);
    }

    $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];

    if ($context['finished'] >= 1) {
      $context['results']['total'] = $context['sandbox']['total'];
    }
  }
}

/**
 * Removes the translation sets for all the upgraded nodes.
 */
function entity_translation_upgrade_complete(&$context) {
  if ($nids = db_query('SELECT DISTINCT etuh.tnid FROM {entity_translation_upgrade_history} etuh WHERE etuh.complete = 0')->fetchCol()) {
    // Remove translation sets for migrated nodes.
    db_query('UPDATE {node} SET tnid = 0 WHERE nid IN (:nids)', array(':nids' => $nids));
    entity_get_controller('node')->resetCache($nids);
    // Mark nodes as migrated.
    db_query('UPDATE {entity_translation_upgrade_history} SET complete = 1 WHERE complete = 0');
  }
}

/**
 * Implementations of hook_entity_translation_upgrade() on behalf of core
 * modules.
 */

/**
 * Implements hook_entity_translation_upgrade().
 */
function path_entity_translation_upgrade($node, $translation) {
  // Update URL aliases.
  db_update('url_alias')
    ->fields(array('source' => 'node/' . $node->nid))
    ->condition('source', 'node/' . $translation->nid)
    ->execute();
}

/**
 * Implements hook_entity_translation_upgrade().
 */
function comment_entity_translation_upgrade($node, $translation) {
  // Attach comments to the original node.
  db_update('comment')
    ->fields(array('nid' => $node->nid, 'language' => $translation->language))
    ->condition('nid', $translation->nid)
    ->execute();

  // Update node-comment statistics.
  $ncs = db_select('node_comment_statistics', 'ncs')
    ->fields('ncs')
    ->condition('nid', array($node->nid, $translation->nid))
    ->execute()
    ->fetchAllAssoc('nid');

  $last = $ncs[$node->nid]->last_comment_timestamp > $ncs[$translation->nid]->last_comment_timestamp;
  $ncs_updated = $last ? $ncs[$node->nid] : $ncs[$translation->nid];
  $ncs_updated->nid = $node->nid;
  $ncs_updated->comment_count = $ncs[$node->nid]->comment_count + $ncs[$translation->nid]->comment_count;

  db_update('node_comment_statistics')
    ->fields((array) $ncs_updated)
    ->condition('nid', $node->nid)
    ->execute();
}
