#!/usr/bin/env python3

# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import asyncio
import psutil
import time
import os
import argparse
import logging
from datetime import datetime

from kasa import SmartStrip


class KasaPlugController:
    """Provides control of a device's charger.

    The device's charger must be plugged into one of the 3 outlets of a Kasa
    Smart
    Plug Power Strip (KP303). The outlet name must match the device's host name
    (this is intended to prevent inadvertently controlling the wrong device's
    charger).
    """

    def __init__(self, kasa_power_strip_ip: str):
        """
        Constructs a KasaPlugController to control the current device's
        charger.

        Args:
            kasa_power_strip_ip: IP of the Kasak Smart Plug Power Strip in which
                this device's charger is connected.
        """
        # The outlet name must match the device's host name.
        self.kasa_outlet_name = os.uname()[1].split('.')[0]

        # Create the event loop
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)

        # Create the strip controller
        self.strip = SmartStrip(kasa_power_strip_ip)
        self.loop.run_until_complete(self.strip.update())

        self.closed = False

    def __del__(self):
        self.close()

    def turn_on(self):
        """Turns on this device's charger."""
        logging.info("Turning on the charger")
        for plug in self.strip.children:
            if plug.alias == self.kasa_outlet_name:
                self.loop.run_until_complete(plug.turn_on())
                return
        logging.error("Cannot find device to turn on")

    def turn_off(self):
        """Turns off this device's charger."""
        logging.info("Turning off the charger")
        for plug in self.strip.children:
            if plug.alias == self.kasa_outlet_name:
                self.loop.run_until_complete(plug.turn_off())

                battery = psutil.sensors_battery()
                while battery.power_plugged:
                    logging.info(
                        "Waiting for device to no longer be plugged in"
                    )
                    time.sleep(1)
                    battery = psutil.sensors_battery()
                return
        logging.error("Cannot find device to turn off")

    def discharge_to(self, level: int):
        """Discharges the battery until it reaches a target level.

        Args:
            level: The target battery level.
        """

        self.turn_off()

        battery = psutil.sensors_battery()

        while battery.percent > level:
            logging.info(
                f"Waiting to discharge to {level}%."
                f" Currently at {battery.percent}%"
            )

            # Perform arbitrary operations as fast as possible to burn
            # CPU and discharge faster.
            f_value = 0.81
            start = datetime.now()
            while (datetime.now() - start).total_seconds() < 10:
                f_value = f_value * 1.7272882
                f_value = f_value / 1.7272882

            battery = psutil.sensors_battery()

        logging.info(f"Discharge to {level}% complete")

    def charge_to(self, level: int):
        """Charges the battery until it reaches a target level.

        Args:
            level: The target battery level.
        """

        self.turn_on()

        battery = psutil.sensors_battery()

        while battery.percent < level:
            logging.info(
                f"Waiting to charge to {level}%."
                f" Currently at {battery.percent}%"
            )
            time.sleep(10)
            battery = psutil.sensors_battery()

        logging.info(f"Charge to {level}% complete")

    def charge_or_discharge_to(self, level: int):
        """Charges or discharges the battery until it reaches a target level.

        Leaves the charger in an unplugged state.

        Args:
            level: The target battery level.
        """
        battery = psutil.sensors_battery()
        if battery.percent < level:
            self.charge_to(level)
        elif battery.percent > level:
            self.discharge_to(level)
        else:
            logging.info(f"Battery is already at the target level {level}%")
        self.turn_off()

    def close(self):
        """Closes the message loop."""
        if self.closed:
            return
        self.closed = True
        self.loop.close()


def get_plug_controller(ip: str):
    return KasaPlugController(ip)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description='Controls kasa power switch connected to this device.'
    )
    parser.add_argument(
        "--kasa_power_strip_ip",
        required=True,
        help="IP address of the kasa power switch.",
    )
    parser.add_argument(
        "--charge_level", type=int, required=True, help="Desired charge level."
    )
    args = parser.parse_args()

    kasa_plug_controller = KasaPlugController(args.kasa_power_strip_ip)
    kasa_plug_controller.charge_to(args.charge_level)
    kasa_plug_controller.close()
