package com.radio.codec2talkie.protocol;

import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;

import androidx.preference.PreferenceManager;

import com.radio.codec2talkie.protocol.aprs.AprsCallsign;
import com.radio.codec2talkie.protocol.aprs.AprsData;
import com.radio.codec2talkie.protocol.aprs.AprsDataFactory;
import com.radio.codec2talkie.protocol.aprs.AprsDataType;
import com.radio.codec2talkie.protocol.message.TextMessage;
import com.radio.codec2talkie.protocol.position.Position;
import com.radio.codec2talkie.protocol.ax25.AX25Callsign;
import com.radio.codec2talkie.settings.PreferenceKeys;
import com.radio.codec2talkie.settings.SettingsWrapper;
import com.radio.codec2talkie.transport.Transport;

import java.io.IOException;

public class Aprs implements Protocol {
    public static final String APRS_ID = "APCTLK";

    private static final String TAG = Aprs.class.getSimpleName();

    private final Protocol _childProtocol;

    private ProtocolCallback _parentProtocolCallback;

    private String _srcCallsign;
    private String _dstCallsign;
    private String _symbolCode;
    private String _status;
    private String _comment;
    private int _miceDigipath;
    private boolean _isVoax25Enabled;
    private boolean _isCompressed;
    private boolean _isBearingCourseEnabled;
    private boolean _isAltitudeEnabled;
    private int _privacyLevel;

    private AprsDataType _positionDataType;

    public Aprs(Protocol childProtocol) {
        _childProtocol = childProtocol;
    }

