// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.gitiles;

import static com.google.common.base.Preconditions.checkNotNull;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.gson.reflect.TypeToken;
import com.google.template.soy.data.SoyListData;
import com.google.template.soy.data.SoyMapData;

import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/** Serves the top level index page for a Gitiles host. */
public class HostIndexServlet extends BaseServlet {
  private static final long serialVersionUID = 1L;
  private static final Logger log = LoggerFactory.getLogger(HostIndexServlet.class);

  protected final GitilesUrls urls;
  private final GitilesAccess.Factory accessFactory;

  public HostIndexServlet(Renderer renderer, GitilesUrls urls,
      GitilesAccess.Factory accessFactory) {
    super(renderer);
    this.urls = checkNotNull(urls, "urls");
    this.accessFactory = checkNotNull(accessFactory, "accessFactory");
  }

  private Map<String, RepositoryDescription> getDescriptions(HttpServletRequest req,
      HttpServletResponse res) throws IOException {
    return getDescriptions(req, res, parseShowBranch(req));
  }

  private Map<String, RepositoryDescription> getDescriptions(HttpServletRequest req,
      HttpServletResponse res, Set<String> branches) throws IOException {
    Map<String, RepositoryDescription> descs;
    try {
      descs = accessFactory.forRequest(req).listRepositories(branches);
    } catch (RepositoryNotFoundException e) {
      res.sendError(SC_NOT_FOUND);
      return null;
    } catch (ServiceNotEnabledException e) {
      res.sendError(SC_FORBIDDEN);
      return null;
    } catch (ServiceNotAuthorizedException e) {
      res.sendError(SC_UNAUTHORIZED);
      return null;
    } catch (ServiceMayNotContinueException e) {
      // TODO(dborowitz): Show the error message to the user.
      res.sendError(SC_FORBIDDEN);
      return null;
    } catch (IOException err) {
      String name = urls.getHostName(req);
      log.warn("Cannot scan repositories" + (name != null ? "for " + name : ""), err);
      res.sendError(SC_SERVICE_UNAVAILABLE);
      return null;
    }
    return descs;
  }

  private SoyMapData toSoyMapData(RepositoryDescription desc, GitilesView view) {
    return new SoyMapData(
        "name", desc.name,
        "description", Strings.nullToEmpty(desc.description),
        "url", GitilesView.repositoryIndex()
            .copyFrom(view)
            .setRepositoryName(desc.name)
            .toUrl());
  }

  @Override
  protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
    Map<String, RepositoryDescription> descs = getDescriptions(req, res);
    if (descs == null) {
      return;
    }
    SoyListData repos = new SoyListData();
    for (RepositoryDescription desc : descs.values()) {
      repos.add(toSoyMapData(desc, ViewFilter.getView(req)));
    }

    renderHtml(req, res, "gitiles.hostIndex", ImmutableMap.of(
        "hostName", urls.getHostName(req),
        "baseUrl", urls.getBaseGitUrl(req),
        "repositories", repos));
  }

  @Override
  protected void doGetText(HttpServletRequest req, HttpServletResponse res) throws IOException {
    Set<String> branches = parseShowBranch(req);
    Map<String, RepositoryDescription> descs = getDescriptions(req, res, branches);
    if (descs == null) {
      return;
    }

    PrintWriter writer = startRenderText(req, res);
    for (RepositoryDescription repo : descs.values()) {
      for (String name : branches) {
        String ref = repo.branches.get(name);
        if (ref == null) {
          // Print stub (forty '-' symbols)
          ref = "----------------------------------------";
        }
        writer.print(ref);
        writer.print(' ');
      }
      writer.print(GitilesUrls.NAME_ESCAPER.apply(repo.name));
      writer.print('\n');
    }
    writer.flush();
    writer.close();
  }

  @Override
  protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
    Map<String, RepositoryDescription> descs = getDescriptions(req, res);
    if (descs == null) {
      return;
    }
    renderJson(req, res, descs, new TypeToken<Map<String, RepositoryDescription>>() {}.getType());
  }

  private static Set<String> parseShowBranch(HttpServletRequest req) {
    // Roughly match Gerrit Code Review's /projects/ API by supporting
    // both show-branch and b as query parameters.
    Set<String> branches = Sets.newLinkedHashSet();
    String[] values = req.getParameterValues("show-branch");
    if (values != null) {
      branches.addAll(Arrays.asList(values));
    }
    values = req.getParameterValues("b");
    if (values != null) {
      branches.addAll(Arrays.asList(values));
    }
    return branches;
  }
}

@@ -3474,16 +3474,17 @@
 null ? %22
+ 
 for %22 + 

Add missing space to "Cannot scan repositories" message