<?php

final class ArcanistMercurialRepositoryMarkerQuery
  extends ArcanistRepositoryMarkerQuery {

  protected function newLocalRefMarkers() {
    return $this->newMarkers();
  }

  protected function newRemoteRefMarkers(ArcanistRemoteRef $remote = null) {
    return $this->newMarkers($remote);
  }

  private function newMarkers(ArcanistRemoteRef $remote = null) {
    $api = $this->getRepositoryAPI();

    // In native Mercurial it is difficult to identify remote markers, and
    // complicated to identify local markers efficiently. We use an extension
    // to provide a command which works like "git for-each-ref" locally and
    // "git ls-remote" when given a remote.

    $argv = array();
    foreach ($api->getMercurialExtensionArguments() as $arg) {
      $argv[] = $arg;
    }
    $argv[] = 'arc-ls-markers';

    // NOTE: In remote mode, we're using passthru and a tempfile on this
    // because it's a remote command and may prompt the user to provide
    // credentials interactively. In local mode, we can just read stdout.

    if ($remote !== null) {
      $tmpfile = new TempFile();
      Filesystem::remove($tmpfile);

      $argv[] = '--output';
      $argv[] = phutil_string_cast($tmpfile);
    }

    $argv[] = '--';

    if ($remote !== null) {
      $argv[] = $remote->getRemoteName();
    }

    if ($remote !== null) {
      $passthru = $api->newPassthru('%Ls', $argv);

      $err = $passthru->execute();
      if ($err) {
        throw new Exception(
          pht(
            'Call to "hg arc-ls-markers" failed with error "%s".',
            $err));
      }

      $raw_data = Filesystem::readFile($tmpfile);
      unset($tmpfile);
    } else {
      $future = $api->newFuture('%Ls', $argv);
      list($raw_data) = $future->resolve();
    }

    $items = phutil_json_decode($raw_data);

    $markers = array();
    foreach ($items as $item) {
      if (!empty($item['isClosed'])) {
        // NOTE: For now, we ignore closed branch heads.
        continue;
      }

      $node = $item['node'];

      switch ($item['type']) {
        case 'branch':
          $marker_type = ArcanistMarkerRef::TYPE_BRANCH;
          break;
        case 'bookmark':
          $marker_type = ArcanistMarkerRef::TYPE_BOOKMARK;
          break;
        case 'commit-state':
          $marker_type = ArcanistMarkerRef::TYPE_COMMIT_STATE;
          break;
        case 'branch-state':
          $marker_type = ArcanistMarkerRef::TYPE_BRANCH_STATE;
          break;
        default:
          throw new Exception(
            pht(
              'Call to "hg arc-ls-markers" returned marker of unknown '.
              'type "%s".',
              $item['type']));
      }

      $commit_ref = $api->newCommitRef()
        ->setCommitHash($node);

      $marker_ref = id(new ArcanistMarkerRef())
        ->setName($item['name'])
        ->setCommitHash($node)
        ->attachCommitRef($commit_ref);

      if (isset($item['description'])) {
        $description = $item['description'];
        $commit_ref->attachMessage($description);

        $description_lines = phutil_split_lines($description, false);
        $marker_ref->setSummary(head($description_lines));
      }

      $marker_ref
        ->setMarkerType($marker_type)
        ->setIsActive(!empty($item['isActive']));

      $markers[] = $marker_ref;
    }

    return $markers;
  }

}
