/*
 * 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.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import rx.Observable;
import rx.exceptions.Exceptions;
import timber.log.Timber;

abstract class LoggerInteractorImpl implements LoggerInteractor {

  @NonNull private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance();
  private static final int MAX_LINE_LENGTH = 80;
  @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)) {
        final String formattedMessage = formatMessage(message);
        bufferedWriter.write(formattedMessage);
        bufferedWriter.flush();
      } catch (IOException e) {
        throw Exceptions.propagate(e);
      }
      return true;
    });
  }

  @NonNull @CheckResult private String formatMessage(String message) {
    final String datePrefix = DATE_FORMAT.format(Calendar.getInstance().getTime());
    return String.format(Locale.getDefault(), "[%s] - %s", datePrefix, addLinebreaks(message));
  }

  // Bug: if a word in the input is longer than maxLineLength it will be appended to the current
  // line instead of on a too-long line of its own. I assume line length is something like
  // 80 or 120 characters, in which case this is unlikely to be a problem.
  @NonNull @CheckResult private String addLinebreaks(@NonNull String input) {
    final StringTokenizer tok = new StringTokenizer(input, " ");
    final StringBuilder output = new StringBuilder(input.length());
    int lineLen = 0;
    while (tok.hasMoreTokens()) {
      final String word = tok.nextToken();

      if (lineLen + word.length() > MAX_LINE_LENGTH) {
        output.append("\n");
        lineLen = 0;
      }

      output.append(word);
      lineLen += word.length();
    }

    return output.toString();
  }
}

@@ -770,16 +770,60 @@
 onNull;%0A
+import android.support.annotation.Nullable;%0A
 import c
@@ -1788,55 +1788,24 @@
 %0A  @
-SuppressWarnings(%22WeakerAccess%22) @NonNull final
+Nullable private
  Str
@@ -2006,16 +2006,100 @@
 rences;%0A
+  %7D%0A%0A  @SuppressWarnings(%22WeakerAccess%22) @NonNull @CheckResult String getLogPath() %7B
 %0A    fin
@@ -2125,24 +2125,54 @@
 tLogType();%0A
+%0A    if (logPath == null) %7B%0A  
     final St
@@ -2231,24 +2231,27 @@
 lutePath();%0A
+%0A  
     final St
@@ -2252,25 +2252,19 @@
 nal 
-String
+File
  logDir
-Path
  = n
@@ -2294,16 +2294,101 @@
 logger%22)
+;%0A      if (!logDir.mkdirs()) %7B%0A        Timber.e(%22Failed to make log dir: %25s%22, logDir
 .getAbso
@@ -2393,26 +2393,147 @@
 solutePath()
+)
 ;%0A
+        Timber.e(%22Will be unable to log to file%22);%0A      %7D%0A%0A      final String logDirPath = logDir.getAbsolutePath();%0A  
     logPath 
@@ -2576,24 +2576,31 @@
 lutePath();%0A
+    %7D%0A%0A
     Timber.d
@@ -2639,16 +2639,36 @@
 gPath);%0A
+    return logPath;%0A
   %7D%0A%0A  @
@@ -3028,23 +3028,28 @@
 le.just(
-l
+getL
 ogPath
+()
 )).flatM
@@ -3919,23 +3919,28 @@
 le.just(
-l
+getL
 ogPath
+()
 )).map(l

Make the logger directory before logging