package app.fedilab.fedilabtube.helper;
/* Copyright 2020 Thomas Schneider
 *
 * This file is a part of TubeLab
 *
 * 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.
 *
 * TubeLab 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 TubeLab; if not,
 * see <http://www.gnu.org/licenses>. */

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.WindowManager;
import android.webkit.CookieManager;
import android.webkit.URLUtil;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;

import app.fedilab.fedilabtube.BuildConfig;
import app.fedilab.fedilabtube.MainActivity;
import app.fedilab.fedilabtube.R;
import app.fedilab.fedilabtube.WebviewActivity;
import app.fedilab.fedilabtube.client.data.AccountData.Account;
import app.fedilab.fedilabtube.client.entities.File;
import app.fedilab.fedilabtube.sqlite.AccountDAO;
import app.fedilab.fedilabtube.sqlite.Sqlite;
import app.fedilab.fedilabtube.webview.CustomWebview;
import app.fedilab.fedilabtube.webview.ProxyHelper;
import es.dmoral.toasty.Toasty;

import static android.content.Context.DOWNLOAD_SERVICE;

public class Helper {

    public static final int RELOAD_MYVIDEOS = 10;
    public static final String SET_VIDEO_MODE = "set_video_mode";
    public static final String SET_QUALITY_MODE = "set_quality_mode";
    public static final int VIDEO_MODE_NORMAL = 0;
    public static final int VIDEO_MODE_STREAMING = 1;
    public static final int VIDEO_MODE_WEBVIEW = 2;
    public static final int QUALITY_HIGH = 0;
    public static final int QUALITY_MEDIUM = 1;
    public static final int QUALITY_LOW = 2;
    public static final int ADD_USER_INTENT = 5;
    public static final String SET_SHARE_DETAILS = "set_share_details";
    public static final int DEFAULT_VIDEO_CACHE_MB = 100;
    @SuppressWarnings({"unused", "RedundantSuppression"})
    public static final String TAG = "mastodon_etalab";
    public static final String ID = "id";
    public static final String CLIENT_ID = "client_id";
    public static final String CLIENT_SECRET = "client_secret";
    public static final String WEBSITE_VALUE = "https://fedilab.app";
    public static final String CLIENT_NAME_VALUE = "TubeLab";
    public static final String OAUTH_SCOPES_PEERTUBE = "openid profile";
    public static final String PREF_KEY_OAUTH_TOKEN = "oauth_token";
    public static final Pattern redirectPattern = Pattern.compile("externalAuthToken=(\\w+)&username=([\\w.-]+)");
    public static final String SET_VIDEO_CACHE = "set_video_cache";
    //Proxy
    public static final String SET_PROXY_ENABLED = "set_proxy_enabled";
    public static final String SET_PROXY_HOST = "set_proxy_host";
    public static final String SET_PROXY_PORT = "set_proxy_port";
    public static final String INTENT_ACTION = "intent_action";
    public static final String PREF_KEY_ID = "userID";
    public static final String PREF_KEY_NAME = "my_user_name";
    public static final String PREF_INSTANCE = "instance";
    public static final int EXTERNAL_STORAGE_REQUEST_CODE = 84;
    public static final String SET_VIDEOS_PER_PAGE = "set_videos_per_page";
    public static final String VIDEO_ID = "video_id_update";
    public static final String APP_PREFS = "app_prefs";

    public static final int VIDEOS_PER_PAGE = 40;
    public static final String INTENT_ADD_UPLOADED_MEDIA = "intent_add_uploaded_media";
    public static final String RECEIVE_ACTION = "receive_action";
    public static final String SET_UNFOLLOW_VALIDATION = "set_unfollow_validation";
    //List of available academies


    public static String[] openid = {
            "ac-normandie.fr",
            "education.fr",
            "education.gouv.fr"
            //TODO: remove this one used for tests
            // "ac-orleans-tours.fr"
    };

    public static String[] academies = {
            "ac-aix-marseille.fr",
            "ac-amiens.fr",
            "ac-besancon.fr",
            "ac-bordeaux.fr",
            "clermont-ferrand.fr",
            "ac-corse.fr",
            "ac-creteil.fr",
            "ac-dijon.fr",
            "ac-grenoble.fr",
            "education.fr",
            "ac-lille.fr",
            "ac-limoges.fr",
            "ac-lyon.fr",
            "ac-mayotte.fr",
            "ac-montpellier.fr",
            "ac-nancy.fr",
            "ac-nantes.fr",
            "ac-normandie.fr",
            "ac-orleans-tours.fr",
            "ac-paris.fr",
            "ac-poitiers.fr",
            "outremer.fr",
            "ac-rennes.fr",
            "ac-strasbourg.fr",
            "ac-toulouse.fr",
            "ac-versailles.fr"
    };


