/*  Copyright (C) 2024 Vitalii Tomin, 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.List;

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

public class GpsAndTime {
    public static final byte id = 0x18;

    public static class GpsParameters {
        public static final byte id = 0x01;

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

                this.serviceId = GpsAndTime.id;
                this.commandId = id;

                this.tlv = new HuaweiTLV().put(0x81);

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

        public static class Response extends HuaweiPacket {
            public byte supportBitmap; // Supported bitmap, parsed into the booleans below
            public byte format = 0; // 1 to send a single location per message (id 3), 2 for multiple
            public byte locationsPerMessage = 0; // Only for format 2, the number of locations to send per message
            public byte threshold = 0;

            public boolean supportsSpeed;
            public boolean supportsDistance;
            public boolean supportsAltitude;
            public boolean supportsTotalDistance;
            public boolean supportsLatLon;
            public boolean supportsMarsLatLon;
            public boolean supportsDirection;
            public boolean supportsPrecision;

            public Response(ParamsProvider paramsProvider) {
                super(paramsProvider);
            }

            @Override
            public void parseTlv() throws ParseException {
                HuaweiTLV subTlv = this.tlv.getObject(0x81);

                supportBitmap = subTlv.getByte(0x02);
                supportsSpeed = (supportBitmap & 0x01) != 0;
                supportsDistance = (supportBitmap & 0x02) != 0;
                supportsAltitude = (supportBitmap & 0x04) != 0;
                supportsTotalDistance = (supportBitmap & 0x08) != 0;
                supportsLatLon = (supportBitmap & 0x10) != 0;
                supportsMarsLatLon = (supportBitmap & 0x20) != 0;
                supportsDirection = (supportBitmap & 0x40) != 0;
                supportsPrecision = (supportBitmap & 0x80) != 0;

                if (subTlv.contains(0x03))
                    format = subTlv.getByte(0x03);
                if (subTlv.contains(0x04))
                    locationsPerMessage = subTlv.getByte(0x04);
                if (subTlv.contains(0x05))
                    threshold = subTlv.getByte(0x05);
            }
        }
    }

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

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

                this.serviceId = GpsAndTime.id;
                this.commandId = id;

                // Currently only sends "OK" value
                this.tlv = new HuaweiTLV().put(0x7f, 0x186A0);

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

        public static class Response extends HuaweiPacket {
            public boolean enableGps;

            public Response(ParamsProvider paramsProvider) {
                super(paramsProvider);
            }

            @Override
            public void parseTlv() throws ParseException {
                enableGps = this.tlv.getByte(0x01) == 0x01;
            }
        }
    }

    public static class GpsData {
        public static final byte id = 0x03;

        public static class Request extends HuaweiPacket {
            public static class GpsDataContainer {
                public boolean hasSpeed = false;
                public short speed;

                public boolean hasDistance = false;
                public int distance;

                public boolean hasAltitude = false;
                public short altitude;

                public boolean hasTotalDistance = false;
                public int totalDistance;

                public boolean hasStartTime = false;
                public int startTime;

                public boolean hasEndTime = false;
                public int endTime;

                public boolean hasLatLon = false;
                public double lat;
                public double lon;

                public boolean hasMarsLatLon = false;
                public double marsLat;
                public double marsLon;

                public boolean hasBearing = false;
                public double bearing;

                public boolean hasAccuracy = false;
                public double accuracy;
            }

            public Request(
                    ParamsProvider paramsProvider,
                    List<GpsDataContainer> gpsDataList
            ) {
                super(paramsProvider);

                this.serviceId = GpsAndTime.id;
                this.commandId = id;

                HuaweiTLV subTlv = new HuaweiTLV();
                for (GpsDataContainer gpsData : gpsDataList) {
                    HuaweiTLV gpsTlv = new HuaweiTLV();
                    if (gpsData.hasSpeed)
                        gpsTlv.put(0x03, gpsData.speed);
                    if (gpsData.hasDistance)
                        gpsTlv.put(0x04, gpsData.distance);
                    if (gpsData.hasAltitude)
                        gpsTlv.put(0x05, gpsData.altitude);
                    if (gpsData.hasTotalDistance)
                        gpsTlv.put(0x06, gpsData.totalDistance);
                    if (gpsData.hasStartTime)
                        gpsTlv.put(0x07, gpsData.startTime);
                    if (gpsData.hasEndTime)
                        gpsTlv.put(0x08, gpsData.endTime);
                    if (gpsData.hasLatLon) {
                        gpsTlv.put(0x09, gpsData.lat);
                        gpsTlv.put(0x0a, gpsData.lon);
                    }
                    if (gpsData.hasMarsLatLon) {
                        gpsTlv.put(0x0b, gpsData.marsLat);
                        gpsTlv.put(0x0c, gpsData.marsLon);
                    }
                    if (gpsData.hasBearing)
                        gpsTlv.put(0x0d, gpsData.bearing);
                    if (gpsData.hasAccuracy)
                        gpsTlv.put(0x0e, gpsData.accuracy);
                    subTlv.put(0x82, gpsTlv);
                }
                this.tlv = new HuaweiTLV().put(0x81, subTlv);

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

        public static class Response extends HuaweiPacket {
            boolean shouldStop;

            public Response(ParamsProvider paramsProvider) {
                super(paramsProvider);
            }

            @Override
            public void parseTlv() throws ParseException {
                // If the response is not 0x000186A0, sending should be stopped
                shouldStop = this.tlv.getInteger(0x7f) != 0x000186A0;
            }
        }
    }

    public static class CurrentGPSRequest extends HuaweiPacket {
        public static final byte id = 0x07;
        public CurrentGPSRequest (
                ParamsProvider paramsProvider,
                int timestamp,
                double lat,
                double lon
        ) {
            super(paramsProvider);

            this.serviceId = GpsAndTime.id;
            this.commandId = id;
            this.tlv = new HuaweiTLV()
                    .put(0x01, timestamp)
                    .put(0x02, lon)
                    .put(0x03, lat);
            this.isEncrypted = true;
            this.complete = true;
        }
    }
}
