# Copyright 2016 Jason Horne
#
# 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.

import logging
from datetime import timedelta, datetime
import pytz

import pycarwings3

log = logging.getLogger(__name__)


def _time_remaining(t):
    minutes = float(0)
    if t:
        if ("hours" in t) and t["hours"]:
            minutes = 60 * float(t["hours"])
        elif ("HourRequiredToFull" in t) and t["HourRequiredToFull"]:
            minutes = 60 * float(t["HourRequiredToFull"])
        if ("minutes" in t) and t["minutes"]:
            minutes += float(t["minutes"])
        elif ("MinutesRequiredToFull" in t) and t["MinutesRequiredToFull"]:
            minutes += float(t["MinutesRequiredToFull"])

    return minutes


class CarwingsResponse:
    def __init__(self, response):
        op_result = None
        if ("operationResult" in response):
            op_result = response["operationResult"]
        elif ("OperationResult" in response):
            op_result = response["OperationResult"]

        # seems to indicate that the vehicle cannot be reached
        if ("ELECTRIC_WAVE_ABNORMAL" == op_result):
            log.error("could not establish communications with vehicle")
            raise pycarwings3.CarwingsError("could not establish communications with vehicle")

    def _set_cruising_ranges(self, status, off_key="cruisingRangeAcOff", on_key="cruisingRangeAcOn"):
        if off_key in status:
            self.cruising_range_ac_off_km = float(status[off_key]) / 1000
        if on_key in status:
            self.cruising_range_ac_on_km = float(status[on_key]) / 1000

    def _set_timestamp(self, status):
        self.timestamp = datetime.strptime(status["timeStamp"], "%Y-%m-%d %H:%M:%S")   # "2016-01-02 17:17:38"


class CarwingsInitialAppResponse(CarwingsResponse):
    def __init__(self, response):
        CarwingsResponse.__init__(self, response)
        self.baseprm = response["baseprm"]


class CarwingsLoginResponse(CarwingsResponse):
    """
        example JSON response to login:
        {
            "status":200,
            "message":"success",
            "sessionId":"12345678-1234-1234-1234-1234567890",
            "VehicleInfoList": {
                "VehicleInfo": [
                    {
                        "charger20066":"false",
                        "nickname":"LEAF",
                        "telematicsEnabled":"true",
                        "vin":"1ABCDEFG2HIJKLM3N"
                    }
                ],
                "vehicleInfo": [
                    {
                        "charger20066":"false",
                        "nickname":"LEAF",
                        "telematicsEnabled":"true",
                        "vin":"1ABCDEFG2HIJKLM3N"
                    }
                ]
            },
            "vehicle": {
                "profile": {
                    "vin":"1ABCDEFG2HIJKLM3N",
                    "gdcUserId":"FG12345678",
                    "gdcPassword":"password",
                    "encAuthToken":"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ",
                    "dcmId":"123456789012",
                    "nickname":"Alpha124",
                    "status":"ACCEPTED",
                    "statusDate": "Aug 15, 2015 07:00 PM"
                }
            },
            "EncAuthToken":"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ",
            "CustomerInfo": {
                "UserId":"AB12345678",
                "Language":"en-US",
                "Timezone":"America/New_York",
                "RegionCode":"NNA",
                "OwnerId":"1234567890",
                "Nickname":"Bravo456",
                "Country":"US",
                "VehicleImage":"/content/language/default/images/img/ph_car.jpg",
                "UserVehicleBoundDurationSec":"999971200",
                "VehicleInfo": {
                    "VIN":"1ABCDEFG2HIJKLM3N",
                    "DCMID":"201212345678",
                    "SIMID":"12345678901234567890",
                    "NAVIID":"1234567890",
                    "EncryptedNAVIID":"1234567890ABCDEFGHIJKLMNOP",
                    "MSN":"123456789012345",
                    "LastVehicleLoginTime":"",
                    "UserVehicleBoundTime":"2015-08-17T14:16:32Z",
                    "LastDCMUseTime":""
                }
            },
            "UserInfoRevisionNo":"1"
        }

        example JSON response to login when subscription has expired. Resubscribe via the Nissan Connect EV website.
        {
            "status":"AAS-604",
            "message":"INVALID PARAMS"
        }
    """
    def __init__(self, response):
        CarwingsResponse.__init__(self, response)

        profile = response["vehicle"]["profile"]
        self.gdc_user_id = profile["gdcUserId"]
        self.dcm_id = profile["dcmId"]
        self.vin = profile["vin"]

        # vehicleInfo block may be top level, or contained in a VehicleInfoList object;
        # why it's sometimes one way and sometimes another is not clear.
        if "VehicleInfoList" in response:
            self.nickname = response["VehicleInfoList"]["vehicleInfo"][0]["nickname"]
            self.custom_sessionid = response["VehicleInfoList"]["vehicleInfo"][0]["custom_sessionid"]
        elif "vehicleInfo" in response:
            self.nickname = response["vehicleInfo"][0]["nickname"]
            self.custom_sessionid = response["vehicleInfo"][0]["custom_sessionid"]

        customer_info = response["CustomerInfo"]
        self.tz = customer_info["Timezone"]
        self.language = customer_info["Language"]
        self.user_vehicle_bound_time = \
            customer_info["VehicleInfo"].get("UserVehicleBoundTime",
                                             "1970-01-01T00:00:00Z")

        self.leafs = [{
            "vin": self.vin,
            "nickname": self.nickname,
            "bound_time": self.user_vehicle_bound_time
        }]


