// Copyright (C) 2015 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.googlesource.gerrit.plugins.importer;

import com.google.common.base.Objects;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.common.SshKeyInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;

import org.eclipse.jgit.errors.ConfigInvalidException;

import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

interface GerritApi {

  class Factory {
    private final LocalApi localApi;

    @Inject
    Factory(LocalApi localApi) {
      this.localApi = localApi;
    }

    GerritApi create(String url, String user, String pass) {
      if (url == null) {
        return localApi;
      }
      return new RemoteApi(url, user, pass);
    }
  }

  public ProjectInfo getProject(String projectName) throws BadRequestException,
      IOException;

  public List<ChangeInfo> queryChanges(String projectName, int start, int limit)
      throws BadRequestException, IOException;

  public GroupInfo getGroup(String groupName) throws BadRequestException,
      IOException, OrmException;

  /**
   * Retrieves inline comments of a patch set.
   *
   * @param changeId numeric change ID
   * @param rev the revision
   * @return Iterable that provides the inline comments, or {@code null} if the
   *         revision does not exist
   * @throws IOException thrown if sending the request fails
   * @throws BadRequestException thrown if the response is neither
   *         {@code 200 OK} nor {@code 404 Not Found}
   */
  public Iterable<CommentInfo> getComments(int changeId, String rev)
      throws BadRequestException, IOException, OrmException;

  public List<SshKeyInfo> getSshKeys(String userId) throws BadRequestException,
      IOException, OrmException, ConfigInvalidException;

  public Version getVersion() throws BadRequestException, IOException;

  class Version implements Comparable<Version> {
    final String formatted;
    final Integer major;
    final Integer minor;
    final Integer patch;
    final String qualifier;

    Version(String formatted) {
      this.formatted = formatted;

      Matcher m = Pattern.compile("(\\d+)\\.(\\d+)(\\.(\\d+))?(-(.+))?")
          .matcher(formatted);
      if (m.matches()) {
        this.major = Integer.parseInt(m.group(1));
        this.minor = Integer.parseInt(m.group(2));
        this.patch = m.group(3) != null ? Integer.parseInt(m.group(4)) : null;
        this.qualifier = m.group(5) != null ? m.group(6) : null;
      } else {
        this.major = null;
        this.minor = null;
        this.patch = null;
        this.qualifier = null;
      }
    }

    @Override
    public int compareTo(Version o) {
      if (major == null || o.major == null) {
        // either of the compared version is not valid
        return -1;
      }
      if (major == o.major) {
        if (Objects.equal(minor, o.minor)) {
          if (Objects.equal(patch, o.patch)) {
            return 0;
          }
          if (o.patch == null) {
            return 1;
          }
          if (patch == null) {
            return -1;
          }
          return patch - o.patch;
        }
        if (o.minor == null) {
          return 1;
        }
        if (minor == null) {
          return -1;
        }
        return minor - o.minor;
      }
      return major - o.major;
    }

    @Override
    public String toString() {
      return formatted;
    }
  }
}

@@ -2880,24 +2880,52 @@
 eger patch;%0A
+    final Integer revision;%0A
     final St
@@ -2940,16 +2940,16 @@
 lifier;%0A
-
 %0A    Ver
@@ -3071,16 +3071,28 @@
 %5C%5Cd+))?(
+%5C%5C.(%5C%5Cd+))?(
 -(.+))?%22
@@ -3326,24 +3326,106 @@
 4)) : null;%0A
+        this.revision = m.group(5) != null ? Integer.parseInt(m.group(6)) : null;%0A
         this
@@ -3437,33 +3437,33 @@
 ifier = m.group(
-5
+7
 ) != null ? m.gr
@@ -3466,17 +3466,17 @@
 m.group(
-6
+8
 ) : null
@@ -3538,32 +3538,32 @@
 s.minor = null;%0A
-
         this.pat
@@ -3565,32 +3565,62 @@
 s.patch = null;%0A
+        this.revision = null;%0A
         this.qua

Handle sub-patch versioning