package app.andrey_voroshkov.chorus_laptimer;

import android.content.SharedPreferences;
import android.text.TextUtils;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * Created by Andrey_Voroshkov on 3/26/2017.
 */

public class AppPreferences {
    public static final String STRING_ITEMS_DELIMITER = "%%"; //should not contain regex special characters

    public static final String SKIP_FIRST_LAP = "skip_first_lap";
    public static final String SPEAK_LAP_TIMES = "speak_lap_times";
    public static final String SPEAK_MESSAGES = "speak_messages";
    public static final String SPEAK_ENGLISH_ONLY = "speak_english_only";
    public static final String LAPS_TO_GO = "laps_to_go";
    public static final String MIN_LAP_TIME = "min_lap_time";
    public static final String PREPARATION_TIME = "preparation_time";
    public static final String ENABLE_DEVICE_SOUNDS = "enable_device_sounds";
    public static final String USE_EXPERIMENTAL_FEATURES = "use_experimental_features";
    public static final String DEVICE_BANDS = "device_bands";
    public static final String DEVICE_CHANNELS = "device_channels";
    public static final String DEVICE_PILOTS = "device_pilots";
    public static final String DEVICE_ENABLED = "device_enabled";
    public static final String DEVICE_THRESHOLDS = "device_thresholds";
    public static final String LIPO_MONITOR_ENABLED = "lipo_mon_enabled";
    public static final String LIPO_ADJUSTMENT_CONST = "lipo_adjust_const";
    public static final String RANDOM_START_TIME = "random_start_time";


    public String[] mBands;
    public String[] mChannels;
    public String[] mPilots;
    public String[] mDeviceEnabledStatuses;
    public String[] mDeviceThresholds;

    private static AppPreferences instance = new AppPreferences();

    public static AppPreferences getInstance() {
        return instance;
    }

    private AppPreferences() {
        mBands = getArrayFromStringPreference(DEVICE_BANDS);
        mChannels = getArrayFromStringPreference(DEVICE_CHANNELS);
        mPilots = getArrayFromStringPreference(DEVICE_PILOTS);
        mDeviceEnabledStatuses = getArrayFromStringPreference(DEVICE_ENABLED);
        mDeviceThresholds = getArrayFromStringPreference(DEVICE_THRESHOLDS);
    }

    private static String[] getArrayFromStringPreference(String prefName) {
        String value = AppState.getInstance().preferences.getString(prefName, "");
        if (value.equals("")) {
            return new String[0];
        }
        return TextUtils.split(value, STRING_ITEMS_DELIMITER);
    }

    // this function is needed to preserve preferencess of devices that are not currently available in chorus
    // e.g. prefs were saved for 3 devices, then used with 1 device. When you're back with 3 - you'll have old prefs restored
    private static void AppendExtraItemsFromSavedArrayPreference(ArrayList<String> list, String[] savedPreferenceArr) {
        int currentCount = list.size();
        int savedCount = savedPreferenceArr.length;
        if (currentCount < savedCount) {
            list.addAll(Arrays.asList(savedPreferenceArr).subList(currentCount, savedCount));
        }
    }

