/*
 * Copyright 2016 Peter Kenji Yamanaka
 *
 * 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.pyamsoft.powermanager.dagger.logger;

import android.content.Context;
import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import com.pyamsoft.powermanager.PowerManagerPreferences;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import rx.Observable;
import rx.exceptions.Exceptions;
import timber.log.Timber;

abstract class LoggerInteractorImpl implements LoggerInteractor {

  @SuppressWarnings("WeakerAccess") @NonNull final Context appContext;
  @SuppressWarnings("WeakerAccess") @NonNull final PowerManagerPreferences preferences;
  @SuppressWarnings("WeakerAccess") @NonNull final String logPath;

  LoggerInteractorImpl(@NonNull Context context, @NonNull PowerManagerPreferences preferences) {
    this.appContext = context.getApplicationContext();
    this.preferences = preferences;

    final String type = getLogType();
    final String filesDirPath = appContext.getFilesDir().getAbsolutePath();
    final String logDirPath = new File(filesDirPath, "logger").getAbsolutePath();
    logPath = new File(logDirPath, type).getAbsolutePath();
    Timber.d("%s Log location: %s", type, logPath);
  }

  @NonNull @Override public Observable<Boolean> isLoggingEnabled() {
    return Observable.defer(() -> {
      // TODO get from preferences
      Timber.d("isLoggingEnabled: return hardcoded TRUE");
      return Observable.just(true);
    });
  }

  @NonNull @Override public Observable<String> getLogContents() {
    return Observable.defer(() -> Observable.just(logPath)).flatMap(logLocation -> {
      final List<String> fileContents = new ArrayList<>();
      try (
          final BufferedInputStream bufferedInputStream = new BufferedInputStream(
              appContext.openFileInput(logLocation));
          final InputStreamReader inputStreamReader = new InputStreamReader(bufferedInputStream,
              StandardCharsets.UTF_8);
          final BufferedReader reader = new BufferedReader(inputStreamReader)) {
        String line = reader.readLine();
        while (line != null) {
          fileContents.add(line);
          line = reader.readLine();
        }
      } catch (IOException e) {
        throw Exceptions.propagate(e);
      }

      return Observable.from(fileContents);
    });
  }

  @NonNull @Override public Observable<Boolean> appendToLog(@NonNull String message) {
    return Observable.defer(() -> Observable.just(logPath)).map(logLocation -> {
      try (
          final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
              appContext.openFileOutput(logLocation, Context.MODE_APPEND));
          final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(bufferedOutputStream,
              StandardCharsets.UTF_8);
          final BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter)) {
        bufferedWriter.write(message);
        bufferedWriter.flush();
      } catch (IOException e) {
        throw Exceptions.propagate(e);
      }
      return true;
    });
  }
}

@@ -1123,16 +1123,45 @@
 arsets;%0A
+import java.text.DateFormat;%0A
 import j
@@ -1197,20 +1197,106 @@
 va.util.
-List
+Calendar;%0Aimport java.util.List;%0Aimport java.util.Locale;%0Aimport java.util.StringTokenizer
 ;%0Aimport
@@ -1438,16 +1438,156 @@
 ctor %7B%0A%0A
+  @NonNull private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance();%0A  private static final int MAX_LINE_LENGTH = 80;%0A
   @Suppr
@@ -3988,24 +3988,88 @@
 mWriter)) %7B%0A
+        final String formattedMessage = formatMessage(message);%0A
         buff
@@ -4085,17 +4085,26 @@
 r.write(
-m
+formattedM
 essage);
@@ -4234,16 +4234,16 @@
 n true;%0A
-
     %7D);%0A
@@ -4245,11 +4245,1055 @@
  %7D);%0A  %7D
+%0A%0A  @NonNull @CheckResult private String formatMessage(String message) %7B%0A    final String datePrefix = DATE_FORMAT.format(Calendar.getInstance().getTime());%0A    return String.format(Locale.getDefault(), %22%5B%25s%5D - %25s%22, datePrefix, addLinebreaks(message));%0A  %7D%0A%0A  // Bug: if a word in the input is longer than maxLineLength it will be appended to the current%0A  // line instead of on a too-long line of its own. I assume line length is something like%0A  // 80 or 120 characters, in which case this is unlikely to be a problem.%0A  @NonNull @CheckResult private String addLinebreaks(@NonNull String input) %7B%0A    final StringTokenizer tok = new StringTokenizer(input, %22 %22);%0A    final StringBuilder output = new StringBuilder(input.length());%0A    int lineLen = 0;%0A    while (tok.hasMoreTokens()) %7B%0A      final String word = tok.nextToken();%0A%0A      if (lineLen + word.length() %3E MAX_LINE_LENGTH) %7B%0A        output.append(%22%5Cn%22);%0A        lineLen = 0;%0A      %7D%0A%0A      output.append(word);%0A      lineLen += word.length();%0A    %7D%0A%0A    return output.toString();%0A  %7D
 %0A%7D%0A

Format log messages with date/time and cleaner line breaking