/*  Copyright (C) 2023-2024 José Rebelo

    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.service.devices.xiaomi.activity.impl;

import android.content.Context;
import android.widget.Toast;

import androidx.annotation.Nullable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Date;

import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.export.AutoGpxExporter;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileFetcher;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
import nodomain.freeyourgadget.gadgetbridge.util.GB;

public class WorkoutGpsParser extends XiaomiActivityParser {
    private static final Logger LOG = LoggerFactory.getLogger(WorkoutGpsParser.class);

    @Nullable
    public ActivityTrack getActivityTrack(final XiaomiActivityFileId fileId, final byte[] bytes) {
        final int version = fileId.getVersion();
        final int headerSize;
        final int sampleSize;
        switch (version) {
            case 1:
                headerSize = 1;
                sampleSize = 12;
                break;
            case 2:
                headerSize = 1;
                sampleSize = 18;
                break;
            default:
                LOG.warn("Unable to parse workout gps version {}", fileId.getVersion());
                return null;
        }

        final ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
        buf.limit(buf.limit() - 4); // discard crc at the end
        buf.get(new byte[7]); // skip fileId bytes
        final byte fileIdPadding = buf.get();
        if (fileIdPadding != 0) {
            LOG.warn("Expected 0 padding after fileId, got {} - parsing might fail", fileIdPadding);
        }
        final byte[] header = new byte[headerSize];
        buf.get(header);

        LOG.debug("Workout gps Header: {}", GB.hexdump(header));

        if ((buf.limit() - buf.position()) % sampleSize != 0) {
            LOG.warn("Remaining data in the buffer is not a multiple of {}", sampleSize);
        }

        final ActivityTrack activityTrack = new ActivityTrack();

        // GPS V1 contains no speed data, therefore second while loop to avoid too many ifs within the loop
        if (version == 1) {
            while (buf.position() < buf.limit()) {
                final int ts = buf.getInt();
                final float longitude = buf.getFloat();
                final float latitude = buf.getFloat();

                final ActivityPoint ap = new ActivityPoint(new Date(ts * 1000L));
                ap.setLocation(new GPSCoordinate(longitude, latitude, 0));
                activityTrack.addTrackPoint(ap);
                LOG.trace("ActivityPoint V1: ts={} lon={} lat={}", ts, longitude, latitude);
            }
        } else {
            while (buf.position() < buf.limit()) {
                final int ts = buf.getInt();
                final float longitude = buf.getFloat();
                final float latitude = buf.getFloat();
                final float hdop = buf.getFloat() / 4.8f;
                final float speed = (buf.getShort() >> 2) / 10.0f;

                final ActivityPoint ap = new ActivityPoint(new Date(ts * 1000L));
                final GPSCoordinate gpsc = new GPSCoordinate(longitude, latitude);
                gpsc.setHdop(hdop);
                ap.setLocation(gpsc);

                activityTrack.addTrackPoint(ap);
                LOG.trace("ActivityPoint: ts={} lon={} lat={} hdop={} speed={}", ts, longitude, latitude, hdop, speed);
            }
        }

        return activityTrack;
    }

    @Override
    public boolean parse(final Context context, final GBDevice gbDevice, final XiaomiActivityFileId fileId, final byte[] bytes) {
        final ActivityTrack activityTrack = getActivityTrack(fileId, bytes);
        if (activityTrack == null) {
            return false;
        }

        final BaseActivitySummary summary;
        try (DBHandler dbHandler = GBApplication.acquireDB()) {
            final DaoSession session = dbHandler.getDaoSession();
            final Device device = DBHelper.getDevice(gbDevice, session);
            final User user = DBHelper.getUser(session);

            // Find the matching summary
            summary = findOrCreateBaseActivitySummary(session, device, user, fileId);

            // Set the info on the activity track
            activityTrack.setUser(user);
            activityTrack.setDevice(device);
            activityTrack.setName(ActivityKind.fromCode(summary.getActivityKind()).getLabel(context));

            // The file was already persisted by XiaomiActivityFileFetcher - just use the existing file
            final File rawBytesFile = XiaomiActivityFileFetcher.getRawFile(gbDevice, fileId);

            // Save the gpx file
            if (rawBytesFile != null) {
                summary.setRawDetailsPath(rawBytesFile.getAbsolutePath());
            }
            session.getBaseActivitySummaryDao().insertOrReplace(summary);
        } catch (final Exception e) {
            GB.toast(context, "Error saving workout gps", Toast.LENGTH_LONG, GB.ERROR, e);
            return false;
        }

        AutoGpxExporter.doExport(context, gbDevice, summary, activityTrack);

        return true;
    }
}
