/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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.google.android.car.uxr.sample;

import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_IDLING;
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_MOVING;
import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED;
import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;

import android.app.AlertDialog;
import android.car.Car;
import android.car.content.pm.CarPackageManager;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarDrivingStateManager;
import android.car.drivingstate.CarUxRestrictions;
import android.car.drivingstate.CarUxRestrictionsConfiguration;
import android.car.drivingstate.CarUxRestrictionsConfiguration.DrivingStateRestrictions;
import android.car.drivingstate.CarUxRestrictionsManager;
import android.car.experimental.CarDriverDistractionManager;
import android.car.experimental.DriverDistractionChangeEvent;
import android.car.experimental.ExperimentalCar;
import android.os.Bundle;
import android.util.JsonWriter;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.DialogFragment;

import java.io.CharArrayWriter;
import java.util.List;

/**
 * Sample app that uses components in car support library to demonstrate Car drivingstate UXR
 * status.
 */
public class MainActivity extends AppCompatActivity
        implements ConfigurationDialogFragment.OnConfirmListener {

    public static final String TAG = "UxRDemo";

    private static final String DIALOG_FRAGMENT_TAG = "dialog_fragment_tag";
    private static final String UX_RESTRICTION_MODE_PASSENGER = "passenger";

    private Car mCar;
    private CarDrivingStateManager mCarDrivingStateManager;
    private CarUxRestrictionsManager mCarUxRestrictionsManager;
    private CarPackageManager mCarPackageManager;
    private CarDriverDistractionManager mDistractionManager;

    private CarUxRestrictionsManager.OnUxRestrictionsChangedListener mUxRChangeListener =
            this::updateUxRText;
    private CarDrivingStateManager.CarDrivingStateEventListener mDrvStateChangeListener =
            this::updateDrivingStateText;
    private CarDriverDistractionManager.OnDriverDistractionChangeListener
            mDistractionChangedListener = this::onDriverDistractionChange;

    private TextView mDrvStatus;
    private TextView mDistractionOptStatus;
    private TextView mUxrStatus;
    private TextView mUxrRestrictionParametersMaxStringLength;
    private TextView mUxrRestrictionParametersMaxContentDepth;
    private TextView mUxrRestrictionParametersMaxCumulativeContent;
    private TextView mDistractionStatus;
    private Button mToggleButton;
    private Button mSaveUxrConfigButton;
    private Button mShowStagedConfig;
    private Button mShowProdConfig;
    private Button mTogglePassengerMode;

    private boolean mEnableUxR;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.main_activity);

        mDrvStatus = findViewById(R.id.driving_state);
        mDistractionOptStatus = findViewById(R.id.do_status);
        mUxrStatus = findViewById(R.id.uxr_status);
        mUxrRestrictionParametersMaxStringLength = findViewById(
                R.id.uxr_restriction_parameters_string_length);
        mUxrRestrictionParametersMaxContentDepth = findViewById(
                R.id.uxr_restriction_parameters_content_depth);
        mUxrRestrictionParametersMaxCumulativeContent = findViewById(
                R.id.uxr_restriction_parameters_cumulative_content);
        mDistractionStatus = findViewById(R.id.current_driver_distraction);

        mToggleButton = findViewById(R.id.toggle_status);
        mToggleButton.setOnClickListener(v -> updateToggleUxREnable());

        mSaveUxrConfigButton = findViewById(R.id.save_uxr_config);
        mSaveUxrConfigButton.setOnClickListener(v -> showConfigurationDialog());

        mShowStagedConfig = findViewById(R.id.show_staged_config);
        mShowStagedConfig.setOnClickListener(v -> showStagedUxRestrictionsConfig());
        mShowProdConfig = findViewById(R.id.show_prod_config);
        mShowProdConfig.setOnClickListener(v -> showProdUxRestrictionsConfig());

        mTogglePassengerMode = findViewById(R.id.toggle_passenger_mode);
        mTogglePassengerMode.setOnClickListener(v -> togglePassengerMode());

        // Connect to car service
        mCar = Car.createCar(this);

        mCarDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager(
                Car.CAR_DRIVING_STATE_SERVICE);
        mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(
                Car.CAR_UX_RESTRICTION_SERVICE);
        mCarPackageManager = (CarPackageManager) mCar.getCarManager(
                Car.PACKAGE_SERVICE);
        if (mCarDrivingStateManager != null) {
            mCarDrivingStateManager.registerListener(mDrvStateChangeListener);
            updateDrivingStateText(
                    mCarDrivingStateManager.getCurrentCarDrivingState());
        }
        if (mCarUxRestrictionsManager != null) {
            mCarUxRestrictionsManager.registerListener(mUxRChangeListener);
            updateUxRText(mCarUxRestrictionsManager.getCurrentCarUxRestrictions());
        }

        if (mCar.isFeatureEnabled(
                ExperimentalCar.DRIVER_DISTRACTION_EXPERIMENTAL_FEATURE_SERVICE)) {
            mDistractionManager = (CarDriverDistractionManager) mCar.getCarManager(
                    ExperimentalCar.DRIVER_DISTRACTION_EXPERIMENTAL_FEATURE_SERVICE);
            if (mDistractionManager != null) {
                mDistractionManager.addDriverDistractionChangeListener(mDistractionChangedListener);
            }
        } else {
            mDistractionStatus.setText(
                    getString(R.string.current_driver_distraction, "feature disabled"));
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mCarUxRestrictionsManager != null) {
            mCarUxRestrictionsManager.unregisterListener();
        }
        if (mCarDrivingStateManager != null) {
            mCarDrivingStateManager.unregisterListener();
        }
        if (mDistractionManager != null) {
            mDistractionManager.removeDriverDistractionChangeListener(mDistractionChangedListener);
        }
        if (mCar != null) {
            mCar.disconnect();
        }
    }

    private void updateUxRText(CarUxRestrictions restrictions) {
        if (restrictions.isRequiresDistractionOptimization()) {
            mDistractionOptStatus.setText(getString(R.string.do_reqd));
        } else {
            mDistractionOptStatus.setText(getString(R.string.do_not_reqd));
        }

        mUxrStatus.setText(getString(R.string.active_restrictions,
                Integer.toHexString(restrictions.getActiveRestrictions()),
                Integer.toBinaryString(restrictions.getActiveRestrictions())));

        mUxrRestrictionParametersMaxStringLength.setText(
                getString(R.string.active_restriction_parameters_string_length,
                        restrictions.getMaxRestrictedStringLength()));

        mUxrRestrictionParametersMaxContentDepth.setText(
                getString(R.string.active_restriction_parameters_content_depth,
                        restrictions.getMaxContentDepth()));

        mUxrRestrictionParametersMaxCumulativeContent.setText(
                getString(R.string.active_restriction_parameters_cumulative_content,
                        restrictions.getMaxCumulativeContentItems()));

        mDistractionOptStatus.requestLayout();
        mUxrStatus.requestLayout();
        mUxrRestrictionParametersMaxStringLength.requestLayout();
        mUxrRestrictionParametersMaxContentDepth.requestLayout();
        mUxrRestrictionParametersMaxCumulativeContent.requestLayout();
    }

    private void onDriverDistractionChange(DriverDistractionChangeEvent event) {
        mDistractionStatus.setText(
                getString(R.string.current_driver_distraction, event.getAwarenessPercentage()));
    }

    private void updateToggleUxREnable() {
        if (mCarPackageManager == null) {
            return;
        }
        mCarPackageManager.setEnableActivityBlocking(mEnableUxR);
        if (mEnableUxR) {
            mToggleButton.setText("Disable UX Restrictions");
        } else {
            mToggleButton.setText("Enable UX Restrictions");
        }
        mEnableUxR = !mEnableUxR;
        mToggleButton.requestLayout();

    }

    private void updateDrivingStateText(CarDrivingStateEvent state) {
        if (state == null) {
            return;
        }
        String displayText;
        switch (state.eventValue) {
            case DRIVING_STATE_PARKED:
                displayText = "Parked";
                break;
            case DRIVING_STATE_IDLING:
                displayText = "Idling";
                break;
            case DRIVING_STATE_MOVING:
                displayText = "Moving";
                break;
            default:
                displayText = "Unknown";
        }
        mDrvStatus.setText("Driving State: " + displayText);
        mDrvStatus.requestLayout();
    }

    private void togglePassengerMode() {
        if (mCarUxRestrictionsManager == null) {
            return;
        }

        String mode = mCarUxRestrictionsManager.getRestrictionMode();
        switch (mode) {
            case UX_RESTRICTION_MODE_BASELINE:
                mCarUxRestrictionsManager.setRestrictionMode(UX_RESTRICTION_MODE_PASSENGER);
                mTogglePassengerMode.setText(R.string.disable_passenger_mode);
                break;
            case UX_RESTRICTION_MODE_PASSENGER:
                mCarUxRestrictionsManager.setRestrictionMode(UX_RESTRICTION_MODE_BASELINE);
                mTogglePassengerMode.setText(R.string.enable_passenger_mode);
                break;
            default:
                throw new IllegalStateException("Unrecognized restriction mode " + mode);
        }
    }

    private void showConfigurationDialog() {
        DialogFragment dialogFragment = ConfigurationDialogFragment.newInstance();
        dialogFragment.show(getSupportFragmentManager(), DIALOG_FRAGMENT_TAG);
    }

    @Override
    public void onConfirm(int baseline, int passenger) {

        CarUxRestrictionsConfiguration config = new CarUxRestrictionsConfiguration.Builder()
                .setUxRestrictions(DRIVING_STATE_PARKED, false, 0)
                .setUxRestrictions(DRIVING_STATE_MOVING,
                        new DrivingStateRestrictions()
                                .setDistractionOptimizationRequired(baseline != 0)
                                .setRestrictions(baseline))
                .setUxRestrictions(DRIVING_STATE_MOVING,
                        new DrivingStateRestrictions()
                                .setMode(UX_RESTRICTION_MODE_PASSENGER)
                                .setDistractionOptimizationRequired(passenger != 0)
                                .setRestrictions(passenger))
                .setUxRestrictions(DRIVING_STATE_IDLING,
                        new DrivingStateRestrictions()
                                .setDistractionOptimizationRequired(baseline != 0)
                                .setRestrictions(baseline))
                .setUxRestrictions(DRIVING_STATE_IDLING,
                        new DrivingStateRestrictions()
                                .setMode(UX_RESTRICTION_MODE_PASSENGER)
                                .setDistractionOptimizationRequired(passenger != 0)
                                .setRestrictions(passenger))
                .build();

        if (mCarUxRestrictionsManager.saveUxRestrictionsConfigurationForNextBoot(config)) {
            Toast.makeText(this, "Config saved successfully", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "Config failed to save", Toast.LENGTH_SHORT).show();
        }
    }

    private void showStagedUxRestrictionsConfig() {
        try {
            List<CarUxRestrictionsConfiguration> stagedConfigs =
                    mCarUxRestrictionsManager.getStagedConfigs();
            if (stagedConfigs == null || stagedConfigs.size() == 0) {
                new AlertDialog.Builder(this)
                        .setMessage(R.string.no_staged_config)
                        .show();
                return;
            }
            CharArrayWriter charWriter = new CharArrayWriter();
            for (int i = 0; i < stagedConfigs.size(); i++) {
                CarUxRestrictionsConfiguration stagedConfig =
                        stagedConfigs.get(i);
                JsonWriter writer = new JsonWriter(charWriter);
                writer.setIndent("\t");
                stagedConfig.writeJson(writer);
            }
            new AlertDialog.Builder(this)
                    .setTitle(R.string.staged_config_title)
                    .setMessage(charWriter.toString())
                    .show();
        } catch (Exception e) {
            Log.e(TAG, "Failed writing restrictions configuration", e);
        }
    }

    private void showProdUxRestrictionsConfig() {
        try {
            List<CarUxRestrictionsConfiguration> configs =
                    mCarUxRestrictionsManager.getConfigs();
            if (configs == null || configs.size() == 0) {
                new AlertDialog.Builder(this)
                        .setMessage(R.string.no_prod_config)
                        .show();
                return;
            }

            CharArrayWriter charWriter = new CharArrayWriter();
            for (int i = 0; i < configs.size(); i++) {
                CarUxRestrictionsConfiguration prodConfig = configs.get(i);
                JsonWriter writer = new JsonWriter(charWriter);
                writer.setIndent("\t");
                // TODO(b/241589812): Also show the config for the current display.
                prodConfig.writeJson(writer);
            }
            new AlertDialog.Builder(this)
                    .setTitle(R.string.prod_config_title)
                    .setMessage(charWriter.toString())
                    .show();
        } catch (Exception e) {
            Log.e(TAG, "Failed writing restrictions configuration", e);
        }
    }
}