    public static String[] valideEmails = {
            "ac-aix-marseille.fr",
            "ac-amiens.fr",
            "ac-besancon.fr",
            "ac-bordeaux.fr",
            "clermont-ferrand.fr",
            "ac-corse.fr",
            "ac-creteil.fr",
            "ac-dijon.fr",
            "ac-grenoble.fr",
            "education.fr",
            "ac-guadeloupe.fr",
            "ac-guyane.fr",
            "ac-reunion.fr",
            "ac-lille.fr",
            "ac-limoges.fr",
            "ac-lyon.fr",
            "ac-martinique.fr",
            "ac-mayotte.fr",
            "ac-montpellier.fr",
            "ac-nancy.fr",
            "ac-nantes.fr",
            "ac-normandie.fr",
            "ac-orleans-tours.fr",
            "ac-paris.fr",
            "ac-poitiers.fr",
            "ac-rennes.fr",
            "ac-spm.fr",
            "ac-strasbourg.fr",
            "ac-toulouse.fr",
            "ac-versailles.fr",
            "ac-wf.wf",
            "monvr.pf",
            "ac-noumea.nc",
            "education.gouv.fr",
            "igesr.gouv.fr"
    };

    /**
     * Returns the peertube URL depending of the academic domain name
     *
     * @param acad String academic domain name
     * @return String the peertube URL
     */
    public static String getPeertubeUrl(String acad) {

        if (BuildConfig.full_instances) {
            return acad;
        }
        if (acad.compareTo("education.gouv.fr") == 0 || acad.compareTo("igesr.gouv.fr") == 0) {
            acad = "education.fr";
        } else if (acad.compareTo("ac-nancy-metz.fr") == 0) {
            acad = "ac-nancy.fr";
        } else if (acad.compareTo("clermont.fr") == 0) {
            acad = "clermont-ferrand.fr";
        } else if (acad.compareTo("ac-wf.wf") == 0 || acad.compareTo("ac-mayotte.fr") == 0 || acad.compareTo("ac-noumea.nc") == 0
                || acad.compareTo("ac-guadeloupe.fr") == 0 || acad.compareTo("monvr.pf") == 0 || acad.compareTo("ac-reunion.fr") == 0 ||
                acad.compareTo("ac-martinique.fr") == 0 || acad.compareTo("ac-guyane.fr") == 0
        ) {
            acad = "outremer.fr";
        }
        if (!acad.contains("ac-lyon.fr")) {
            //TODO: remove hack for test with openid
            /*if( acad.contains("orleans-tours.fr")) {
                return "tube-normandie.beta.education.fr";
            }*/
            return "tube-" + acad.replaceAll("ac-|\\.fr", "") + ".beta.education.fr";
        } else {
            return "tube.ac-lyon.fr";
        }
    }


    /**
     * Returns the instance of the authenticated user
     *
     * @param context Context
     * @return String domain instance
     */
    public static String getLiveInstance(Context context) {
        final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        String acad;
        if (BuildConfig.full_instances) {
            return sharedpreferences.getString(Helper.PREF_INSTANCE, "peertube.social");
        } else {
            acad = sharedpreferences.getString(Helper.PREF_INSTANCE, "tube.ac-lyon.fr");
            if (acad == null) {
                acad = "tube.ac-lyon.fr";
            }
            if (acad.startsWith("tube-")) {
                return acad;
            } else {
                return getPeertubeUrl(acad);
            }
        }
    }

    /**
     * Convert a date in String -> format yyyy-MM-dd HH:mm:ss
     *
     * @param date Date
     * @return String
     */
    public static String dateToString(Date date) {
        if (date == null)
            return null;
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
        return dateFormat.format(date);
    }

