// Copyright (C) 2017 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.
(function() {
  'use strict';

  Polymer({
    is: 'gr-diff-comment-thread-group',

    properties: {
      changeNum: String,
      comments: {
        type: Array,
        value() { return []; },
      },
      projectName: String,
      patchForNewThreads: String,
      range: Object,
      isOnParent: {
        type: Boolean,
        value: false,
      },
      parentIndex: {
        type: Number,
        value: null,
      },
      _threads: {
        type: Array,
        value() { return []; },
      },
    },

    observers: [
      '_commentsChanged(comments.*)',
    ],

    addNewThread(locationRange) {
      this.push('_threads', {
        comments: [],
        locationRange,
        patchNum: this.patchForNewThreads,
      });
    },

    removeThread(locationRange) {
      for (let i = 0; i < this._threads.length; i++) {
        if (this._threads[i].locationRange === locationRange) {
          this.splice('_threads', i, 1);
          return;
        }
      }
    },

    getThreadForRange(rangeToCheck) {
      const threads = [].filter.call(
          Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread'),
          thread => {
            return thread.locationRange === rangeToCheck;
          });
      if (threads.length === 1) {
        return threads[0];
      }
    },

    _commentsChanged() {
      this._threads = this._getThreadGroups(this.comments);
    },

    _sortByDate(threadGroups) {
      if (!threadGroups.length) { return; }
      return threadGroups.sort((a, b) => {
        // If a comment is a draft, it doesn't have a start_datetime yet.
        // Assume it is newer than the comment it is being compared to.
        if (!a.start_datetime) {
          return 1;
        }
        if (!b.start_datetime) {
          return -1;
        }
        return util.parseDate(a.start_datetime) -
            util.parseDate(b.start_datetime);
      });
    },

    _calculateLocationRange(range, comment) {
      return 'range-' + range.start_line + '-' +
          range.start_character + '-' +
          range.end_line + '-' +
          range.end_character + '-' +
          comment.__commentSide;
    },

    /**
     * Determines what the patchNum of a thread should be. Use patchNum from
     * comment if it exists, otherwise the property of the thread group.
     * This is needed for switching between side-by-side and unified views when
     * there are unsaved drafts.
     */
    _getPatchNum(comment) {
      return comment.patchNum || this.patchForNewThreads;
    },

    _getThreadGroups(comments) {
      const sortedComments = comments.slice(0).sort((a, b) =>
          util.parseDate(a.updated) - util.parseDate(b.updated));

      const threads = [];
      for (const comment of sortedComments) {
        let locationRange;
        if (!comment.range) {
          locationRange = 'line-' + comment.__commentSide;
        } else {
          locationRange = this._calculateLocationRange(comment.range, comment);
        }

        // If the comment is in reply to another comment, find that comment's
        // thread and append to it.
        if (comment.in_reply_to) {
          const thread = threads.find(thread =>
              thread.comments.some(c => c.id === comment.in_reply_to));
          if (thread) {
            thread.comments.push(comment);
            continue;
          }
        }

        // Otherwise, this comment starts its own thread.
        threads.push({
          start_datetime: comment.updated,
          comments: [comment],
          locationRange,
          commentSide: comment.__commentSide,
          patchNum: this._getPatchNum(comment),
        });
      }
      return threads;
    },
  });
})();