class CarwingsBatteryStatusResponse(CarwingsResponse):
    """
        Note that before December 2018 this returned a response. Between Dec-2018 and Aug-2019
        it did not return a response from the Nissan Servers.  As of Aug 2019 a response is now
        returned again.

        # Original
        {
            "status": 200,
            "message": "success",
            "responseFlag": "1",
            "operationResult": "START",
            "timeStamp": "2016-01-02 17:17:38",
            "cruisingRangeAcOn": "115328.0",
            "cruisingRangeAcOff": "117024.0",
            "currentChargeLevel": "0",
            "chargeMode": "220V",
            "pluginState": "CONNECTED",
            "charging": "YES",
            "chargeStatus": "CT",
            "batteryDegradation": "10",
            "batteryCapacity": "12",
            "timeRequiredToFull": {
                "hours": "",
                "minutes": ""
            },
            "timeRequiredToFull200": {
                "hours": "",
                "minutes": ""
            },
            "timeRequiredToFull200_6kW": {
                "hours": "",
                "minutes": ""
            }
        }

        # As at 21/01/2019 for a 30kWh Leaf now seems that
        # BatteryStatusCheckResultRequest.php always returns this
        # regardless of battery status.
        {
            "status":200,
            "responseFlag":"0"
        }

        {
            "status":200,
            "message":"success",
            "responseFlag":"1",
            "operationResult":"START",
            "timeStamp":"2016-02-14 20:28:45",
            "cruisingRangeAcOn":"107136.0",
            "cruisingRangeAcOff":"115776.0",
            "currentChargeLevel":"0",
            "chargeMode":"NOT_CHARGING",
            "pluginState":"QC_CONNECTED",
            "charging":"YES",
            "chargeStatus":"CT",
            "batteryDegradation":"11",
            "batteryCapacity":"12",
            "timeRequiredToFull":{
                "hours":"",
                "minutes":""
            },
            "timeRequiredToFull200":{
                "hours":"",
                "minutes":""
            },
            "timeRequiredToFull200_6kW":{
                "hours":"",
                "minutes":""
            }
        }

        # As at 22/08/2019 for a 30kWh Leaf now seesm that
        # BatteryStatusCheckResultRequest.php returns data again
        # after polling a number of times.
        {
                "status": 200,
                "responseFlag": "1",
                "operationResult": "START",
                "timeStamp": "2019-08-22 10:26:51",
                "cruisingRangeAcOn": "129000.0",
                "cruisingRangeAcOff": "132000.0",
                "currentChargeLevel": "0",
                "chargeMode": "NOT_CHARGING",
                "pluginState": "NOT_CONNECTED",
                "charging": "NO",
                "chargeStatus": "0",
                "batteryDegradation": "180",
                "batteryCapacity": "240",
                "timeRequiredToFull": {
                        "hours": "11",
                        "minutes": "30"
                },
                "timeRequiredToFull200": {
                        "hours": "6",
                        "minutes": "30"
                },
                "timeRequiredToFull200_6kW": {
                        "hours": "2",
                        "minutes": "30"
                }
        }

    """
    def __init__(self, status):
        CarwingsResponse.__init__(self, status)

        self._set_timestamp(status)
        self._set_cruising_ranges(status)

        self.answer = status

        self.battery_capacity = status["batteryCapacity"]
        self.battery_degradation = status["batteryDegradation"]

        self.is_connected = ("NOT_CONNECTED" != status["pluginState"])    # fun double negative
        self.plugin_state = status["pluginState"]

        self.charging_status = status["chargeMode"]

        self.is_charging = ("YES" == status["charging"])

        self.is_quick_charging = ("RAPIDLY_CHARGING" == status["chargeMode"])
        self.is_connected_to_quick_charger = ("QC_CONNECTED" == status["pluginState"])

        self.time_to_full_trickle = timedelta(minutes=_time_remaining(status["timeRequiredToFull"]))
        self.time_to_full_l2 = timedelta(minutes=_time_remaining(status["timeRequiredToFull200"]))
        self.time_to_full_l2_6kw = timedelta(minutes=_time_remaining(status["timeRequiredToFull200_6kW"]))

        # For some leafs the battery_percent is not returned
        self.battery_percent = 100 * float(self.battery_degradation) / 12


