<?php

/*
 ********************************************************************
 *                   Translations CLI commands                      *
 ********************************************************************
 */

if (php_sapi_name() != "cli")
  return true;

/**
 * Convert PO file to JSON file
 *
 * @param  string $locale  The locale of the input PO file
 * @param  string $path    The path of the input PO file
 *
 * @return string JSON encoded file content
 */
function po2json($locale, $path) {
  $fileHandler = new \Sepia\PoParser\SourceHandler\FileSystem($path);
  $poparser = new \Sepia\PoParser\Parser($fileHandler);
  $catalog = $poparser->parse();
  $headers = $catalog->getHeader();

  $messages = array();
  foreach ($catalog->getEntries() as $entry) {
    // msg id json format
    $msg = $entry->getMsgStr();
    if ($entry->isPlural())
      $msg = array($msg, $entry->getMsgIdPlural());
    $messages[$entry->getMsgId()] = $msg;
  }
  return json_encode(array(
    'messages' => $messages,
    'locale' => $locale,
    'domain' => TEXT_DOMAIN,
    'plural_expr' => '(n > 1)',
  ));
}

/**
 * Command to extract messages from PHP/JS & template files and
 * generate the lang/messages.pot file.
 *
 * @param  array $command_args  The command arguments
 * @return void
 */
function cli_extract_messages($command_args) {
  global $root_dir_path, $root_lang_dir, $smarty_templates_dir;

  // List PHP files to parse
  $php_files = run_external_command(
    array('find', escapeshellarg($root_dir_path), '-name', "'*.php'"),
    null,   // no STDIN data
    false   // do not escape command args (already done)
  );
  if (!is_array($php_files) || $php_files[0] != 0) {
    logging('FATAL', _("Fail to list PHP files."));
  }

  // Extract messages from PHP files using xgettext
  $result = run_external_command(
    array(
      "xgettext",
      "--from-code utf-8",
      "--language=PHP",
      "-o", "$root_lang_dir/php-messages.pot",  // Output
      "--omit-header",                          // No POT header
      "--keyword=___",                          // Handle custom ___() translation function
      "--files=-"                               // Read files to parse from STDIN
    ),
    $php_files[1]                               // Pass PHP files list via STDIN
  );
  if (!is_array($result) || $result[0] != 0)
    logging('FATAL', _("Fail to extract messages from PHP files using xgettext."));


  // List JS files to parse
  $js_files = run_external_command(
    array('find', escapeshellarg("$root_dir_path/public_html/js"), '-name', "'*.js'"),
    null,   // no STDIN data
    false   // do not escape command args (already done)
  );
  if (!is_array($js_files) || $js_files[0] != 0) {
    logging('FATAL', _("Fail to list JS files."));
  }

  // Extract messages from JS files using xgettext
  $result = run_external_command(
    array(
      "xgettext",
      "--from-code utf-8",
      "--language=JavaScript",
      "-o", "$root_lang_dir/js-messages.pot", // Output
      "--omit-header",                        // No POT header
      "--keyword=___",                        // Handle custom ___() translation function
      "--files=-"                             // Read files to parse from STDIN
    ),
    $js_files[1]                              // Pass JS files list via STDIN
  );
  if (!is_array($result) || $result[0] != 0)
    logging('FATAL', _("Fail to extract messages from JS files using xgettext."));

  // Extract messages from templates files using tsmarty2c.php
  $result = run_external_command(
    array (
      "$root_dir_path/vendor/bin/tsmarty2c.php",
      "-o", "$root_lang_dir/templates-messages.pot",
      $smarty_templates_dir,
    )
  );
  if (!is_array($result) || $result[0] != 0)
    logging(
      'FATAL',
      _("Fail to extract messages from template files using tsmarty2c.php script."));

  $fd = fopen("$root_lang_dir/headers.pot", 'w');
  $headers = array(
    'msgid ""',
    'msgstr ""',
    '"POT-Creation-Date: '.date('Y-m-d H:iO').'\n"',
    '"PO-Revision-Date: '.date('Y-m-d H:iO').'\n"',
    '"MIME-Version: 1.0\n"',
    '"Content-Type: text/plain; charset=utf-8\n"',
    '"Content-Transfer-Encoding: 8bit\n"',
  );
  fwrite($fd, implode("\n", $headers));
  fclose($fd);

  // Merge previous results in messages.pot file using msgcat
  $result = run_external_command(array(
    'msgcat',
    "$root_lang_dir/headers.pot",
    "$root_lang_dir/php-messages.pot",
    "$root_lang_dir/js-messages.pot",
    "$root_lang_dir/templates-messages.pot",
    "-t", "utf-8", "--use-first",
    "-o", "$root_lang_dir/messages.pot",
  ));
  if (!is_array($result) || $result[0] != 0)
    logging('FATAL', _("Fail to merge messages using msgcat."));
}
add_cli_command(
  'extract_messages',
  'cli_extract_messages',
  ___("Extract messages that need to be translated"),
  null,
  ___("This command could be used to generate/update lang/messages.pot file.")
);