    @Override
    public void initialize(Transport transport, Context context, ProtocolCallback protocolCallback) throws IOException {
        _parentProtocolCallback = protocolCallback;
        _childProtocol.initialize(transport, context, _protocolCallback);

        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        _isVoax25Enabled = SettingsWrapper.isVoax25Enabled(sharedPreferences);

        _srcCallsign = AX25Callsign.formatCallsign(
                sharedPreferences.getString(PreferenceKeys.AX25_CALLSIGN, "NOCALL").toUpperCase(),
                sharedPreferences.getString(PreferenceKeys.AX25_SSID, "0"));
        _dstCallsign = APRS_ID;

        _symbolCode = sharedPreferences.getString(PreferenceKeys.APRS_SYMBOL, "/[");
        String packetFormat = sharedPreferences.getString(PreferenceKeys.APRS_LOCATION_PACKET_FORMAT, "uncompressed");
        _status = sharedPreferences.getString(PreferenceKeys.APRS_LOCATION_MIC_E_MESSAGE_TYPE, "off_duty");
        _comment = sharedPreferences.getString(PreferenceKeys.APRS_COMMENT, "off_duty");
        _privacyLevel = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.APRS_PRIVACY_POSITION_AMBIGUITY, "0"));
        _isAltitudeEnabled = sharedPreferences.getBoolean(PreferenceKeys.APRS_PRIVACY_ALTITUDE_ENABLED, false);
        _isBearingCourseEnabled = sharedPreferences.getBoolean(PreferenceKeys.APRS_PRIVACY_SPEED_ENABLED, false);
        _miceDigipath = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.APRS_LOCATION_MIC_E_DIGIPATH, "0"));
        _isCompressed = packetFormat.equals("compressed");

        AprsDataType.DataType dataType = packetFormat.equals("mic_e") ?
                AprsDataType.DataType.MIC_E : AprsDataType.DataType.POSITION_WITHOUT_TIMESTAMP_MSG;
        _positionDataType = new AprsDataType(dataType);
    }

    @Override
    public int getPcmAudioRecordBufferSize() {
        return _childProtocol.getPcmAudioRecordBufferSize();
    }

    @Override
    public void sendCompressedAudio(String src, String dst, byte[] frame) throws IOException {
        _childProtocol.sendCompressedAudio(src, dst, frame);
    }

    @Override
    public void sendTextMessage(TextMessage textMessage) throws IOException {
        AprsDataType aprsDataType = new AprsDataType(AprsDataType.DataType.MESSAGE);
        AprsData aprsData = AprsDataFactory.create(aprsDataType);
        assert aprsData != null;
        aprsData.fromTextMessage(textMessage);
        if (aprsData.isValid()) {
            textMessage.src = _srcCallsign;
            sendData(_srcCallsign, _dstCallsign, null, aprsData.toBinary());
            _parentProtocolCallback.onTransmitTextMessage(textMessage);
        } else {
            Log.e(TAG, "Invalid APRS message");
            _parentProtocolCallback.onProtocolTxError();
        }
    }

    @Override
    public void sendPcmAudio(String src, String dst, short[] pcmFrame) throws IOException {
        if (_isVoax25Enabled) {
            _childProtocol.sendPcmAudio(src == null ? _srcCallsign : src, dst == null ? _dstCallsign : dst, pcmFrame);
        } else {
            _childProtocol.sendPcmAudio(src, dst, pcmFrame);
        }
    }

    @Override
    public void sendData(String src, String dst, String path, byte[] dataPacket) throws IOException {
        _childProtocol.sendData(src == null ? _srcCallsign : src, dst == null ? _dstCallsign : dst, path, dataPacket);
    }

    @Override
    public boolean receive() throws IOException {
        return _childProtocol.receive();
    }

    ProtocolCallback _protocolCallback = new ProtocolCallback() {
        @Override
        protected void onReceivePosition(Position position) {
            _parentProtocolCallback.onReceivePosition(position);
        }

        @Override
        protected void onReceivePcmAudio(String src, String dst, short[] pcmFrame) {
            String dstCallsign = new AprsCallsign(dst).isSoftware() ? "*" : dst;
            _parentProtocolCallback.onReceivePcmAudio(src, dstCallsign, pcmFrame);
        }

        @Override
        protected void onReceiveCompressedAudio(String src, String dst, byte[] audioFrame) {
            _parentProtocolCallback.onReceiveCompressedAudio(src, dst, audioFrame);
        }

        @Override
        protected void onReceiveTextMessage(TextMessage textMessage) {
            _parentProtocolCallback.onReceiveTextMessage(textMessage);
        }

        @Override
        protected void onReceiveData(String src, String dst, String path, byte[] data) {
            if (data.length == 0) return;
            AprsData aprsData = AprsDataFactory.fromBinary(src, dst, path, data);
            if (aprsData != null && aprsData.isValid()) {
                if (aprsData.isTextMessage()) {
                    TextMessage textMessage = aprsData.toTextMessage();
                    _parentProtocolCallback.onReceiveTextMessage(textMessage);
                    return;
                } else if (aprsData.isPositionReport()) {
                    Position position = aprsData.toPosition();
                    if (position != null) {
                        _parentProtocolCallback.onReceivePosition(position);
                        return;
                    }
                }
            }
            _parentProtocolCallback.onReceiveData(src, dst, path, data);
        }

        @Override
        protected void onReceiveSignalLevel(short rssi, short snr) {
            _parentProtocolCallback.onReceiveSignalLevel(rssi, snr);
        }

        @Override
        protected void onReceiveTelemetry(int batVoltage) {
            _parentProtocolCallback.onReceiveTelemetry(batVoltage);
        }

        @Override
        protected void onReceiveLog(String logData) {
            _parentProtocolCallback.onReceiveLog(logData);
        }

        @Override
        protected void onTransmitPcmAudio(String src, String dst, short[] frame) {
            String dstCallsign = new AprsCallsign(dst).isSoftware() ? "*" : dst;
            _parentProtocolCallback.onTransmitPcmAudio(src, dstCallsign, frame);
        }

        @Override
        protected void onTransmitCompressedAudio(String src, String dst, byte[] frame) {
            _parentProtocolCallback.onTransmitCompressedAudio(src, dst, frame);
        }

        @Override
        protected void onTransmitTextMessage(TextMessage textMessage) {
            _parentProtocolCallback.onTransmitTextMessage(textMessage);
        }

        @Override
        protected void onTransmitPosition(Position position) {
            _parentProtocolCallback.onTransmitPosition(position);
        }

        @Override
        protected void onTransmitData(String src, String dst, String path, byte[] data) {
            _parentProtocolCallback.onTransmitData(src, dst, path, data);
        }

        @Override
        protected void onTransmitLog(String logData) {
            _parentProtocolCallback.onTransmitLog(logData);
        }

        @Override
        protected void onProtocolRxError() {
            _parentProtocolCallback.onProtocolRxError();
        }

        @Override
        protected void onProtocolTxError() {
            _parentProtocolCallback.onProtocolTxError();
        }
    };

    @Override
    public void sendPosition(Position position) throws IOException {
        position.dstCallsign = _dstCallsign;
        position.srcCallsign = _srcCallsign;
        position.symbolCode = _symbolCode;
        position.comment = _comment;
        position.status = _status;
        position.isCompressed = _isCompressed;
        position.privacyLevel = _privacyLevel;
        position.isSpeedBearingEnabled = _isBearingCourseEnabled;
        position.isAltitudeEnabled = _isAltitudeEnabled;
        position.extDigipathSsid = _miceDigipath;
        AprsData aprsData = AprsDataFactory.create(_positionDataType);
        if (aprsData != null) {
            try {
                aprsData.fromPosition(position);
            } catch (Exception e) {
                e.printStackTrace();
                _parentProtocolCallback.onProtocolTxError();
                return;
            }
            if (aprsData.isValid()) {
                sendData(position.srcCallsign, position.dstCallsign, null, aprsData.toBinary());
                _parentProtocolCallback.onTransmitPosition(position);
            } else {
                Log.e(TAG, "Invalid APRS data");
                _parentProtocolCallback.onProtocolTxError();
            }
        }
    }

    @Override
    public void flush() throws IOException {
        _childProtocol.flush();
    }

    @Override
    public void close() {
        _childProtocol.close();
    }
}
