<?php

final class DifferentialRevisionCommentListView extends AphrontView {

  private $comments;
  private $handles;
  private $inlines;
  private $changesets;
  private $target;
  private $versusDiffID;
  private $id;

  public function setComments(array $comments) {
    assert_instances_of($comments, 'DifferentialComment');
    $this->comments = $comments;
    return $this;
  }

  public function setInlineComments(array $inline_comments) {
    assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface');
    $this->inlines = $inline_comments;
    return $this;
  }

  public function setHandles(array $handles) {
    assert_instances_of($handles, 'PhabricatorObjectHandle');
    $this->handles = $handles;
    return $this;
  }

  public function setChangesets(array $changesets) {
    assert_instances_of($changesets, 'DifferentialChangeset');
    $this->changesets = $changesets;
    return $this;
  }

  public function setTargetDiff(DifferentialDiff $target) {
    $this->target = $target;
    return $this;
  }

  public function setVersusDiffID($diff_vs) {
    $this->versusDiffID = $diff_vs;
    return $this;
  }

  public function getID() {
    if (!$this->id) {
      $this->id = celerity_generate_unique_node_id();
    }
    return $this->id;
  }

  public function render() {

    require_celerity_resource('differential-revision-comment-list-css');

    $engine = new PhabricatorMarkupEngine();
    $engine->setViewer($this->user);
    foreach ($this->comments as $comment) {
      $comment->giveFacebookSomeArbitraryDiff($this->target);

      $engine->addObject(
        $comment,
        DifferentialComment::MARKUP_FIELD_BODY);
    }

    foreach ($this->inlines as $inline) {
      $engine->addObject(
        $inline,
        PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
    }

    $engine->process();

    $inlines = mgroup($this->inlines, 'getCommentID');

    $num = 1;
    $html = array();
    foreach ($this->comments as $comment) {
      $view = new DifferentialRevisionCommentView();
      $view->setComment($comment);
      $view->setUser($this->user);
      $view->setHandles($this->handles);
      $view->setMarkupEngine($engine);
      $view->setInlineComments(idx($inlines, $comment->getID(), array()));
      $view->setChangesets($this->changesets);
      $view->setTargetDiff($this->target);
      $view->setVersusDiffID($this->versusDiffID);
      if ($comment->getAction() == DifferentialAction::ACTION_SUMMARIZE) {
        $view->setAnchorName('summary');
      } else if ($comment->getAction() == DifferentialAction::ACTION_TESTPLAN) {
        $view->setAnchorName('test-plan');
      } else {
        $view->setAnchorName('comment-'.$num);
        $num++;
      }

      $html[] = $view->render();
    }

    $objs = array_reverse(array_values($this->comments));
    $html = array_reverse(array_values($html));
    $user = $this->user;

    $last_comment = null;
    // Find the most recent comment by the viewer.
    foreach ($objs as $position => $comment) {
      if ($user && ($comment->getAuthorPHID() == $user->getPHID())) {
        if ($last_comment === null) {
          $last_comment = $position;
        } else if ($last_comment == $position - 1) {
          // If the viewer made several comments in a row, show them all. This
          // is a spaz rule for epriestley.
          $last_comment = $position;
        }
      }
    }

    $header = array();
    $hidden = array();
    if ($last_comment !== null) {
      foreach ($objs as $position => $comment) {
        if (!$comment->getID()) {
          // These are synthetic comments with summary/test plan information.
          $header[] = $html[$position];
          unset($html[$position]);
          continue;
        }
        if ($position <= $last_comment) {
          // Always show comments after the viewer's last comment.
          continue;
        }
        if ($position < 3) {
          // Always show the 3 most recent comments.
          continue;
        }
        $hidden[] = $position;
      }
    }

    if (count($hidden) <= 3) {
      // Don't hide if there's not much to hide.
      $hidden = array();
    }

    $header = array_reverse($header);


    $hidden = array_select_keys($html, $hidden);
    $visible = array_diff_key($html, $hidden);

    $hidden = array_reverse($hidden);
    $visible = array_reverse($visible);

    if ($hidden) {
      Javelin::initBehavior(
        'differential-show-all-comments',
        array(
          'markup' => implode("\n", $hidden),
        ));
      $hidden = javelin_tag(
        'div',
        array(
          'sigil' =>  "differential-all-comments-container",
        ),
        phutil_tag(
          'div',
          array(
            'class' => 'differential-older-comments-are-hidden',
          ),
          array(
            pht(
              '%s older comments are hidden.',
              new PhutilNumber(count($hidden))),
            ' ',
            javelin_tag(
              'a',
              array(
                'href' => '#',
                'mustcapture' => true,
                'sigil' => 'differential-show-all-comments',
              ),
              pht('Show all comments.')),
          )));
    } else {
      $hidden = null;
    }

    return javelin_tag(
      'div',
      array(
        'class' => 'differential-comment-list',
        'id' => $this->getID(),
      ),
      array_merge($header, array($hidden), $visible));
  }
}
