package com.genymobile.scrcpy;

import com.genymobile.scrcpy.device.Device;
import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.Settings;
import com.genymobile.scrcpy.util.SettingsException;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Handle the cleanup of scrcpy, even if the main process is killed.
 * <p>
 * This is useful to restore some state when scrcpy is closed, even on device disconnection (which kills the scrcpy process).
 */
public final class CleanUp {

    private static final int MSG_TYPE_MASK = 0b11;
    private static final int MSG_TYPE_RESTORE_STAY_ON = 0;
    private static final int MSG_TYPE_DISABLE_SHOW_TOUCHES = 1;
    private static final int MSG_TYPE_RESTORE_NORMAL_POWER_MODE = 2;
    private static final int MSG_TYPE_POWER_OFF_SCREEN = 3;

    private static final int MSG_PARAM_SHIFT = 2;

    private final OutputStream out;

    public CleanUp(OutputStream out) {
        this.out = out;
    }

    public static CleanUp configure(int displayId) throws IOException {
        String[] cmd = {"app_process", "/", CleanUp.class.getName(), String.valueOf(displayId)};

        ProcessBuilder builder = new ProcessBuilder(cmd);
        builder.environment().put("CLASSPATH", Server.SERVER_PATH);
        Process process = builder.start();
        return new CleanUp(process.getOutputStream());
    }

    private boolean sendMessage(int type, int param) {
        assert (type & ~MSG_TYPE_MASK) == 0;
        int msg = type | param << MSG_PARAM_SHIFT;
        try {
            out.write(msg);
            out.flush();
            return true;
        } catch (IOException e) {
            Ln.w("Could not configure cleanup (type=" + type + ", param=" + param + ")", e);
            return false;
        }
    }

    public boolean setRestoreStayOn(int restoreValue) {
        // Restore the value (between 0 and 7), -1 to not restore
        // <https://developer.android.com/reference/android/provider/Settings.Global#STAY_ON_WHILE_PLUGGED_IN>
        assert restoreValue >= -1 && restoreValue <= 7;
        return sendMessage(MSG_TYPE_RESTORE_STAY_ON, restoreValue & 0b1111);
    }

    public boolean setDisableShowTouches(boolean disableOnExit) {
        return sendMessage(MSG_TYPE_DISABLE_SHOW_TOUCHES, disableOnExit ? 1 : 0);
    }

    public boolean setRestoreNormalPowerMode(boolean restoreOnExit) {
        return sendMessage(MSG_TYPE_RESTORE_NORMAL_POWER_MODE, restoreOnExit ? 1 : 0);
    }

    public boolean setPowerOffScreen(boolean powerOffScreenOnExit) {
        return sendMessage(MSG_TYPE_POWER_OFF_SCREEN, powerOffScreenOnExit ? 1 : 0);
    }

    public static void unlinkSelf() {
        try {
            new File(Server.SERVER_PATH).delete();
        } catch (Exception e) {
            Ln.e("Could not unlink server", e);
        }
    }

    public static void main(String... args) {
        unlinkSelf();

        int displayId = Integer.parseInt(args[0]);

        int restoreStayOn = -1;
        boolean disableShowTouches = false;
        boolean restoreNormalPowerMode = false;
        boolean powerOffScreen = false;

        try {
            // Wait for the server to die
            int msg;
            while ((msg = System.in.read()) != -1) {
                int type = msg & MSG_TYPE_MASK;
                int param = msg >> MSG_PARAM_SHIFT;
                switch (type) {
                    case MSG_TYPE_RESTORE_STAY_ON:
                        restoreStayOn = param > 7 ? -1 : param;
                        break;
                    case MSG_TYPE_DISABLE_SHOW_TOUCHES:
                        disableShowTouches = param != 0;
                        break;
                    case MSG_TYPE_RESTORE_NORMAL_POWER_MODE:
                        restoreNormalPowerMode = param != 0;
                        break;
                    case MSG_TYPE_POWER_OFF_SCREEN:
                        powerOffScreen = param != 0;
                        break;
                    default:
                        Ln.w("Unexpected msg type: " + type);
                        break;
                }
            }
        } catch (IOException e) {
            // Expected when the server is dead
        }

        Ln.i("Cleaning up");

        if (disableShowTouches) {
            Ln.i("Disabling \"show touches\"");
            try {
                Settings.putValue(Settings.TABLE_SYSTEM, "show_touches", "0");
            } catch (SettingsException e) {
                Ln.e("Could not restore \"show_touches\"", e);
            }
        }

        if (restoreStayOn != -1) {
            Ln.i("Restoring \"stay awake\"");
            try {
                Settings.putValue(Settings.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(restoreStayOn));
            } catch (SettingsException e) {
                Ln.e("Could not restore \"stay_on_while_plugged_in\"", e);
            }
        }

        if (Device.isScreenOn()) {
            if (powerOffScreen) {
                Ln.i("Power off screen");
                Device.powerOffScreen(displayId);
            } else if (restoreNormalPowerMode) {
                Ln.i("Restoring normal power mode");
                Device.setScreenPowerMode(Device.POWER_MODE_NORMAL);
            }
        }

        System.exit(0);
    }
}