    public static void save(String preferenceName) {
        AppState app = AppState.getInstance();
        // don't save anything to prefs before device initialization is done
        if (!app.isDevicesInitializationOver()) return;

        SharedPreferences.Editor editor = app.preferences.edit();
        switch (preferenceName) {
            case SKIP_FIRST_LAP:
                editor.putBoolean(SKIP_FIRST_LAP, app.shouldSkipFirstLap);
                break;
            case SPEAK_LAP_TIMES:
                editor.putBoolean(SPEAK_LAP_TIMES, app.shouldSpeakLapTimes);
                break;
            case SPEAK_MESSAGES:
                editor.putBoolean(SPEAK_MESSAGES, app.shouldSpeakMessages);
                break;
            case SPEAK_ENGLISH_ONLY:
                editor.putBoolean(SPEAK_ENGLISH_ONLY, app.shouldSpeakEnglishOnly);
                break;
            case LAPS_TO_GO:
                if (app.raceState == null) break;
                editor.putInt(LAPS_TO_GO, app.raceState.lapsToGo);
                break;
            case MIN_LAP_TIME:
                if (app.raceState == null) break;
                editor.putInt(MIN_LAP_TIME, app.raceState.minLapTime);
                break;
            case PREPARATION_TIME:
                editor.putInt(PREPARATION_TIME, app.timeToPrepareForRace);
                break;
            case RANDOM_START_TIME:
                editor.putInt(RANDOM_START_TIME, app.randomStartTime);
                break;
            case ENABLE_DEVICE_SOUNDS:
                editor.putBoolean(ENABLE_DEVICE_SOUNDS, app.isDeviceSoundEnabled);
                break;
            case USE_EXPERIMENTAL_FEATURES:
                editor.putBoolean(USE_EXPERIMENTAL_FEATURES, app.shouldUseExperimentalFeatures);
            case DEVICE_BANDS:
                if (app.deviceStates == null) break;
                ArrayList<String> bandsList = new ArrayList<>();
                for(int i = 0; i < app.deviceStates.size(); i++) {
                    bandsList.add(Integer.toString(app.deviceStates.get(i).band));
                }
                AppendExtraItemsFromSavedArrayPreference(bandsList, AppPreferences.getInstance().mBands);
                String bands = TextUtils.join(STRING_ITEMS_DELIMITER, bandsList);
                editor.putString(DEVICE_BANDS, bands);
                break;
            case DEVICE_CHANNELS:
                if (app.deviceStates == null) break;
                ArrayList<String> channelsList = new ArrayList<>();
                for(int i = 0; i < app.deviceStates.size(); i++) {
                    channelsList.add(Integer.toString(app.deviceStates.get(i).channel));
                }
                AppendExtraItemsFromSavedArrayPreference(channelsList, AppPreferences.getInstance().mChannels);
                String channels = TextUtils.join(STRING_ITEMS_DELIMITER, channelsList);
                editor.putString(DEVICE_CHANNELS, channels);
                break;
            case DEVICE_PILOTS:
                if (app.deviceStates == null) break;
                ArrayList<String> pilotsList = new ArrayList<>();
                for(int i = 0; i < app.deviceStates.size(); i++) {
                    pilotsList.add(app.deviceStates.get(i).pilotName);
                }
                AppendExtraItemsFromSavedArrayPreference(pilotsList, AppPreferences.getInstance().mPilots);
                String pilots = TextUtils.join(STRING_ITEMS_DELIMITER, pilotsList);
                editor.putString(DEVICE_PILOTS, pilots);
                break;
            case DEVICE_ENABLED:
                if (app.deviceStates == null) break;
                ArrayList<String> statusesList = new ArrayList<>();
                for(int i = 0; i < app.deviceStates.size(); i++) {
                    statusesList.add(Boolean.toString(app.deviceStates.get(i).isEnabled));
                }
                AppendExtraItemsFromSavedArrayPreference(statusesList, AppPreferences.getInstance().mDeviceEnabledStatuses);
                String statuses = TextUtils.join(STRING_ITEMS_DELIMITER, statusesList);
                editor.putString(DEVICE_ENABLED, statuses);
                break;
            case DEVICE_THRESHOLDS:
                if (app.deviceStates == null) break;
                ArrayList<String> thresholdsList = new ArrayList<>();
                for(int i = 0; i < app.deviceStates.size(); i++) {
                    thresholdsList.add(Integer.toString(app.deviceStates.get(i).threshold));
                }
                AppendExtraItemsFromSavedArrayPreference(thresholdsList, AppPreferences.getInstance().mDeviceThresholds);
                String thresholds = TextUtils.join(STRING_ITEMS_DELIMITER, thresholdsList);
                editor.putString(DEVICE_THRESHOLDS, thresholds);
                break;

            case LIPO_MONITOR_ENABLED:
                editor.putBoolean(LIPO_MONITOR_ENABLED, app.isLiPoMonitorEnabled);
                break;
            case LIPO_ADJUSTMENT_CONST:
                editor.putInt(LIPO_ADJUSTMENT_CONST, app.batteryAdjustmentConst);
                break;
        }
        editor.apply();
    }

    public static void applyInAppPreferences() {
        AppState app = AppState.getInstance();

        app.changeShouldSpeakLapTimes(app.preferences.getBoolean(SPEAK_LAP_TIMES, true));
        app.changeShouldSpeakMessages(app.preferences.getBoolean(SPEAK_MESSAGES, true));
        app.changeShouldSpeakEnglishOnly(app.preferences.getBoolean(SPEAK_ENGLISH_ONLY, false));
        app.changeTimeToPrepareForRace(app.preferences.getInt(PREPARATION_TIME, 5));
        app.changeRandomStartTime(app.preferences.getInt(RANDOM_START_TIME, 0));
        app.changeRaceLaps(app.preferences.getInt(LAPS_TO_GO, 5));
        app.changeAdjustmentConst(app.preferences.getInt(LIPO_ADJUSTMENT_CONST, 0));
        app.changeEnableLiPoMonitor(app.preferences.getBoolean(LIPO_MONITOR_ENABLED, true));

        if (app.deviceStates != null) {
            String pilots = app.preferences.getString(DEVICE_PILOTS, "");
            if (!pilots.equals("")) {
                String[] pilotsArray = TextUtils.split(pilots, STRING_ITEMS_DELIMITER);
                int pilotsCount = pilotsArray.length;
                for(int i = 0; i < app.deviceStates.size(); i++) {
                    if (i < pilotsCount) {
                        app.changeDevicePilot(i, pilotsArray[i]);
                    }
                }
                app.updatePilotNamesInEdits(); //because pilot names in UI are not updated via changeDevicePilot()
            }
        }


    }