/**
 * Command to update messages from lang/messages.pot file to
 * all PO file in lang/[lang]/LC_MESSAGES.
 *
 * @param  array $command_args  The command arguments
 * @return bool
 */
function cli_update_messages($command_args) {
  global $root_dir_path, $root_lang_dir, $smarty_templates_dir;

  $compendium_args = array();
  foreach ($command_args as $path) {
    if (!file_exists($path))
      logging('FATAL', _("Compendium file %s not found."), $path);
    $compendium_args[] = '-C';
    $compendium_args[] = $path;
  }

  $pot_file = "$root_lang_dir/messages.pot";
  if (!is_file($pot_file))
    logging('FATAL', _("POT file not found (%s). Please run extract_messages first."), $pot_file);

  if ($dh = opendir($root_lang_dir)) {
    $error = False;
    while (($file = readdir($dh)) !== false) {
      if (
        !is_dir($root_lang_dir . '/' . $file) ||
        in_array($file, array('.', '..')) ||
        is_link($root_lang_dir . '/' . $file)
      )
        continue;

      logging('DEBUG', _("Lang directory '%s' found"), $file);

      // Check LC_MESSAGES directory exists
      $lang = $file;
      $lang_dir = $root_lang_dir . '/' . $file . '/LC_MESSAGES' ;
      if (!is_dir($lang_dir)) {
        logging('DEBUG', _("LC_MESSAGES directory not found in lang '%s' directory, ignore it."),
          $lang);
        continue;
      }

      $po_file = $lang_dir . '/' . TEXT_DOMAIN . '.po';
      $created = false;
      if (!is_file($po_file)) {
        // Init PO file from POT file using msginit
        $result = run_external_command(
          array("msginit", "-i", "$pot_file", "-l", "$lang", "-o", $po_file)
        );
        if (is_array($result) && $result[0] == 0) {
          $created = true;
        } else {
          logging('ERROR', _("Fail to init messages in %s PO file using msginit (%s)."),
            $lang, $po_file);
          $error = True;
        }
      }

      // Update messages in PO file from POT file using msgmerge
      // Note: msginit does not accept compendium files, so we also run
      // msgmerge on creation with compendium file(s).
      if (is_file($po_file) && (!$created || $compendium_args)) {
        $result = run_external_command(
          array_merge(
            array("msgmerge", "-q", "-U"),
            $compendium_args,
            array($po_file, $pot_file)
          )
        );
        if (!is_array($result) || $result[0] != 0) {
          logging('ERROR', _("Fail to update messages in %s PO file using msgmerge (%s)."),
            $lang, $po_file);
          $error = True;
        }
      }
      elseif (!$created) {
        logging('DEBUG', _("PO file not found in lang '%s' directory, ignore it."), $lang);
      }
    }
    closedir($dh);
    return !$error;
  }

  logging('FATAL', _("Fail to open root lang directory (%s)."), $root_dir_path);
  return false;
}
add_cli_command(
  'update_messages',
  'cli_update_messages',
  ___("Update messages in translation PO lang files"),
  null,
  ___("This command could be used to init/update PO files in lang/*/LC_MESSAGES directories.")
);

/**
 * Command to compile messages from existing translation PO lang files
 * to corresponding MO files and as JSON catalog (for translation in JS).
 *
 * @param  array $command_args  The command arguments
 * @return bool
 */
