/*
 * DSBDirect
 * Copyright (C) 2019 Fynn Godau
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * This software is not affiliated with heinekingmedia GmbH, the
 * developer of the DSB platform.
 */

package godau.fynn.dsbdirect;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.*;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.view.ViewGroup;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import godau.fynn.dsbdirect.reader.Roentgen;
import godau.fynn.dsbdirect.reader.Untis;
import org.json.JSONException;
import org.json.JSONObject;

import java.text.SimpleDateFormat;
import java.util.*;

import static android.content.Context.MODE_PRIVATE;

public class Utility {

    public static final String NULL = "automatic";
    public static final String UNTIS = "untis";
    public static final String ROENTGEN = "roentgen";

    public static final String DATE_FORMAT = "EEEE, d.M.yyyy";

    // For Gadgetbridge compatibility: Gadgetbridge seems to only push content text, not big text text
    public static final String SUPER_SECRET_SETTING_TEXT_AS_CONTENT_TEXT = "textAsContentText";

    // For debugging: display messages even if the exact same content has already been displayed
    public static final String SUPER_SECRET_SETTING_NOTIFY_ABOUT_NOTHING_NEW = "ignoreAlreadyDisplayed";

    // For debugging: also poll on weekends
    public static final String SUPER_SECRED_SETTING_POLL_ON_WEEKENDS = "ignoreWeekend";

    private Context mContext;

    public Utility(Context context) {
        this.mContext = context;
    }

    public static String formatDate(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault());
        return simpleDateFormat.format(date);
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    public @Nullable Reader getReader(String html, String id) {
        switch (getParserIdentifier(id)) {
            case UNTIS:
                return new Untis(html, mContext);
            case ROENTGEN:
                return new Roentgen(html, mContext);
        }

        // There is no reader if we got here
        return null;
    }

    public String getParserIdentifier(String id) {
        String userSetting = getParserUserSetting();

        if (!userSetting.equals("automatic")) {
            // userSetting overwrites id
            id = userSetting;
        }

        switch (id) {
            case "162161": // Alexander-von-Humboldt-Gymnasium Schweinfurt
            case UNTIS:
                return UNTIS;
            case ROENTGEN:
            case "219261": // Röntgen-Gymnasium Würzburg
                return ROENTGEN;
            default:
                return NULL;
        }
    }

    public String getParserUserSetting() {
        return getSharedPreferences().getString("parser", "automatic");
    }

    public static Date parseLastUpdatedDate(String input) {
        String[] dateParts = input.split(" ");
        String[] dateDigits = dateParts[0].split("\\.");
        String[] timeDigits = dateParts[1].split(":");
        return new Date(Integer.parseInt(dateDigits[2]) - 1900 /* years start at 1900 */, Integer.parseInt(dateDigits[1]) - 1 /* months start with 0 */,
                Integer.parseInt(dateDigits[0]), Integer.parseInt(timeDigits[0]), Integer.parseInt(timeDigits[1])); // because SimpleDateTime did deliver good results, again
    }