class CarwingsLatestClimateControlStatusResponse(CarwingsResponse):
    """
    climate control on:
        {
            "status":200,
            "message":"success",
            "RemoteACRecords":{
                "OperationResult":"START_BATTERY",
                "OperationDateAndTime":"Feb 10, 2016 10:22 PM",
                "RemoteACOperation":"START",
                "ACStartStopDateAndTime":"2018/04/08 10:00",
                "CruisingRangeAcOn":"107712.0",
                "CruisingRangeAcOff":"109344.0",
                "ACStartStopURL":"",
                "PluginState":"NOT_CONNECTED",
                "ACDurationBatterySec":"900",
                "ACDurationPluggedSec":"7200"
            },
            "OperationDateAndTime":""
        }

    climate control off:
        {
            "status":200,
            "message":"success",
            "RemoteACRecords":{
                "OperationResult":"START",
                "OperationDateAndTime":"Feb 10, 2016 10:26 PM",
                "RemoteACOperation":"STOP",
                "ACStartStopDateAndTime":"2018/04/08 10:00",
                "CruisingRangeAcOn":"111936.0",
                "CruisingRangeAcOff":"113632.0",
                "ACStartStopURL":"",
                "PluginState":"NOT_CONNECTED",
                "ACDurationBatterySec":"900",
                "ACDurationPluggedSec":"7200"
            },
            "OperationDateAndTime":""
        }

    error:
        {
            "status":200,
            "RemoteACRecords":{
                "OperationResult":"ELECTRIC_WAVE_ABNORMAL",
                "OperationDateAndTime":"2018/04/08 10:00",
                "RemoteACOperation":"START",
                "ACStartStopDateAndTime":"2018/04/08 10:00",
                "ACStartStopURL":"",
                "PluginState":"INVALID",
                "ACDurationBatterySec":"900",
                "ACDurationPluggedSec":"7200",
                "PreAC_unit":"C",
                "PreAC_temp":"22"
            }
        }
    noinfo (from a 2014 24kWh Leaf):
        {
            "status":200,
            "RemoteACRecords":[]
        }

    """
    def __init__(self, status):
        CarwingsResponse.__init__(self, status["RemoteACRecords"])
        racr = status["RemoteACRecords"]

        self._set_cruising_ranges(racr, on_key="CruisingRangeAcOn", off_key="CruisingRangeAcOff")

        if isinstance(racr, dict):
            self._operation_date_and_time = racr["OperationDateAndTime"]

        # If no empty RemoteACRecords list is returned then assume CC is off.
        if not isinstance(racr, dict):
            self.is_hvac_running = False
        else:
            # Seems to be running only if both of these contain "START".
            self.is_hvac_running = (
                racr["OperationResult"] and
                racr["OperationResult"].startswith("START") and
                racr["RemoteACOperation"] == "START"
            )

        if isinstance(racr, dict):

            try:
                self.is_plugged_in = ("NOT_CONNECTED" != racr["PluginState"])
            except KeyError:
                self.is_plugged_in = None

            try:
                self.ac_duration = timedelta(seconds=int(racr["ACDurationPluggedSec"] if self.is_plugged_in else racr["ACDurationBatterySec"]))
            except KeyError:
                self.ac_duration = None

            try:
                # "2018/04/08 10:00"
                self.ac_start_stop_date_and_time = datetime.strptime(racr["ACStartStopDateAndTime"], "%Y/%m/%d %H:%M").replace(tzinfo=pytz.utc)
            except (KeyError, ValueError):
                self.ac_start_stop_date_and_time = None
        else:
            self.is_plugged_in = None
            self.ac_duration = None
            self.ac_start_stop_date_and_time = None

        # "Feb 10, 2016 10:26 PM"
        try:
            if isinstance(racr, dict):
                self.timestamp = datetime.strptime(racr["OperationDateAndTime"],
                                                    "%b %d, %Y %I:%M %p").replace(tzinfo=pytz.utc)
            else:
                self.timestamp = None
        except (KeyError, ValueError):
            self.timestamp = None

    def __eq__(self, other):
        if not isinstance(other, CarwingsLatestClimateControlStatusResponse):
            return NotImplemented

        if self._operation_date_and_time is None:
            return NotImplemented

        return (
            self._operation_date_and_time == other._operation_date_and_time
        )


class CarwingsStartClimateControlResponse(CarwingsResponse):
    """
        {
            "status":200,
            "message":"success",
            "responseFlag":"1",
            "operationResult":"START_BATTERY",
            "acContinueTime":"15",
            "cruisingRangeAcOn":"106400.0",
            "cruisingRangeAcOff":"107920.0",
            "timeStamp":"2016-02-05 12:59:46",
            "hvacStatus":"ON"
        }
    """
    def __init__(self, status):
        CarwingsResponse.__init__(self, status)

        self._set_timestamp(status)
        self._set_cruising_ranges(status)

        self.operation_result = status["operationResult"]     # e.g. "START_BATTERY", ...?
        self.ac_continue_time = timedelta(minutes=float(status["acContinueTime"]))
        self.hvac_status = status["hvacStatus"]               # "ON" or "OFF"
        self.is_hvac_running = ("ON" == self.hvac_status)


class CarwingsStopClimateControlResponse(CarwingsResponse):
    """
        {
            "status":200,
            "message":"success",
            "responseFlag":"1",
            "operationResult":"START",
            "timeStamp":"2016-02-09 03:32:51",
            "hvacStatus":"OFF"
        }
    """
    def __init__(self, status):
        CarwingsResponse.__init__(self, status)

        self._set_timestamp(status)
        self.hvac_status = status["hvacStatus"]                # "ON" or "OFF"
        self.is_hvac_running = ("ON" == self.hvac_status)


class CarwingsClimateControlScheduleResponse(CarwingsResponse):
    """
        {
            "status":200,
            "message":"success",
            "LastScheduledTime":"Feb  9, 2016 05:39 PM",
            "ExecuteTime":"2016-02-10 01:00:00",
            "DisplayExecuteTime":"Feb  9, 2016 08:00 PM",
            "TargetDate":"2016/02/10 01:00"
        }
    """
    def __init__(self, status):
        CarwingsResponse.__init__(self, status)

        self.display_execute_time = status["DisplayExecuteTime"]         # displayable, timezone-adjusted
        self.execute_time = datetime.strptime(status["ExecuteTime"] + " UTC", "%Y-%m-%d %H:%M:%S %Z")   # GMT
        self.display_last_scheduled_time = status["LastScheduledTime"]   # displayable, timezone-adjusted
        self.last_scheduled_time = datetime.strptime(status["LastScheduledTime"], "%b %d, %Y %I:%M %p")
        # unknown purpose; don't surface to avoid confusion
        # self.target_date = status["TargetDate"]


class CarwingsDrivingAnalysisResponse(CarwingsResponse):
    """
        {
            "status":200,
            "message":"success",
            "DriveAnalysisBasicScreenResponsePersonalData": {
                "DateSummary":{
                    "TargetDate":"2016-02-03",
                    "ElectricMileage":"4.4",
                    "ElectricMileageLevel":"3",
                    "PowerConsumptMoter":"295.2",
                    "PowerConsumptMoterLevel":"4",
                    "PowerConsumptMinus":"84.8",
                    "PowerConsumptMinusLevel":"3",
                    "PowerConsumptAUX":"17.1",
                    "PowerConsumptAUXLevel":"5",
                    "DisplayDate":"Feb  3, 16"
                },
                "ElectricCostScale":"miles/kWh"
            },
            "AdviceList":{
                "Advice":{
                    "title":"World Number of Trips Rankings (last week):",
                    "body":"The highest number of trips driven was 130 by a driver located in Japan."
                }
            }
        }
    """
    def __init__(self, status):
        CarwingsResponse.__init__(self, status)

        summary = status["DriveAnalysisBasicScreenResponsePersonalData"]["DateSummary"]

        # avg energy economy, in units of 'electric_cost_scale' (e.g. miles/kWh)
        self.electric_mileage = summary["ElectricMileage"]
        # rating for above, scale of 1-5
        self.electric_mileage_level = summary["ElectricMileageLevel"]

        # "acceleration performance": "electricity used for motor activation over 1km", Watt-Hours
        self.power_consumption_moter = summary["PowerConsumptMoter"]
        # rating for above, scale of 1-5
        self.power_consumption_moter_level = summary["PowerConsumptMoterLevel"]

        # Watt-Hours generated by braking
        self.power_consumption_minus = summary["PowerConsumptMinus"]
        # rating for above, scale of 1-5
        self.power_consumption_minus_level = summary["PowerConsumptMinusLevel"]

        # Electricity used by aux devices, Watt-Hours
        self.power_consumption_aux = summary["PowerConsumptAUX"]
        # rating for above, scale of 1-5
        self.power_consumption_aux_level = summary["PowerConsumptAUXLevel"]

        self.display_date = summary["DisplayDate"]     # "Feb  3, 16"
        self.target_date = summary["TargetDate"]       # "2016-02-03"

        self.electric_cost_scale = status["DriveAnalysisBasicScreenResponsePersonalData"]["ElectricCostScale"]

        self.advice = [status["AdviceList"]["Advice"]]  # will contain "title" and "body"

    @property
    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented
        return (self.target_date == other.target_date
                and self.electric_mileage == other.electric_mileage
                and self.power_consumption_aux == other.power_consumption_aux
                and self.power_consumption_minus == other.power_consumption_minus
                and self.power_consumption_moter == other.power_consumption_moter)


class CarwingsLatestBatteryStatusResponse(CarwingsResponse):
    """
        # not connected to a charger
        {
            "status":200,
            "message":"success",
            "BatteryStatusRecords":{
                "OperationResult":"START",
                "OperationDateAndTime":"Feb  9, 2016 11:09 PM",
                "BatteryStatus":{
                    "BatteryChargingStatus":"NOT_CHARGING",
                    "BatteryCapacity":"12",
                    "BatteryRemainingAmount":"3",
                    "BatteryRemainingAmountWH":"",
                    "BatteryRemainingAmountkWH":""
                },
                "PluginState":"NOT_CONNECTED",
                "CruisingRangeAcOn":"39192.0",
                "CruisingRangeAcOff":"39744.0",
                "TimeRequiredToFull":{              # 120V
                    "HourRequiredToFull":"18",
                    "MinutesRequiredToFull":"30"
                },
                "TimeRequiredToFull200":{           # 240V, 3kW
                    "HourRequiredToFull":"6",
                    "MinutesRequiredToFull":"0"
                },
                "TimeRequiredToFull200_6kW":{       # 240V, 6kW
                    "HourRequiredToFull":"4",
                    "MinutesRequiredToFull":"0"
                },
                "NotificationDateAndTime":"2016/02/10 04:10",
                "TargetDate":"2016/02/10 04:09"
            }
        }

        # not connected to a charger - as at 21/01/2019 20:01 (for a 30kWh leaf)
        {
            "status":200,
            "BatteryStatusRecords": {
                "OperationResult":"START",
                "OperationDateAndTime":"21-Jan-2019 13:29",
                "BatteryStatus":{
                    "BatteryChargingStatus":"NOT_CHARGING",
                    "BatteryCapacity":"240",
                    "BatteryRemainingAmount":"220",
                    "BatteryRemainingAmountWH":"24480",
                    "BatteryRemainingAmountkWH":"",
                    "SOC":{
                        "Value":"91"
                    }
                },
                "PluginState":"NOT_CONNECTED",
                "CruisingRangeAcOn":"146000",
                "CruisingRangeAcOff":"168000",
                "TimeRequiredToFull":{
                    "HourRequiredToFull":"4",
                    "MinutesRequiredToFull":"30"
                },
                "TimeRequiredToFull200":{
                    "HourRequiredToFull":"3"
                    ,"MinutesRequiredToFull":"0"
                },
                "TimeRequiredToFull200_6kW":{
                    "HourRequiredToFull":"1",
                    "MinutesRequiredToFull":"30"
                },
                "NotificationDateAndTime":"2019/01/21 13:29",
                "TargetDate":"2019/01/21 13:29"
            }
        }


        # connected to a quick charger
        {
            "status":200,
            "message":"success",
            "BatteryStatusRecords":{
                "OperationResult":"START",
                "OperationDateAndTime":"Feb 14, 2016 03:28 PM",
                "BatteryStatus":{
                    "BatteryChargingStatus":"RAPIDLY_CHARGING",
                    "BatteryCapacity":"12",
                    "BatteryRemainingAmount":"11",
                    "BatteryRemainingAmountWH":"",
                    "BatteryRemainingAmountkWH":""
                },
                "PluginState":"QC_CONNECTED",
                "CruisingRangeAcOn":"107136.0",
                "CruisingRangeAcOff":"115776.0",
                "NotificationDateAndTime":"2016/02/14 20:28",
                "TargetDate":"2016/02/14 20:28"
            }
        }

        # connected to a charging station
        {
          "status": 200,
          "message": "success",
          "BatteryStatusRecords": {
            "OperationResult": "START",
            "OperationDateAndTime": "Feb 19, 2016 12:12 PM",
            "BatteryStatus": {
              "BatteryChargingStatus": "NORMAL_CHARGING",
              "BatteryCapacity": "12",
              "BatteryRemainingAmount": "12",
              "BatteryRemainingAmountWH": "",
              "BatteryRemainingAmountkWH": ""
            },
            "PluginState": "CONNECTED",
            "CruisingRangeAcOn": "132000.0",
            "CruisingRangeAcOff": "134000.0",
            "TimeRequiredToFull200_6kW": {
              "HourRequiredToFull": "0",
              "MinutesRequiredToFull": "40"
            },
            "NotificationDateAndTime": "2016/02/19 17:12",
            "TargetDate": "2016/02/19 17:12"
          }
        }
    """
    def __init__(self, status):
        CarwingsResponse.__init__(self, status["BatteryStatusRecords"])
        self.answer = status

        recs = status["BatteryStatusRecords"]

        bs = recs["BatteryStatus"]
        self.battery_capacity = bs["BatteryCapacity"]
        self.battery_remaining_amount = bs["BatteryRemainingAmount"]
        self.battery_remaining_amount_wh = bs["BatteryRemainingAmountWH"]
        self.charging_status = bs["BatteryChargingStatus"]
        self.is_charging = ("NOT_CHARGING" != bs["BatteryChargingStatus"])    # double negatives are fun
        self.is_quick_charging = ("RAPIDLY_CHARGING" == bs["BatteryChargingStatus"])

        self.plugin_state = recs["PluginState"]
        self.is_connected = ("NOT_CONNECTED" != recs["PluginState"])          # another double negative
        self.is_connected_to_quick_charger = ("QC_CONNECTED" == recs["PluginState"])

        self._set_cruising_ranges(recs, off_key="CruisingRangeAcOff", on_key="CruisingRangeAcOn")

        if "TimeRequiredToFull" in recs:
            self.time_to_full_trickle = timedelta(minutes=_time_remaining(recs["TimeRequiredToFull"]))
        else:
            self.time_to_full_trickle = None

        if "TimeRequiredToFull200" in recs:
            self.time_to_full_l2 = timedelta(minutes=_time_remaining(recs["TimeRequiredToFull200"]))
        else:
            self.time_to_full_l2 = None

        if "TimeRequiredToFull200_6kW" in recs:
            self.time_to_full_l2_6kw = timedelta(minutes=_time_remaining(recs["TimeRequiredToFull200_6kW"]))
        else:
            self.time_to_full_l2_6kw = None

        if float(self.battery_capacity) == 0:
            log.debug("battery_capacity=0, status=%s", status)
            self.battery_percent = 0
        else:
            if self.battery_remaining_amount == '':
                self.battery_percent = 0
            else:
                self.battery_percent = 100 * float(self.battery_remaining_amount) / 12

        # Leaf 2016 has SOC (State Of Charge) in BatteryStatus, a more accurate battery_percentage
        if "SOC" in bs:
            self.state_of_charge = bs["SOC"]["Value"]
            # Update battery_percent with more accurate version
            self.battery_percent = float(self.state_of_charge)
        else:
            self.state_of_charge = None

        # "NotificationDateAndTime":"2016/02/14 20:28",
        self.timestamp = datetime.strptime(
            recs["NotificationDateAndTime"],
            "%Y/%m/%d %H:%M"
        ).replace(tzinfo=pytz.utc)

    def __eq__(self, other):
        if not isinstance(other, CarwingsLatestBatteryStatusResponse):
            return NotImplemented

        return (
            self.timestamp == other.timestamp
        )

class CarwingsElectricRateSimulationResponse(CarwingsResponse):
    def __init__(self, status):
        CarwingsResponse.__init__(self, status)

        r = status["PriceSimulatorDetailInfoResponsePersonalData"]
        t = r["PriceSimulatorTotalInfo"]

        self.month = r["DisplayMonth"]     # e.g. "Feb/2016"
        self.travellist = r["PriceSimulatorDetailInfoDateList"]["PriceSimulatorDetailInfoDate"]
        self.total_number_of_trips = t["TotalNumberOfTrips"]
        self.total_power_consumption = t["TotalPowerConsumptTotal"]                # in kWh
        self.total_acceleration_power_consumption = t["TotalPowerConsumptMoter"]   # in kWh
        self.total_power_regenerated_in_braking = t["TotalPowerConsumptMinus"]     # in kWh
        self.total_travel_distance_km = float(t["TotalTravelDistance"]) / 1000     # assumed to be in meters

        self.total_electric_mileage = t["TotalElectricMileage"]
        self.total_co2_reduction = t["TotalCO2Reductiont"]   # (yep, extra 't' at the end)

        self.electricity_rate = r["ElectricPrice"]
        self.electric_bill = r["ElectricBill"]
        self.electric_cost_scale = r["ElectricCostScale"]    # e.g. "miles/kWh"


class CarwingsMyCarFinderResponse(CarwingsResponse):
    """
        {
            "Location": {
                "Country": "",
                "Home": "OUTSIDE",
                "LatitudeDeg": "69",
                "LatitudeMin": "41",
                "LatitudeMode": "NORTH",
                "LatitudeSec": "5540",
                "LocationType": "WGS84",
                "LongitudeDeg": "18",
                "LongitudeMin": "38",
                "LongitudeMode": "EAST",
                "LongitudeSec": "2506",
                "Position": "UNAVAILABLE"
            },
            "TargetDate": "2017/11/29 20:02",
            "lat": "69.698722222222",
            "lng": "18.640294444444",
            "receivedDate": "2017/11/29 20:02",
            "responseFlag": "1",
            "resultCode": "1",
            "status": 200,
            "timeStamp": "2017-11-29 20:02:45"
        }
    """
    def __init__(self, status):
        CarwingsResponse.__init__(self, status)

        self.latitude = status["lat"]
        self.longitude = status["lng"]