    public static void applyDeviceDependentPreferences() {
        AppState app = AppState.getInstance();

        boolean prefSkipFirstLap = app.preferences.getBoolean(SKIP_FIRST_LAP, true);
        if (app.shouldSkipFirstLap != prefSkipFirstLap) {
            app.changeSkipFirstLap(prefSkipFirstLap);
            app.sendBtCommand("R*1" + (prefSkipFirstLap ? "0" : "1"));
        }

        boolean prefEnableDeviceSounds = app.preferences.getBoolean(ENABLE_DEVICE_SOUNDS, true);
        if (app.isDeviceSoundEnabled != prefEnableDeviceSounds) {
            app.changeDeviceSoundState(prefEnableDeviceSounds);
            app.sendBtCommand("R*S" + (prefEnableDeviceSounds ? "1" : "0"));
        }

        boolean prefUseExperimentalFeatures = app.preferences.getBoolean(USE_EXPERIMENTAL_FEATURES, false);
        if (app.shouldUseExperimentalFeatures != prefUseExperimentalFeatures) {
            app.changeExperimentalFeatureState(prefUseExperimentalFeatures);
            app.sendBtCommand("R*E" + (prefUseExperimentalFeatures? "1" : "0"));
        }

        if (app.raceState != null) {
            int prefMLT = app.preferences.getInt(MIN_LAP_TIME, 3);
            app.changeRaceMinLapTime(prefMLT);
            app.sendBtCommand("R*M" + String.format("%02X", prefMLT));
        }

        if (app.deviceStates != null) {
            String bands = app.preferences.getString(DEVICE_BANDS, "");
            if (!bands.equals("")) {
                String[] bandsArray = TextUtils.split(bands, STRING_ITEMS_DELIMITER);
                int bandsCount = bandsArray.length;
                //count backwards to send commands for last devices first to make sure that
                // first devices don't delay propagation of commands to next devices
                for(int i = app.deviceStates.size() - 1; i >= 0 ; i--) {
                    if (i < bandsCount) {
                        int prefBand = Integer.parseInt(bandsArray[i]);
                        app.changeDeviceBand(i, prefBand);
                        app.sendBtCommand("R" +  String.format("%X", i) + "B" + String.format("%X", prefBand));
                    }
                }
            }

            String channels = app.preferences.getString(DEVICE_CHANNELS, "");
            if (!channels.equals("")) {
                String[] channelsArray = TextUtils.split(channels, STRING_ITEMS_DELIMITER);
                int channelsCount = channelsArray.length;
                //count backwards to send commands for last devices first to make sure that
                // first devices don't delay propagation of commands to next devices
                for(int i = app.deviceStates.size() - 1; i >= 0 ; i--) {
                   if (i < channelsCount) {
                        int prefChannel = Integer.parseInt(channelsArray[i]);
                        app.changeDeviceChannel(i, prefChannel);
                        app.sendBtCommand("R" +  String.format("%X", i) + "C" + String.format("%X", prefChannel));
                    }
                }
            }

            String thresholds = app.preferences.getString(DEVICE_THRESHOLDS, "");
            if (!thresholds.equals("")) {
                String[] thresholdsArray = TextUtils.split(thresholds, STRING_ITEMS_DELIMITER);
                int thresholdsCount = thresholdsArray.length;
                //count backwards to send commands for last devices first to make sure that
                // first devices don't delay propagation of commands to next devices
                for(int i = app.deviceStates.size() - 1; i >= 0 ; i--) {
                    if (i < thresholdsCount) {
                        int prefThreshold = Integer.parseInt(thresholdsArray[i]);
                        app.changeDeviceThreshold(i, prefThreshold);
                        app.sendBtCommand("R" +  String.format("%X", i) + "T" + String.format("%04X", prefThreshold));
                    }
                }
            }

            String enabledStatuses = app.preferences.getString(DEVICE_ENABLED, "");
            if (!enabledStatuses.equals("")) {
                String[] enabledStatusesArray = TextUtils.split(enabledStatuses, STRING_ITEMS_DELIMITER);
                int statusesCount = enabledStatusesArray.length;
                for(int i = 0; i < app.deviceStates.size(); i++) {
                    if (i < statusesCount) {
                        boolean prefEnabledStatus = Boolean.parseBoolean(enabledStatusesArray[i]);
                        app.changeDeviceEnabled(i, prefEnabledStatus);
                        app.sendBtCommand("R" +  String.format("%X", i) + "A" + (prefEnabledStatus ? "1" : "0"));
                    }
                }
            }
            else {
                // forcibly enable all devices if prefs are not saved
                for(int i = 0; i < app.deviceStates.size(); i++) {
                    app.changeDeviceEnabled(i, true);
                    app.sendBtCommand("R" +  String.format("%X", i) + "A1");
                }
            }
        }
    }

    public static void applyAll() {
        applyInAppPreferences();
        applyDeviceDependentPreferences();
    }
}