#!/usr/bin/env python3
import subprocess
import sys
import time
import threading
from enum import IntEnum

import pyray as rl

from openpilot.common.hardware import HARDWARE, PC
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.widgets.scroller import Scroller
from openpilot.system.ui.mici_setup import GreyBigButton, FailedPage
from openpilot.selfdrive.ui.mici.widgets.dialog import BigDialog, BigConfirmationCircleButton

USERDATA = "/dev/disk/by-partlabel/userdata"
TIMEOUT = 3*60


class ResetMode(IntEnum):
  USER_RESET = 0  # user initiated a factory reset from openpilot
  RECOVER = 1     # userdata is corrupt for some reason, give a chance to recover
  TAP_RESET = 2   # user initiated a factory reset by tapping the screen during boot


class ResetFailedPage(FailedPage):
  def __init__(self):
    super().__init__(None, "reset failed", "reboot to try again", icon="icons_mici/setup/reset_failed.png")

  def show_event(self):
    super().show_event()
    self._nav_bar._alpha = 0.0  # not dismissable

  def _back_enabled(self) -> bool:
    return False


class ResettingPage(BigDialog):
  DOT_STEP = 0.6

  def __init__(self):
    super().__init__("resetting device", "this may take up to\na minute...",
                     gui_app.texture("icons_mici/setup/factory_reset.png", 64, 64))
    self._show_time = 0.0

  def show_event(self):
    super().show_event()
    self._nav_bar._alpha = 0.0  # not dismissable
    self._show_time = rl.get_time()

  def _back_enabled(self) -> bool:
    return False

  def _render(self, _):
    t = (rl.get_time() - self._show_time) % (self.DOT_STEP * 2)
    dots = "." * min(int(t / (self.DOT_STEP / 4)), 3)
    self._card.set_value(f"this may take up to\na minute{dots}")
    super()._render(_)


class Reset(Scroller):
  def __init__(self, mode):
    super().__init__()
    self._mode = mode
    self._previous_active_widget = None
    self._reset_failed = False
    self._timeout_st = time.monotonic()

    self._resetting_page = ResettingPage()
    self._reset_failed_page = ResetFailedPage()

    self._reset_button = BigConfirmationCircleButton("reset &\nerase", gui_app.texture("icons_mici/settings/device/uninstall.png", 70, 70),
                                                     self._start_reset, exit_on_confirm=False, red=True)
    self._cancel_button = BigConfirmationCircleButton("cancel", gui_app.texture("icons_mici/setup/cancel.png", 64, 64),
                                                      gui_app.request_close, exit_on_confirm=False)
    self._reboot_button = BigConfirmationCircleButton("reboot\ndevice", gui_app.texture("icons_mici/settings/device/reboot.png", 64, 70),
                                                      HARDWARE.reboot, exit_on_confirm=False)

    # show reboot button if in recover mode
    self._cancel_button.set_visible(mode != ResetMode.RECOVER)
    self._reboot_button.set_visible(mode == ResetMode.RECOVER)

    main_card = GreyBigButton("factory reset", "resetting erases\nall user content & data",
                              gui_app.texture("icons_mici/setup/factory_reset.png", 64, 64))
    self._scroller.add_widget(main_card)

    if mode != ResetMode.USER_RESET:
      self._scroller.add_widget(GreyBigButton("", "Resetting erases all user content & data."))
      if mode == ResetMode.RECOVER:
        main_card.set_value("user data partition\ncould not be mounted")
      elif mode == ResetMode.TAP_RESET:
        main_card.set_value("reset triggered by\ntapping the screen")

    self._scroller.add_widgets([
      GreyBigButton("", "For a deeper reset, go to\nhttps://flash.comma.ai"),
      self._cancel_button,
      self._reboot_button,
      self._reset_button,
    ])

    gui_app.add_nav_stack_tick(self._nav_stack_tick)

  def _do_erase(self):
    if PC:
      return

    # Removing data and formatting
    rm = subprocess.run("sudo rm -rf /data/*", shell=True).returncode
    subprocess.run(f"sudo umount {USERDATA}", shell=True)
    fmt = subprocess.run(f"yes | sudo mkfs.ext4 {USERDATA}", shell=True).returncode

    if rm == 0 or fmt == 0:
      subprocess.run("sudo reboot", shell=True)
    else:
      self._reset_failed = True

  def _start_reset(self):
    def do_erase_thread():
      threading.Thread(target=self._do_erase, daemon=True).start()

    self._resetting_page.set_shown_callback(do_erase_thread)
    gui_app.push_widget(self._resetting_page)

  def _nav_stack_tick(self):
    if self._reset_failed:
      self._reset_failed = False
      gui_app.pop_widgets_to(self, lambda: gui_app.push_widget(self._reset_failed_page))

    active_widget = gui_app.get_active_widget()
    if active_widget != self._previous_active_widget:
      self._previous_active_widget = active_widget
      self._timeout_st = time.monotonic()
    elif self._mode != ResetMode.RECOVER and active_widget != self._resetting_page and (time.monotonic() - self._timeout_st) > TIMEOUT:
      exit(0)


def main():
  mode = ResetMode.USER_RESET
  if len(sys.argv) > 1:
    if sys.argv[1] == '--recover':
      mode = ResetMode.RECOVER
    elif sys.argv[1] == '--tap-reset':
      mode = ResetMode.TAP_RESET

  gui_app.init_window("System Reset")
  reset = Reset(mode)
  gui_app.push_widget(reset)

  for _ in gui_app.render():
    pass


if __name__ == "__main__":
  main()
