package com.xlythe.service.weather;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import androidx.work.Data;
import androidx.work.ListenableWorker;
import androidx.work.WorkerParameters;
import androidx.work.impl.utils.futures.SettableFuture;

import com.google.common.util.concurrent.ListenableFuture;

public abstract class WeatherService extends ListenableWorker {
    static final boolean DEBUG = Weather.DEBUG;

    private static final HandlerThread sBackgroundThread = new HandlerThread("ServiceBackgroundThread");

    protected enum Result {
        SUCCESS, RESCHEDULE, FAILURE;
    }

    static {
        sBackgroundThread.start();
    }

    public WeatherService(@NonNull Context appContext, @NonNull WorkerParameters params) {
        super(appContext, params);
    }

    protected Context getContext() {
        return getApplicationContext();
    }

    @NonNull
    @UiThread
    @Override
    public ListenableFuture<ListenableWorker.Result> startWork() {
        SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
        post(() -> {
            switch (onRunTask(toBundle(getInputData()))) {
                case SUCCESS:
                    future.set(ListenableWorker.Result.success());
                    break;
                case FAILURE:
                    future.set(ListenableWorker.Result.failure());
                    break;
                case RESCHEDULE:
                    future.set(ListenableWorker.Result.retry());
                    break;
            }
        });
        return future;
    }

    protected static Bundle toBundle(Data data) {
        Bundle bundle = new Bundle();
        for (String key : data.getKeyValueMap().keySet()) {
            if (data.hasKeyWithValueOfType(key, Boolean.TYPE)) {
                bundle.putBoolean(key, data.getBoolean(key, false));
            } else if (data.hasKeyWithValueOfType(key, Byte.TYPE)) {
                bundle.putByte(key, data.getByte(key, (byte) 0));
            } else if (data.hasKeyWithValueOfType(key, Byte[].class)) {
                bundle.putByteArray(key, data.getByteArray(key));
            } else if (data.hasKeyWithValueOfType(key, Double.TYPE)) {
                bundle.putDouble(key, data.getDouble(key, 0D));
            } else if (data.hasKeyWithValueOfType(key, Double[].class)) {
                bundle.putDoubleArray(key, data.getDoubleArray(key));
            } else if (data.hasKeyWithValueOfType(key, Float.TYPE)) {
                bundle.putFloat(key, data.getFloat(key, 0F));
            } else if (data.hasKeyWithValueOfType(key, Float[].class)) {
                bundle.putFloatArray(key, data.getFloatArray(key));
            } else if (data.hasKeyWithValueOfType(key, Integer.TYPE)) {
                bundle.putInt(key, data.getInt(key, 0));
            } else if (data.hasKeyWithValueOfType(key, Integer[].class)) {
                bundle.putIntArray(key, data.getIntArray(key));
            } else if (data.hasKeyWithValueOfType(key, Long.TYPE)) {
                bundle.putLong(key, data.getLong(key, 0L));
            } else if (data.hasKeyWithValueOfType(key, Long[].class)) {
                bundle.putLongArray(key, data.getLongArray(key));
            } else if (data.hasKeyWithValueOfType(key, String.class)) {
                bundle.putString(key, data.getString(key));
            } else if (data.hasKeyWithValueOfType(key, String[].class)) {
                bundle.putStringArray(key, data.getStringArray(key));
            }
        }
        return bundle;
    }

    protected static Data toData(Bundle bundle) {
        return new Data.Builder().build();
    }

    @UiThread
    @Override
    public void onStopped() {
        // Ignored
    }

    protected static void runImmediately(Context context, Class<? extends WeatherService> clazz, @Nullable Bundle extras) {
        if (DEBUG) {
            Log.d(clazz.getSimpleName(), "Running " + clazz.getSimpleName() + " immediately");
        }

        post(() -> {
            try {
                // TODO: Pass params into class
                WeatherService service = clazz.newInstance();

                if (DEBUG) {
                    Log.d(clazz.getSimpleName(), "Now executing " + clazz.getSimpleName() + ".startWork");
                }
                service.onRunTask(extras);
            } catch (Exception e) {
                if (DEBUG) {
                    Log.d(clazz.getSimpleName(), "Failed to run immediately", e);
                }
            }
        });
    }

    protected static void post(Runnable runnable) {
        new Handler(sBackgroundThread.getLooper()).post(runnable);
    }

    protected static void broadcast(Context context, String action) {
        Intent intent = new Intent(action);
        intent.setPackage(context.getPackageName());
        context.sendBroadcast(intent);
    }

    protected void broadcast(String action) {
        broadcast(getContext(), action);
    }

    @WorkerThread
    protected abstract Result onRunTask(@Nullable Bundle extras);

    protected abstract boolean isScheduled();

    protected abstract void schedule(String apiKey);

    protected abstract void cancel();

    protected abstract String getApiKey();
}

@@ -30,16 +30,56 @@
 ather;%0A%0A
+import android.annotation.SuppressLint;%0A
 import a
@@ -102,16 +102,16 @@
 ontext;%0A
-
 import a
@@ -388,32 +388,76 @@
 n.WorkerThread;%0A
+import androidx.core.content.ContextCompat;%0A
 import androidx.
@@ -662,16 +662,104 @@
 uture;%0A%0A
+import java.lang.reflect.Constructor;%0Aimport java.util.HashSet;%0Aimport java.util.UUID;%0A%0A
 public a
@@ -1333,32 +1333,32 @@
 l%0A    @UiThread%0A
-
     @Override%0A  
@@ -1347,32 +1347,67 @@
 d%0A    @Override%0A
+    @SuppressLint(%22RestrictedApi%22)%0A
     public Liste
@@ -4514,39 +4514,120 @@
     
-// TODO: Pass params into 
+Constructor%3C? extends WeatherService%3E constructor = clazz.getConstructor(Context.class, WorkerParameters.
 class
+);
 %0A   
@@ -4638,16 +4638,47 @@
         
+ @SuppressLint(%22RestrictedApi%22)
  Weather
@@ -4696,20 +4696,26 @@
 vice = c
-lazz
+onstructor
 .newInst
@@ -4719,16 +4719,407 @@
 nstance(
+context, new WorkerParameters(%0A                        UUID.randomUUID(),%0A                        Data.EMPTY,%0A                        new HashSet%3C%3E(),%0A                        null,%0A                        0,%0A                        ContextCompat.getMainExecutor(context),%0A                        null,%0A                        null,%0A                        null,%0A                        null)
 );%0A%0A    

Fix runImmediately