// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.common.data;

import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Comment;
import com.google.gerrit.entities.PatchSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CommentDetail {
  protected List<Comment> a;
  protected List<Comment> b;

  private transient PatchSet.Id idA;
  private transient PatchSet.Id idB;
  private transient Map<Integer, List<Comment>> forA;
  private transient Map<Integer, List<Comment>> forB;

  public CommentDetail(PatchSet.Id idA, PatchSet.Id idB) {
    this.a = new ArrayList<>();
    this.b = new ArrayList<>();
    this.idA = idA;
    this.idB = idB;
  }

  protected CommentDetail() {}

  public void include(Change.Id changeId, Comment p) {
    PatchSet.Id psId = PatchSet.id(changeId, p.key.patchSetId);
    if (p.side == 0) {
      if (idA == null && idB.equals(psId)) {
        a.add(p);
      }
    } else if (p.side == 1) {
      if (idA != null && idA.equals(psId)) {
        a.add(p);
      } else if (idB.equals(psId)) {
        b.add(p);
      }
    }
  }

  public List<Comment> getCommentsA() {
    return a;
  }

  public List<Comment> getCommentsB() {
    return b;
  }

  public boolean isEmpty() {
    return a.isEmpty() && b.isEmpty();
  }

  public List<Comment> getForA(int lineNbr) {
    if (forA == null) {
      forA = index(a);
    }
    return get(forA, lineNbr);
  }

  public List<Comment> getForB(int lineNbr) {
    if (forB == null) {
      forB = index(b);
    }
    return get(forB, lineNbr);
  }

  private static List<Comment> get(Map<Integer, List<Comment>> m, int i) {
    List<Comment> r = m.get(i);
    return r != null ? orderComments(r) : Collections.emptyList();
  }

  /**
   * Order the comments based on their parent_uuid parent. It is possible to do this by iterating
   * over the list only once but it's probably overkill since the number of comments on a given line
   * will be small most of the time.
   *
   * @param comments The list of comments for a given line.
   * @return The comments sorted as they should appear in the UI
   */
  private static List<Comment> orderComments(List<Comment> comments) {
    // Map of comments keyed by their parent. The values are lists of comments since it is
    // possible for several comments to have the same parent (this can happen if two reviewers
    // click Reply on the same comment at the same time). Such comments will be displayed under
    // their correct parent in chronological order.
    Map<String, List<Comment>> parentMap = new HashMap<>();

    // It's possible to have more than one root comment if two reviewers create a comment on the
    // same line at the same time
    List<Comment> rootComments = new ArrayList<>();

    // Store all the comments in parentMap, keyed by their parent
    for (Comment c : comments) {
      String parentUuid = c.parentUuid;
      List<Comment> l = parentMap.get(parentUuid);
      if (l == null) {
        l = new ArrayList<>();
        parentMap.put(parentUuid, l);
      }
      l.add(c);
      if (parentUuid == null) {
        rootComments.add(c);
      }
    }

    // Add the comments in the list, starting with the head and then going through all the
    // comments that have it as a parent, and so on
    List<Comment> result = new ArrayList<>();
    addChildren(parentMap, rootComments, result);

    return result;
  }

  /** Add the comments to {@code outResult}, depth first */
  private static void addChildren(
      Map<String, List<Comment>> parentMap, List<Comment> children, List<Comment> outResult) {
    if (children != null) {
      for (Comment c : children) {
        outResult.add(c);
        addChildren(parentMap, parentMap.get(c.key.uuid), outResult);
      }
    }
  }

  private Map<Integer, List<Comment>> index(List<Comment> in) {
    HashMap<Integer, List<Comment>> r = new HashMap<>();
    for (Comment p : in) {
      List<Comment> l = r.get(p.lineNbr);
      if (l == null) {
        l = new ArrayList<>();
        r.put(p.lineNbr, l);
      }
      l.add(p);
    }
    return r;
  }
}
