/*
 * Copyright 2017 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.job;

import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import com.evernote.android.job.util.support.PersistableBundleCompat;
import com.pyamsoft.powermanager.base.logger.Logger;
import com.pyamsoft.powermanager.model.StateModifier;
import com.pyamsoft.powermanager.model.StateObserver;
import javax.inject.Inject;
import javax.inject.Named;

public abstract class BaseJob {

  @SuppressWarnings("WeakerAccess") @Inject JobQueuer jobQueuer;
  @SuppressWarnings("WeakerAccess") @Inject @Named("obs_charging_state") StateObserver
      chargingObserver;
  private QueuerType type;
  private boolean ignoreWhenCharging;
  private long windowOnTime;
  private long windowOffTime;
  private boolean repeat;
  private String jobTag;

  private void initialize(@NonNull String tag, @NonNull PersistableBundleCompat extras) {
    jobTag = tag;
    ignoreWhenCharging = extras.getBoolean(JobQueuerImpl.KEY_IGNORE_CHARGING, false);
    type = QueuerType.valueOf(extras.getString(JobQueuerImpl.KEY_QUEUE_TYPE, null));
    windowOnTime = extras.getLong(JobQueuerImpl.KEY_ON_WINDOW, 0);
    windowOffTime = extras.getLong(JobQueuerImpl.KEY_OFF_WINDOW, 0);
    repeat = extras.getBoolean(JobQueuerImpl.KEY_PERIODIC, false);
    inject();
  }

  @CheckResult
  private boolean runJob(@NonNull String tag, @NonNull PersistableBundleCompat extras) {
    if (isStopped()) {
      getLogger().w("Stop job early");
      return false;
    }

    initialize(tag, extras);
    if (type == QueuerType.SCREEN_OFF_DISABLE || type == QueuerType.SCREEN_OFF_ENABLE) {
      if (ignoreWhenCharging) {
        if (chargingObserver.enabled()) {
          getLogger().w("Do not run job because device is charging");
          return true;
        }
      }
    }

    if (isStopped()) {
      getLogger().w("Stop job early");
      return false;
    }

    getLogger().i("Run job: %s [%s]", type, jobTag);
    if (type == QueuerType.SCREEN_ON_ENABLE || type == QueuerType.SCREEN_OFF_ENABLE) {
      set();
    } else {
      unset();
    }

    if (isStopped()) {
      getLogger().w("Stop job early");
      return false;
    }

    return true;
  }

  private void repeatIfRequired() {
    if (!repeat) {
      getLogger().w("Job is not repeating. Do not re-queue");
      return;
    }

    final QueuerType newType = type.flip();
    final long newDelayTime;
    // Switch them
    if (newType == QueuerType.SCREEN_ON_ENABLE || newType == QueuerType.SCREEN_ON_DISABLE) {
      newDelayTime = windowOffTime * 1000L;
    } else {
      newDelayTime = windowOnTime * 1000L;
    }

    final JobQueuerEntry entry = JobQueuerEntry.builder(jobTag)
        .ignoreIfCharging(ignoreWhenCharging)
        .delay(newDelayTime)
        .repeating(true)
        .repeatingOffWindow(windowOffTime)
        .repeatingOnWindow(windowOnTime)
        .type(newType)
        .build();

    getLogger().d("Requeue job with new type: %s", newType);
    jobQueuer.cancel(jobTag);
    jobQueuer.queue(entry);
  }

  private void set() {
    if (isStopped()) {
      getLogger().w("Stop job early");
      return;
    }

    getModifier().set();
  }

  private void unset() {
    if (isStopped()) {
      getLogger().w("Stop job early");
      return;
    }

    getModifier().unset();
  }

  /**
   * Runs the Job. Called either by managed jobs or directly by the JobQueuer
   */
  void run(@NonNull String tag, @NonNull PersistableBundleCompat extras) {
    if (runJob(tag, extras)) {
      repeatIfRequired();
    }
  }

  /**
   * Override in the actual ManagedJobs to call Job.isCancelled();
   *
   * If it is not a managed job it never isStopped, always run to completion
   */
  @CheckResult boolean isStopped() {
    return false;
  }

  abstract void inject();

  @CheckResult @NonNull abstract Logger getLogger();

  @CheckResult @NonNull abstract StateObserver getObserver();

  @CheckResult @NonNull abstract StateModifier getModifier();
}

@@ -1377,33 +1377,8 @@
 eat;
-%0A  private String jobTag;
 %0A%0A  
@@ -1410,37 +1410,16 @@
 NonNull 
-String tag, @NonNull 
 Persista
@@ -1448,26 +1448,8 @@
 ) %7B%0A
-    jobTag = tag;%0A
     
@@ -1991,33 +1991,41 @@
 (%22Stop job early
-%22
+: %22 + tag
 );%0A      return 
@@ -2053,21 +2053,16 @@
 tialize(
-tag, 
 extras);
@@ -2291,17 +2291,25 @@
 charging
-%22
+: %22 + tag
 );%0A     
@@ -2401,33 +2401,41 @@
 (%22Stop job early
-%22
+: %22 + tag
 );%0A      return 
@@ -2492,20 +2492,17 @@
 , type, 
-jobT
+t
 ag);%0A   
@@ -2595,130 +2595,69 @@
     
-set();%0A    %7D else %7B%0A      unset();%0A    %7D%0A%0A    if (isStopped()) %7B%0A      getLogger().w(%22Stop job early%22);%0A      return false
+getModifier().set();%0A    %7D else %7B%0A      getModifier().unset()
 ;%0A  
@@ -2715,16 +2715,35 @@
 equired(
+@NonNull String tag
 ) %7B%0A    
@@ -2815,17 +2815,25 @@
 re-queue
-%22
+: %22 + tag
 );%0A     
@@ -3196,20 +3196,17 @@
 builder(
-jobT
+t
 ag)%0A    
@@ -3475,16 +3475,21 @@
 type: %25s
+ %5B%25s%5D
 %22, newTy
@@ -3490,16 +3490,21 @@
  newType
+, tag
 );%0A    j
@@ -3523,12 +3523,9 @@
 cel(
-jobT
+t
 ag);
@@ -3562,284 +3562,8 @@
  %7D%0A%0A
-  private void set() %7B%0A    if (isStopped()) %7B%0A      getLogger().w(%22Stop job early%22);%0A      return;%0A    %7D%0A%0A    getModifier().set();%0A  %7D%0A%0A  private void unset() %7B%0A    if (isStopped()) %7B%0A      getLogger().w(%22Stop job early%22);%0A      return;%0A    %7D%0A%0A    getModifier().unset();%0A  %7D%0A%0A
   /*
@@ -3777,16 +3777,19 @@
 equired(
+tag
 );%0A    %7D

clean up base job. better logging, cleaner flow