    /**
     * Convert String date from db to Date Object
     *
     * @param stringDate date to convert
     * @return Date
     */
    public static Date stringToDate(Context context, String stringDate) {
        if (stringDate == null)
            return null;
        Locale userLocale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            userLocale = context.getResources().getConfiguration().getLocales().get(0);
        } else {
            userLocale = context.getResources().getConfiguration().locale;
        }
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", userLocale);
        Date date = null;
        try {
            date = dateFormat.parse(stringDate);
        } catch (java.text.ParseException ignored) {

        }
        return date;
    }


    public static String secondsToString(int pTime) {

        int hour = pTime / 3600;
        int min = (pTime - (hour * 3600)) / 60;
        int sec = pTime - (hour * 3600) - (min * 60);
        String strHour = "0", strMin = "0", strSec;

        if (hour > 0)
            strHour = String.format(Locale.getDefault(), "%02d", hour);
        if (min > 0)
            strMin = String.format(Locale.getDefault(), "%02d", min);
        strSec = String.format(Locale.getDefault(), "%02d", sec);
        if (hour > 0)
            return String.format(Locale.getDefault(), "%s:%s:%s", strHour, strMin, strSec);
        else
            return String.format(Locale.getDefault(), "%s:%s", strMin, strSec);
    }

    /***
     * Returns a String depending of the date
     * @param context Context
     * @param dateToot Date
     * @return String
     */
    public static String dateDiff(Context context, Date dateToot) {
        Date now = new Date();
        long diff = now.getTime() - dateToot.getTime();
        long seconds = diff / 1000;
        long minutes = seconds / 60;
        long hours = minutes / 60;
        long days = hours / 24;
        long months = days / 30;
        long years = days / 365;

        String format = DateFormat.getDateInstance(DateFormat.LONG).format(dateToot);
        if (years > 0) {
            return format;
        } else if (months > 0 || days > 7) {
            //Removes the year depending of the locale from DateFormat.SHORT format
            try {
                SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.LONG, Locale.getDefault());
                df.applyPattern(df.toPattern().replaceAll("[^\\p{Alpha}]*y+[^\\p{Alpha}]*", ""));
                return df.format(dateToot);
            } catch (Exception e) {
                return format;
            }
        } else if (days > 0)
            return context.getString(R.string.date_day, days);
        else if (hours > 0)
            return context.getResources().getString(R.string.date_hours, (int) hours);
        else if (minutes > 0)
            return context.getResources().getString(R.string.date_minutes, (int) minutes);
        else {
            if (seconds < 0)
                seconds = 0;
            return context.getResources().getString(R.string.date_seconds, (int) seconds);
        }
    }


    public static String withSuffix(long count) {
        if (count < 1000) return "" + count;
        int exp = (int) (Math.log(count) / Math.log(1000));
        Locale locale = null;
        try {
            locale = Locale.getDefault();
        } catch (Exception ignored) {
        }
        if (locale != null)
            return String.format(locale, "%.1f %c",
                    count / Math.pow(1000, exp),
                    "kMGTPE".charAt(exp - 1));
        else
            return String.format(Locale.getDefault(), "%.1f %c",
                    count / Math.pow(1000, exp),
                    "kMGTPE".charAt(exp - 1));
    }


    public static void loadGiF(final Context context, String url, final ImageView imageView) {
        loadGiF(context, url, imageView, 10);
    }

    public static void loadGiF(final Context context, String url, final ImageView imageView, int round) {
        if (url == null || url.trim().toLowerCase().compareTo("null") == 0) {
            Glide.with(imageView.getContext())
                    .asDrawable()
                    .load(R.drawable.missing_peertube)
                    .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
                    .into(imageView);
            return;
        }
        if (url.startsWith("/")) {
            url = Helper.getLiveInstance(context) + url;
        }
        if (!url.startsWith("http")) {
            url = "https://" + url;
        }
        try {
            Glide.with(imageView.getContext())
                    .load(url)
                    .thumbnail(0.1f)
                    .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(round)))
                    .into(imageView);
        } catch (Exception e) {
            try {
                Glide.with(imageView.getContext())
                        .asDrawable()
                        .load(R.drawable.missing_peertube)
                        .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(round)))
                        .into(imageView);

            } catch (Exception ignored) {
            }
        }
    }


    @SuppressLint("SetJavaScriptEnabled")
    public static CustomWebview initializeWebview(Activity activity, int webviewId, View rootView) {

        CustomWebview webView;
        if (rootView == null) {
            webView = activity.findViewById(webviewId);
        } else {
            webView = rootView.findViewById(webviewId);
        }
        final SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);

        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setUseWideViewPort(true);
        webView.getSettings().setLoadWithOverviewMode(true);
        webView.getSettings().setSupportZoom(true);
        webView.getSettings().setDisplayZoomControls(false);
        webView.getSettings().setBuiltInZoomControls(true);
        webView.getSettings().setAllowContentAccess(true);
        webView.getSettings().setLoadsImagesAutomatically(true);
        webView.getSettings().setSupportMultipleWindows(false);
        webView.getSettings().setMediaPlaybackRequiresUserGesture(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            CookieManager cookieManager = CookieManager.getInstance();
            cookieManager.setAcceptThirdPartyCookies(webView, false);
        }
        webView.setBackgroundColor(Color.TRANSPARENT);
        webView.getSettings().setAppCacheEnabled(true);
        webView.getSettings().setDatabaseEnabled(true);
        webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public Bitmap getDefaultVideoPoster() {
                return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
            }
        });
        boolean proxyEnabled = sharedpreferences.getBoolean(Helper.SET_PROXY_ENABLED, false);
        if (proxyEnabled) {
            String host = sharedpreferences.getString(Helper.SET_PROXY_HOST, "127.0.0.1");
            int port = sharedpreferences.getInt(Helper.SET_PROXY_PORT, 8118);
            ProxyHelper.setProxy(activity, webView, host, port, WebviewActivity.class.getName());
        }

        return webView;
    }


    /**
     * Manage downloads with URLs
     *
     * @param context Context
     * @param url     String download url
     */
    public static void manageDownloads(final Context context, final String url) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
        final DownloadManager.Request request;
        try {
            request = new DownloadManager.Request(Uri.parse(url.trim()));
        } catch (Exception e) {
            Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
            return;
        }
        final String fileName = URLUtil.guessFileName(url, null, null);
        builder.setMessage(context.getResources().getString(R.string.download_file, fileName));
        builder.setCancelable(false)
                .setPositiveButton(context.getString(R.string.yes), (dialog, id) -> {
                    request.allowScanningByMediaScanner();
                    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
                    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                    DownloadManager dm = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);
                    assert dm != null;
                    dm.enqueue(request);
                    dialog.dismiss();
                })
                .setNegativeButton(context.getString(R.string.cancel), (dialog, id) -> dialog.cancel());
        AlertDialog alert = builder.create();
        if (alert.getWindow() != null)
            alert.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
        alert.show();
    }


    /**
     * Log out the authenticated user by removing its token
     *
     * @param activity Activity
     * @param account  Account
     */
    public static void logoutCurrentUser(Activity activity, Account account) {
        SharedPreferences sharedpreferences = activity.getSharedPreferences(APP_PREFS, Context.MODE_PRIVATE);
        //Current user
        SQLiteDatabase db = Sqlite.getInstance(activity.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();

        if (account != null) {
            new AccountDAO(activity, db).removeUser(account);
        }
        Account newAccount = new AccountDAO(activity, db).getLastUsedAccount();
        SharedPreferences.Editor editor = sharedpreferences.edit();
        if (newAccount == null) {
            editor.putString(PREF_KEY_OAUTH_TOKEN, null);
            editor.putString(CLIENT_ID, null);
            editor.putString(CLIENT_SECRET, null);
            editor.putString(PREF_KEY_ID, null);
            editor.putString(PREF_INSTANCE, null);
            editor.putString(ID, null);
            editor.apply();
            Intent loginActivity = new Intent(activity, MainActivity.class);
            activity.startActivity(loginActivity);
            activity.finish();
        } else {
            editor.putString(PREF_KEY_OAUTH_TOKEN, newAccount.getToken());
            editor.putString(PREF_KEY_ID, newAccount.getId());
            editor.putString(PREF_INSTANCE, newAccount.getHost().trim());
            editor.commit();
            Intent changeAccount = new Intent(activity, MainActivity.class);
            changeAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            activity.startActivity(changeAccount);
        }
    }


    public static int getAttColor(Context context, int attColor) {
        TypedValue typedValue = new TypedValue();
        context.getTheme().resolveAttribute(attColor, typedValue, true);
        return ContextCompat.getColor(context, typedValue.resourceId);
    }


    /**
     * Returns boolean depending if the user is authenticated
     *
     * @param context Context
     * @return boolean
     */
    public static boolean isLoggedIn(Context context) {
        SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        String prefKeyOauthTokenT = sharedpreferences.getString(PREF_KEY_OAUTH_TOKEN, null);
        return (prefKeyOauthTokenT != null);
    }


    /**
     * Converts dp to pixel
     *
     * @param dp      float - the value in dp to convert
     * @param context Context
     * @return float - the converted value in pixel
     */
    public static float convertDpToPixel(float dp, Context context) {
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        return dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
    }


    public static boolean isTablet(Context context) {
        return context.getResources().getBoolean(R.bool.is_tablet);
    }

    public static int getColorAccent() {
        return BuildConfig.full_instances ? R.color.colorAccent_full : R.color.colorAccent;
    }


    public static boolean isOwner(Context context, Account account) {
        SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        String userName = sharedpreferences.getString(Helper.PREF_KEY_NAME, "");
        String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, "");
        if (instance != null && userName != null) {
            return account.getUsername().compareTo(userName) == 0 && account.getHost().compareTo(instance) == 0;
        } else {
            return false;
        }
    }

    /***
     * Return a File depending of the requested quality
     * @param context Context
     * @param files List<File>
     * @return File
     */
    public static File defaultFile(Context context, List<File> files) {
        SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        int video_quality = sharedpreferences.getInt(Helper.SET_QUALITY_MODE, Helper.QUALITY_HIGH);
        if (video_quality == QUALITY_HIGH) {
            return files.get(0);
        } else if (video_quality == QUALITY_LOW) {
            return files.get(files.size() - 1);
        } else {
            if (files.size() < 3) {
                return files.get(files.size() - 1);
            } else {
                int middle = files.size() / 2 - 1;
                return files.get(middle);
            }
        }
    }
}