function cli_compile_messages($command_args) {
  global $root_dir_path, $root_lang_dir, $smarty_templates_dir;

  if ($dh = opendir($root_lang_dir)) {
    $error = False;
    while (($file = readdir($dh)) !== false) {
      if (
        !is_dir($root_lang_dir . '/' . $file) ||
        in_array($file, array('.', '..'))
      )
        continue;

      if (is_link($root_lang_dir . '/' . $file)) {
        $real_lang_dir = readlink($root_lang_dir . '/' . $file);
        if (dirname($real_lang_dir) != '.' || !is_dir($root_lang_dir . '/' . $real_lang_dir))
          continue;
        $lang = $file;
        logging('DEBUG', _("Lang alias symlink found: %s -> %s"), $lang, $real_lang_dir);

        // Create JSON catalog symlink (if not exists)
        $js_link = "$root_dir_path/public_html/translations/$lang.js";
        $link_target = "$real_lang_dir.js";
        if (!file_exists($js_link)) {
          if (symlink($link_target, $js_link)) {
            logging('INFO', _("JSON catalog symlink for %s -> %s created (%s)"),
              $lang, $real_lang_dir, $js_link);
          }
          else {
            logging('ERROR', _("Fail to create JSON catalog symlink for %s -> %s (%s)"),
              $lang, $real_lang_dir, $js_link);
            $error = True;
          }
        }
        elseif (readlink($js_link) == $link_target) {
          logging('DEBUG', _("JSON catalog symlink for %s -> %s already exist (%s)"),
            $lang, $real_lang_dir, $js_link);
        }
        else {
          logging(
            'WARNING',
            _("JSON catalog file for %s already exist, but it's not a symlink to %s (%s)"),
            $lang, $real_lang_dir, $js_link
          );
          $error = True;
        }
        continue;
      }

      logging('DEBUG', _("Lang directory '%s' found"), $file);

      // Check LC_MESSAGES directory exists
      $lang = $file;
      $lang_dir = $root_lang_dir . '/' . $file . '/LC_MESSAGES' ;
      if (!is_dir($lang_dir)) {
        logging('DEBUG', _("LC_MESSAGES directory not found in lang '%s' directory, ignore it."),
          $lang);
        continue;
      }

      // Test .PO file is present
      $po_file = $lang_dir . '/' . TEXT_DOMAIN . '.po';
      if (!is_file($po_file)) {
        logging('DEBUG', _("PO file not found in lang '%s' directory, ignore it."),
          $lang);
        continue;
      }

      $mo_file = preg_replace('/\.po$/', '.mo', $po_file);

      // Compile messages from PO file to MO file using msgfmt
      $result = run_external_command(
        array("msgfmt", "-o", $mo_file, $po_file)
      );
      if (!is_array($result) || $result[0] != 0) {
        logging(
          'ERROR',
          _("Fail to compile messages from %s PO file as MO file using msgfmt (%s)."),
          $lang, $po_file
        );
        $error = True;
      }

      // Compile messages from PO file to JSON catalog file
      $json_catalog = po2json($lang, $po_file);
      $js_file = "$root_dir_path/public_html/translations/$lang.js";
      if(!$fd = fopen($js_file, 'w')) {
        logging('ERROR', _("Fail to open %s JSON catalog file in write mode (%s)."),
          $lang, $js_file);
        $error = True;
      }
      elseif (fwrite($fd, sprintf("translations_data = %s;", $json_catalog)) === false) {
        logging('ERROR', _("Fail to write %s JSON catalog in file (%s)."),
          $lang, $js_file);
        $error = True;
      }
      else {
        logging('INFO', _("%s JSON catalog writed (%s)."), $lang, $js_file);
      }
    }
    closedir($dh);

    return !$error;
  }
  logging('FATAL', _("Fail to open root lang directory (%s)."), $root_dir_path);
  return false;
}
add_cli_command(
  'compile_messages',
  'cli_compile_messages',
  ___(
    "Compile messages from existing translation PO lang files to ".
    "corresponding MO files and JSON catalogs"
  ),
  null,
  ___(
    "This command could be used to compile PO files in lang/*/LC_MESSAGES ".
    "directories to MO files and as JSON catalogs in public_html/translations."
  )
);

# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab