/*  Copyright (C) 2024 Martin.JM

    This file is part of Gadgetbridge.

    Gadgetbridge is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Gadgetbridge is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;

public class Weather {
    public static final byte id = 0x0f;

    public static class Settings {
        // WeatherSupport
        public boolean weatherSupported = false;
        public boolean windSupported = false;
        public boolean pm25Supported = false;
        public boolean temperatureSupported = false;
        public boolean locationNameSupported = false;
        public boolean currentTemperatureSupported = false;
        public boolean unitSupported = false;
        public boolean airQualityIndexSupported = false;

        // WeatherExtendedSupport
        public boolean timeSupported = false;
        public boolean sourceSupported = false;
        public boolean weatherIconSupported = false;

        // WeatherSunMoonSupport
        public boolean sunRiseSetSupported = false;
        public boolean moonPhaseSupported = false;

        // ExpandCapabilityRequest
        public boolean uvIndexSupported = false;
    }

    public enum WeatherIcon {
        // Also used for the text on the watch
        SUNNY,
        CLOUDY,
        OVERCAST,
        SHOWERS,
        THUNDERSTORMS,
        THUNDER_AND_HAIL,
        SLEET,
        LIGHT_RAIN,
        RAIN,
        HEAVY_RAIN,
        RAIN_STORM,
        HEAVY_RAIN_STORMS,
        SEVERE_RAIN_STORMS,
        SNOW_FLURRIES,
        LIGHT_SNOW,
        SNOW,
        HEAVY_SNOW,
        SNOWSTORMS,
        FOG,
        FREEZING_RAIN,
        DUST_STORM,
        LIGHT_TO_MODERATE_RAIN,
        MODERATE_TO_HEAVY_RAIN,
        HEAVY_TO_SEVERE_RAIN,
        HEAVY_TO_TORRENTIAL_RAIN,
        SEVERE_TO_TORRENTIAL_RAIN,
        LIGHT_TO_MODERATE_SNOW,
        MODERATE_TO_HEAVY_SNOW,
        HEAVY_SNOW_TO_BLIZZARD,
        DUST,
        SAND,
        SANDSTORMS,
        FREEZING, // misses small/non-moving icon
        HOT, // misses small/non-moving icon
        COLD, // misses small/non-moving icon
        WINDY,
        HAZY,
        UNKNOWN // Good to have probably
    }

    private static byte iconToByte(WeatherIcon weatherIcon) {
        switch (weatherIcon) {
            case SUNNY:
                return 0x00;
            case CLOUDY:
                return 0x01;
            case OVERCAST:
                return 0x02;
            case SHOWERS:
                return 0x03;
            case THUNDERSTORMS:
                return 0x04;
            case THUNDER_AND_HAIL:
                return 0x05;
            case SLEET:
                return 0x06;
            case LIGHT_RAIN:
                return 0x07;
            case RAIN:
                return 0x08;
            case HEAVY_RAIN:
                return 0x09;
            case RAIN_STORM:
                return 0x0a;
            case HEAVY_RAIN_STORMS:
                return 0x0b;
            case SEVERE_RAIN_STORMS:
                return 0x0c;
            case SNOW_FLURRIES:
                return 0x0d;
            case LIGHT_SNOW:
                return 0x0e;
            case SNOW:
                return 0x0f;
            case HEAVY_SNOW:
                return 0x10;
            case SNOWSTORMS:
                return 0x11;
            case FOG:
                return 0x12;
            case FREEZING_RAIN:
                return 0x13;
            case DUST_STORM:
                return 0x14;
            case LIGHT_TO_MODERATE_RAIN:
                return 0x15;
            case MODERATE_TO_HEAVY_RAIN:
                return 0x16;
            case HEAVY_TO_SEVERE_RAIN:
                return 0x17;
            case HEAVY_TO_TORRENTIAL_RAIN:
                return 0x18;
            case SEVERE_TO_TORRENTIAL_RAIN:
                return 0x19;
            case LIGHT_TO_MODERATE_SNOW:
                return 0x1a;
            case MODERATE_TO_HEAVY_SNOW:
                return 0x1b;
            case HEAVY_SNOW_TO_BLIZZARD:
                return 0x1c;
            case DUST:
                return 0x1d;
            case SAND:
                return 0x1e;
            case SANDSTORMS:
                return 0x1f;
            case FREEZING:
                return 0x20;
            case HOT:
                return 0x21;
            case COLD:
                return 0x22;
            case WINDY:
                return 0x23;
            case HAZY:
                return 0x35;
            default:
                return 0x63; // Any higher and the current weather breaks
        }
    }

    private static WeatherIcon byteToIcon(byte weatherIcon) {
        switch (weatherIcon) {
            case 0x00:
                return WeatherIcon.SUNNY;
            case 0x01:
                return WeatherIcon.CLOUDY;
            case 0x02:
                return WeatherIcon.OVERCAST;
            case 0x03:
                return WeatherIcon.SHOWERS;
            case 0x04:
                return WeatherIcon.THUNDERSTORMS;
            case 0x05:
                return WeatherIcon.THUNDER_AND_HAIL;
            case 0x06:
                return WeatherIcon.SLEET;
            case 0x07:
                return WeatherIcon.LIGHT_RAIN;
            case 0x08:
                return WeatherIcon.RAIN;
            case 0x09:
                return WeatherIcon.HEAVY_RAIN;
            case 0x0a:
                return WeatherIcon.RAIN_STORM;
            case 0x0b:
                return WeatherIcon.HEAVY_RAIN_STORMS;
            case 0x0c:
                return WeatherIcon.SEVERE_RAIN_STORMS;
            case 0x0d:
                return WeatherIcon.SNOW_FLURRIES;
            case 0x0e:
                return WeatherIcon.LIGHT_SNOW;
            case 0x0f:
                return WeatherIcon.SNOW;
            case 0x10:
                return WeatherIcon.HEAVY_SNOW;
            case 0x11:
                return WeatherIcon.SNOWSTORMS;
            case 0x12:
                return WeatherIcon.FOG;
            case 0x13:
                return WeatherIcon.FREEZING_RAIN;
            case 0x14:
                return WeatherIcon.DUST_STORM;
            case 0x15:
                return WeatherIcon.LIGHT_TO_MODERATE_RAIN;
            case 0x16:
                return WeatherIcon.MODERATE_TO_HEAVY_RAIN;
            case 0x17:
                return WeatherIcon.HEAVY_TO_SEVERE_RAIN;
            case 0x18:
                return WeatherIcon.HEAVY_TO_TORRENTIAL_RAIN;
            case 0x19:
                return WeatherIcon.SEVERE_TO_TORRENTIAL_RAIN;
            case 0x1a:
                return WeatherIcon.LIGHT_TO_MODERATE_SNOW;
            case 0x1b:
                return WeatherIcon.MODERATE_TO_HEAVY_SNOW;
            case 0x1c:
                return WeatherIcon.HEAVY_SNOW_TO_BLIZZARD;
            case 0x1d:
                return WeatherIcon.DUST;
            case 0x1e:
                return WeatherIcon.SAND;
            case 0x1f:
                return WeatherIcon.SANDSTORMS;
            case 0x20:
                return WeatherIcon.FREEZING;
            case 0x21:
                return WeatherIcon.HOT;
            case 0x22:
                return WeatherIcon.COLD;
            case 0x23:
                return WeatherIcon.WINDY;
            case 0x35:
                return WeatherIcon.HAZY;
            default:
                return WeatherIcon.UNKNOWN;
        }
    }

    public enum HuaweiTemperatureFormat {
        CELSIUS,
        FAHRENHEIT
    }

    private static byte temperatureFormatToByte(HuaweiTemperatureFormat temperatureFormat) {
        if (temperatureFormat == HuaweiTemperatureFormat.FAHRENHEIT)
            return 1;
        return 0;
    }

    public enum MoonPhase {
        NEW_MOON,
        WAXING_CRESCENT,
        FIRST_QUARTER,
        WAXING_GIBBOUS,
        FULL_MOON,
        WANING_GIBBOUS,
        THIRD_QUARTER,
        WANING_CRESCENT
    }

    public static MoonPhase degreesToMoonPhase(int degrees) {
        final int leeway = 6; // Give some leeway for the new moon, first quarter, full moon, and third quarter
        if (degrees < 0 || degrees > 360)
            return null;
        else if (degrees >= 360 - leeway || degrees <= leeway)
            return MoonPhase.NEW_MOON;
        else if (degrees < 90)
            return MoonPhase.WAXING_CRESCENT;
        else if (degrees <= 90 + leeway)
            return MoonPhase.FIRST_QUARTER;
        else if (degrees < 180 - leeway)
            return MoonPhase.WAXING_GIBBOUS;
        else if (degrees <= 180 + leeway)
            return MoonPhase.FULL_MOON;
        else if (degrees < 270 - leeway)
            return MoonPhase.WANING_GIBBOUS;
        else if (degrees <= 270 + leeway)
            return MoonPhase.THIRD_QUARTER;
        else
            return MoonPhase.WANING_CRESCENT;
    }

    private static byte moonPhaseToByte (MoonPhase moonPhase) {
        switch (moonPhase) {
            case NEW_MOON:
                return 1;
            case WAXING_CRESCENT:
                return 2;
            case FIRST_QUARTER:
                return 3;
            case WAXING_GIBBOUS:
                return 4;
            case FULL_MOON:
                return 5;
            case WANING_GIBBOUS:
                return 6;
            case THIRD_QUARTER:
                return 7;
            case WANING_CRESCENT:
                return 8;
            default:
                return -1;
        }
    }

    private static MoonPhase byteToMoonPhase(byte moonPhase) {
        switch (moonPhase) {
            case 1:
                return MoonPhase.NEW_MOON;
            case 2:
                return MoonPhase.WAXING_CRESCENT;
            case 3:
                return MoonPhase.FIRST_QUARTER;
            case 4:
                return MoonPhase.WAXING_GIBBOUS;
            case 5:
                return MoonPhase.FULL_MOON;
            case 6:
                return MoonPhase.WANING_GIBBOUS;
            case 7:
                return MoonPhase.THIRD_QUARTER;
            case 8:
                return MoonPhase.WANING_CRESCENT;
            default:
                return null;
        }
    }

    public static class CurrentWeatherRequest extends HuaweiPacket {
        public static final byte id = 0x01;

        public CurrentWeatherRequest(
                ParamsProvider paramsProvider,
                Settings settings,
                WeatherIcon icon,
                Byte windDirection,
                Byte windSpeed,
                Byte lowestTemperature,
                Byte highestTemperature,
                Short pm25, // TODO: might be float?
                String locationName,
                Byte currentTemperature,
                HuaweiTemperatureFormat temperatureUnit,
                Short airQualityIndex,
                Integer observationTime,
                Float uvIndex,
                String sourceName
        ) {
            super(paramsProvider);

            this.serviceId = Weather.id;
            this.commandId = id;
            this.tlv = new HuaweiTLV();

            HuaweiTLV tlv81 = new HuaweiTLV();

            if (icon != null && settings.weatherIconSupported) {
                tlv81.put(0x02, iconToByte(icon));
            }

            if (settings.windSupported) {
                short wind = 0;
                if (windSpeed != null)
                    wind = (short) windSpeed;

                if (windDirection != null) {
                    if (windDirection > 0)
                        wind |= ((short) (windDirection * 8 / 360)) << 8;
                    else
                        wind |= ((short) (360 + windDirection) * 8 / 360) << 8;
                }
                tlv81.put(0x03, wind);
            }

            if (settings.weatherIconSupported || settings.windSupported)
                this.tlv.put(0x81, tlv81);

            if (lowestTemperature != null && highestTemperature != null && settings.temperatureSupported) {
                this.tlv.put(0x85, new HuaweiTLV()
                        .put(0x06, lowestTemperature)
                        .put(0x07, highestTemperature)
                );
            }
            if (windDirection != null && windSpeed != null && settings.windSupported)
                this.tlv.put(0x03, (short) ((((short) windDirection) << 8) | ((short) windSpeed)));
            if (pm25 != null && settings.pm25Supported)
                this.tlv.put(0x04, pm25);
            if (locationName != null && settings.locationNameSupported)
                this.tlv.put(0x08, locationName);
            if (currentTemperature != null && settings.currentTemperatureSupported)
                this.tlv.put(0x09, currentTemperature);
            if (temperatureUnit != null && settings.unitSupported)
                this.tlv.put(0x0a, temperatureFormatToByte(temperatureUnit));
            if (airQualityIndex != null && settings.airQualityIndexSupported)
                this.tlv.put(0x0b, airQualityIndex);
            if (observationTime != null && settings.timeSupported)
                this.tlv.put(0x0c, observationTime);
            if (sourceName != null && settings.sourceSupported)
                this.tlv.put(0x0e, sourceName);
            if (uvIndex != null && settings.uvIndexSupported)
                this.tlv.put(0x0f, uvIndex.byteValue());

            this.isEncrypted = true;
            this.complete = true;
        }
    }

    public static class WeatherSupport {
        public static final byte id = 0x02;

        public static class Request extends HuaweiPacket {
            public Request(ParamsProvider paramsProvider) {
                super(paramsProvider);

                this.serviceId = Weather.id;
                this.commandId = id;
                this.tlv = new HuaweiTLV().put(0x01);
                this.isEncrypted = true;
                this.complete = true;
            }
        }

        public static class Response extends HuaweiPacket {
            public byte supportedBitmap = 0;

            public boolean weatherSupported = false;
            public boolean windSupported = false;
            public boolean pm25Supported = false;
            public boolean temperatureSupported = false;
            public boolean locationNameSupported = false;
            public boolean currentTemperatureSupported = false;
            public boolean unitSupported = false;
            public boolean airQualityIndexSupported = false;

            public Response(ParamsProvider paramsProvider) {
                super(paramsProvider);
                this.serviceId = Weather.id;
                this.commandId = id;
            }

            @Override
            public void parseTlv() throws ParseException {
                if (!this.tlv.contains(0x01))
                    throw new MissingTagException(0x01);
                this.supportedBitmap = this.tlv.getByte(0x01);

                this.weatherSupported = (this.supportedBitmap & 0x01) != 0;
                this.windSupported = (this.supportedBitmap & 0x02) != 0;
                this.pm25Supported = (this.supportedBitmap & 0x04) != 0;
                this.temperatureSupported = (this.supportedBitmap & 0x08) != 0;
                this.locationNameSupported = (this.supportedBitmap & 0x10) != 0;
                this.currentTemperatureSupported = (this.supportedBitmap & 0x20) != 0;
                this.unitSupported = (this.supportedBitmap & 0x40) != 0;
                this.airQualityIndexSupported = (this.supportedBitmap & 0x80) != 0;
            }
        }
    }

    public static class WeatherDeviceRequest extends HuaweiPacket {
        public static final byte id = 0x04;

        public WeatherDeviceRequest(ParamsProvider paramsProvider, int responseValue) {
            super(paramsProvider);

            this.serviceId = Weather.id;
            this.commandId = id;
            this.tlv = new HuaweiTLV().put(0x01, responseValue);
            this.isEncrypted = false;
            this.complete = true;
        }
    }

    public static class WeatherUnitRequest extends HuaweiPacket {
        public static final byte id = 0x05;

        public WeatherUnitRequest(ParamsProvider paramsProvider, HuaweiTemperatureFormat temperatureFormat) {
            super(paramsProvider);

            this.serviceId = Weather.id;
            this.commandId = id;
            this.tlv = new HuaweiTLV().put(0x01, temperatureFormatToByte(temperatureFormat));
            this.isEncrypted = true;
            this.complete = true;
        }
    }

    public static class WeatherExtendedSupport {
        public static final byte id = 0x06;

        public static class Request extends HuaweiPacket {
            public Request(ParamsProvider paramsProvider) {
                super(paramsProvider);

                this.serviceId = Weather.id;
                this.commandId = id;
                this.tlv = new HuaweiTLV().put(0x01);
                this.isEncrypted = true;
                this.complete = true;
            }
        }

        public static class Response extends HuaweiPacket {
            public short supportedBitmap = 0;

            public boolean timeSupported = false;
            public boolean sourceSupported = false;
            public boolean weatherIconSupported = false;

            public Response(ParamsProvider paramsProvider) {
                super(paramsProvider);
                this.serviceId = Weather.id;
                this.commandId = id;
            }

            @Override
            public void parseTlv() throws ParseException {
                if (!this.tlv.contains(0x01))
                    throw new MissingTagException(0x01);
                this.supportedBitmap = this.tlv.getShort(0x01);

                this.timeSupported = (this.supportedBitmap & 0x01) != 0;
                this.sourceSupported = (this.supportedBitmap & 0x02) != 0;
                this.weatherIconSupported = (this.supportedBitmap & 0x04) != 0;
            }
        }
    }

    public static class WeatherForecastData {
        public static final byte id = 0x08;

        public static class TimeData {
            public int timestamp;
            public WeatherIcon icon;
            public Byte temperature;

            @Override
            public String toString() {
                String timestampStr = new Date(timestamp * 1000L).toString();
                return "TimeData{" +
                        "timestamp=" + timestamp +
                        ", timestamp=" + timestampStr +
                        ", icon=" + icon +
                        ", temperature=" + temperature +
                        '}';
            }
        }

        public static class DayData {
            public int timestamp;
            public WeatherIcon icon;
            public Byte highTemperature;
            public Byte lowTemperature;
            public Integer sunriseTime;
            public Integer sunsetTime;
            public Integer moonRiseTime;
            public Integer moonSetTime;
            public MoonPhase moonPhase;

            @Override
            public String toString() {
                String timestampStr = new Date(timestamp * 1000L).toString();
                return "DayData{" +
                        "timestamp=" + timestamp +
                        ", timestamp=" + timestampStr +
                        ", icon=" + icon +
                        ", highTemperature=" + highTemperature +
                        ", lowTemperature=" + lowTemperature +
                        ", sunriseTime=" + sunriseTime +
                        ", sunsetTime=" + sunsetTime +
                        ", moonRiseTime=" + moonRiseTime +
                        ", moonSetTime=" + moonSetTime +
                        ", moonPhase=" + moonPhase +
                        '}';
            }
        }

        public static class Request extends HuaweiPacket {
            public Request(
                    ParamsProvider paramsProvider,
                    Settings settings,
                    List<TimeData> timeDataList,
                    List<DayData> dayDataList
            ) {
                super(paramsProvider);

                this.serviceId = Weather.id;
                this.commandId = id;
                this.tlv = new HuaweiTLV();

                if (timeDataList != null && !timeDataList.isEmpty()) {
                    HuaweiTLV timeDataTlv = new HuaweiTLV();
                    for (TimeData timeData : timeDataList) {
                        HuaweiTLV timeTlv = new HuaweiTLV();
                        timeTlv.put(0x03, timeData.timestamp);
                        if (timeData.icon != null && settings.weatherIconSupported)
                            timeTlv.put(0x04, iconToByte(timeData.icon));
                        if (timeData.temperature != null && (settings.temperatureSupported || settings.currentTemperatureSupported))
                            timeTlv.put(0x05, timeData.temperature);
                        timeDataTlv.put(0x82, timeTlv);
                    }
                    this.tlv.put(0x81, timeDataTlv);
                }

                if (dayDataList != null && !dayDataList.isEmpty()) {
                    HuaweiTLV dayDataTlv = new HuaweiTLV();
                    for (DayData dayData : dayDataList) {
                        HuaweiTLV dayTlv = new HuaweiTLV();
                        dayTlv.put(0x12, dayData.timestamp);
                        if (dayData.icon != null && settings.weatherIconSupported)
                            dayTlv.put(0x13, iconToByte(dayData.icon));
                        if (settings.temperatureSupported) {
                            if (dayData.highTemperature != null)
                                dayTlv.put(0x14, dayData.highTemperature);
                            if (dayData.lowTemperature != null)
                                dayTlv.put(0x15, dayData.lowTemperature);
                        }
                        if (settings.sunRiseSetSupported) {
                            if (dayData.sunriseTime != null && dayData.sunriseTime != 0)
                                dayTlv.put(0x16, dayData.sunriseTime);
                            if (dayData.sunsetTime != null && dayData.sunsetTime != 0)
                                dayTlv.put(0x17, dayData.sunsetTime);
                            if (dayData.moonRiseTime != null && dayData.moonRiseTime != 0)
                                dayTlv.put(0x1a, dayData.moonRiseTime);
                            if (dayData.moonSetTime != null && dayData.moonSetTime != 0)
                                dayTlv.put(0x1b, dayData.moonSetTime);
                        }
                        if (dayData.moonPhase != null && settings.moonPhaseSupported)
                            dayTlv.put(0x1e, moonPhaseToByte(dayData.moonPhase));
                        dayDataTlv.put(0x91, dayTlv);
                    }
                    this.tlv.put(0x90, dayDataTlv);
                }

                this.isEncrypted = true;
                this.isSliced = true;
                this.complete = true;
            }
        }

        public static class OutgoingRequest extends HuaweiPacket {
            List<TimeData> timeDataList;
            List<DayData> dayDataList;

            public OutgoingRequest(ParamsProvider paramsProvider) {
                super(paramsProvider);
                this.complete = false;
            }

            @Override
            public void parseTlv() throws ParseException {
                timeDataList = new ArrayList<>(this.tlv.getObject(0x81).getObjects(0x82).size());
                for (HuaweiTLV timeTlv : this.tlv.getObject(0x81).getObjects(0x82)) {
                    TimeData timeData = new TimeData();
                    timeData.timestamp = timeTlv.getInteger(0x03);
                    timeData.icon = byteToIcon(timeTlv.getByte(0x04));
                    timeData.temperature = timeTlv.getByte(0x05);
                    timeDataList.add(timeData);
                }

                dayDataList = new ArrayList<>(this.tlv.getObject(0x90).getObjects(0x91).size());
                for (HuaweiTLV dayTlv : this.tlv.getObject(0x90).getObjects(0x91)) {
                    DayData dayData = new DayData();
                    dayData.timestamp = dayTlv.getInteger(0x12);
                    dayData.icon = byteToIcon(dayTlv.getByte(0x13));
                    dayData.highTemperature = dayTlv.getByte(0x14);
                    dayData.lowTemperature = dayTlv.getByte(0x15);
                    dayData.sunriseTime = dayTlv.getInteger(0x16);
                    dayData.sunsetTime = dayTlv.getInteger(0x17);
                    dayData.moonRiseTime = dayTlv.getInteger(0x1a);
                    dayData.moonSetTime = dayTlv.getInteger(0x1b);
                    dayData.moonPhase = byteToMoonPhase(dayTlv.getByte(0x1e));
                    dayDataList.add(dayData);
                }
            }
        }
    }

    public static class WeatherStart {
        public static final byte id = 0x09;

        public static class Request extends HuaweiPacket {

            public Request(ParamsProvider paramsProvider) {
                super(paramsProvider);

                this.serviceId = Weather.id;
                this.commandId = id;
                this.tlv = new HuaweiTLV().put(0x01, (byte) 0x03); // TODO: find out what this means
                this.isEncrypted = true;
                this.complete = true;
            }
        }

        public static class Response extends HuaweiPacket {
            public int successCode = -1;
            public boolean success = false;

            public Response(ParamsProvider paramsProvider) {
                super(paramsProvider);
                this.serviceId = Weather.id;
                this.commandId = id;
            }

            @Override
            public void parseTlv() throws ParseException {
                this.successCode = this.tlv.getInteger(0x7f);
                this.success = this.successCode == 0x000186A0 || this.successCode == 0x000186A3;
            }
        }
    }

    public static class WeatherSunMoonSupport {
        public static final byte id = 0x0a;

        public static class Request extends HuaweiPacket {
            public Request(ParamsProvider paramsProvider) {
                super(paramsProvider);

                this.serviceId = Weather.id;
                this.commandId = id;
                this.tlv = new HuaweiTLV().put(0x01);
                this.isEncrypted = true;
                this.complete = true;
            }
        }

        public static class Response extends HuaweiPacket {
            public int supportedBitmap = 0;

            public boolean sunRiseSetSupported = false;
            public boolean moonPhaseSupported = false;

            public Response(ParamsProvider paramsProvider) {
                super(paramsProvider);
                this.serviceId = Weather.id;
                this.commandId = id;
            }

            @Override
            public void parseTlv() throws ParseException {
                if (!this.tlv.contains(0x01))
                    throw new MissingTagException(0x01);
                this.supportedBitmap = this.tlv.getInteger(0x01);

                this.sunRiseSetSupported = (this.supportedBitmap & 0x01) != 0;
                this.moonPhaseSupported = (this.supportedBitmap & 0x02) != 0;
            }
        }
    }
}