    public void checkForUpdate(final boolean verbose) {
        // Thanks, https://stackoverflow.com/a/5069354
        final Activity activity = (Activity) mContext;
        final View rootView = ((ViewGroup) activity
                .findViewById(android.R.id.content)).getChildAt(0);

        // Request current version metadata
        StringRequest stringRequest = new StringRequest(Request.Method.GET,
                mContext.getString(R.string.uri_versioncheck),
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        int currentVersion = BuildConfig.VERSION_CODE;

                        // Let's parse
                        try {
                            JSONObject jsonObject = new JSONObject(response);

                            int latestVersion = jsonObject.getInt("versionCode");
                            final String versionName = jsonObject.getString("versionName");
                            final String whatsNew = jsonObject.getString("whatsNew");
                            final String uri = jsonObject.getString("url");

                            if (latestVersion > currentVersion) {
                                new AlertDialog.Builder(mContext)
                                        .setPositiveButton(R.string.download, new DialogInterface.OnClickListener() {
                                            @Override
                                            public void onClick(DialogInterface dialog, int which) {
                                                // Launch browser to download update

                                                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
                                                mContext.startActivity(intent);

                                            }
                                        })
                                        .setNegativeButton(R.string.dismiss, null)
                                        .setTitle(mContext.getString(R.string.update_popup_title, versionName))
                                        .setMessage(whatsNew)
                                        .show();
                            } else {
                                if (verbose) {
                                    Snackbar
                                            .make(rootView, R.string.up_to_date, Snackbar.LENGTH_SHORT)
                                            .show();
                                }
                            }

                        } catch (JSONException e) {
                            Snackbar
                                    .make(rootView, R.string.network_versioncheck_failed, Snackbar.LENGTH_SHORT)
                                    .show();

                            e.printStackTrace();
                        }

                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Snackbar
                        .make(rootView, R.string.network_versioncheck_failed, Snackbar.LENGTH_SHORT)
                        .show();
            }
        });

        Volley.newRequestQueue(mContext).add(stringRequest);
    }

    public SharedPreferences getSharedPreferences() {
        return mContext.getSharedPreferences("default", MODE_PRIVATE);
    }

    public String getUpdateCheckFrequency() {
        return getSharedPreferences().getString("update_check_frequency",
                mContext.getString(R.string.update_check_frequency_default));
    }

    public boolean shouldCheckForUpdates() {
        switch (getUpdateCheckFrequency()) {
            case "always":
                return true;
            case "sometimes":
                return new Random().nextInt(10) == 0;
            case "never":
            default:
                return false;
        }
    }

    public void resetToken() {
        // Reset token
        getSharedPreferences().edit().putString("token", null).apply();
    }

    public static void sendMessage(Handler handler, String key, String value) {
        if (handler != null) {
            Bundle bundle = new Bundle();
            bundle.putString(key, value);

            Message message = new Message();
            message.setData(bundle);

            handler.sendMessage(message);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void schedulePolling() {
        // Check whether polling is enabled
        SharedPreferences sharedPreferences = getSharedPreferences();
        if (sharedPreferences.getBoolean("parse", true) && sharedPreferences.getBoolean("poll", false)) {
            // Polling is enabled

            ComponentName serviceComponent = new ComponentName(mContext, PollingService.class);
            JobInfo.Builder builder = new JobInfo.Builder(0, serviceComponent);
            builder.setMinimumLatency(PollingService.getNextPollingTimeDelay(sharedPreferences)); // Poll after at least this time
            builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); // Only execute once network is connected
            JobScheduler jobScheduler = (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
            jobScheduler.schedule(builder.build());
        } else {
            // Cancel potential job
            JobScheduler jobScheduler = (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
            jobScheduler.cancel(0);
        }
    }

    public void saveAuthToken(String authToken) {
        final SharedPreferences sharedPreferences = getSharedPreferences();
        sharedPreferences.edit()
                .putString("token", authToken)
                .apply();
    }

    public static String smartConcatenate(String[] strings, String comma) {
        StringBuilder result = new StringBuilder();

        ArrayList<String> list = new ArrayList<>(Arrays.asList(strings));
        for (int i = list.size() - 1; i >= 0; i--) {
            if (list.get(i).isEmpty()) {
                list.remove(i);
            }
        }

        for (int i = 0; i < list.size(); i++) {
            String string = list.get(i);
            result.append(string);

            if (i != list.size() - 1) {
                // This is not the last iteration yet
                result.append(comma);
            }

        }

        return String.valueOf(result);
    }

    public static Calendar zeroOClock(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar;
    }

    public static String smartConcatenate(int[] integers, String comma) {
        String[] strings = new String[integers.length];
        for (int i = 0; i < integers.length; i++) {
            strings[i] = String.valueOf(integers[i]);
        }
        return smartConcatenate(strings, comma);
    }
}
