/*
 * Copyright 2014-present Facebook, Inc.
 *
 * 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.facebook.buck.log;

import com.facebook.buck.io.PathListing;
import com.facebook.buck.util.BuckConstant;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.Logger;

/**
 * Constructed by java.util.logging.LogManager via the system property
 * java.util.logging.LogManager.config.
 *
 * Extends LogManager's support for a single logging.properties file
 * to support a three-level config. Each existent property file is
 * concatenated together and used as a single LogManager configuration,
 * with later entries overriding earlier ones.
 *
 * 1) $BUCK_DIRECTORY/config/logging.properties
 * 2) $PROJECT_ROOT/.bucklogging.properties
 * 3) $PROJECT_ROOT/.bucklogging.local.properties
 */
@SuppressWarnings("checkstyle:hideutilityclassconstructor")
public class LogConfig {

  private static final byte[] NEWLINE = {'\n'};
  private static final long MAX_LOG_SIZE = 25 * 1024 * 1024;

  /**
   * Default constructor, called by LogManager.
   */
  public LogConfig() throws IOException {
    setupLogging();
  }

  /**
   * Creates the log output directory and concatenates logging.properties
   * files together to configure or re-configure LogManager.
   */
  public static synchronized void setupLogging() throws IOException {
    // Bug JDK-6244047: The default FileHandler does not handle the directory not existing,
    // so we have to create it before any log statements actually run.
    Files.createDirectories(BuckConstant.LOG_PATH);

    try {
      deleteOldLogFiles();
    } catch (IOException e) {
      System.err.format(
          "Error deleting old log files (ignored): %s\n",
          e.getMessage());
    }

    ImmutableList.Builder<InputStream> inputStreamsBuilder = ImmutableList.builder();
    if (!LogConfigPaths.MAIN_PATH.isPresent()) {
      System.err.format(
          "Error: Couldn't read system property %s (it should be set by buck_common or buck.cmd)\n",
          LogConfigPaths.BUCK_CONFIG_FILE_PROPERTY);
    } else {
      if (!addInputStreamForPath(LogConfigPaths.MAIN_PATH.get(), inputStreamsBuilder)) {
        System.err.format(
            "Error: Couldn't open logging properties file %s\n",
            LogConfigPaths.MAIN_PATH.get());
      }
    }

    // We ignore the return value for these files; they don't need to exist.
    addInputStreamForPath(LogConfigPaths.PROJECT_PATH, inputStreamsBuilder);
    addInputStreamForPath(LogConfigPaths.LOCAL_PATH, inputStreamsBuilder);

    // Concatenate each of the files together and read them in as a single properties file
    // for log settings.
    try (InputStream is =
         new SequenceInputStream(Iterators.asEnumeration(inputStreamsBuilder.build().iterator()))) {
        LogManager.getLogManager().readConfiguration(is);
    }
  }

  public static void flushLogs() {
    Logger rootLogger = LogManager.getLogManager().getLogger("");
    if (rootLogger == null) {
      return;
    }
    Handler[] handlers = rootLogger.getHandlers();
    if (handlers == null) {
      return;
    }
    for (Handler h : Arrays.asList(handlers)) {
      h.flush();
    }
  }

  private static boolean addInputStreamForPath(
      Path path,
      ImmutableList.Builder<InputStream> inputStreamsBuilder) {
    try {
      inputStreamsBuilder.add(new FileInputStream(path.toString()));
      // Handle the case where a file doesn't end with a newline.
      inputStreamsBuilder.add(new ByteArrayInputStream(NEWLINE));
      return true;
    } catch (FileNotFoundException e) {
      return false;
    }
  }

  private static void deleteOldLogFiles() throws IOException {
    for (Path path : PathListing.listMatchingPathsWithFilters(
             BuckConstant.LOG_PATH,
             "buck-*.log*",
             PathListing.GET_PATH_MODIFIED_TIME,
             PathListing.FilterMode.EXCLUDE,
             Optional.<Integer>absent(),
             Optional.of(MAX_LOG_SIZE))) {
      Files.deleteIfExists(path);
    }
  }
}

@@ -1938,10 +1938,11 @@
 E = 
-25
+100
  * 1

Increase max log size