package com.radio.codec2talkie.protocol;

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

import com.radio.codec2talkie.MainActivity;
import com.radio.codec2talkie.R;
import com.radio.codec2talkie.protocol.message.TextMessage;
import com.radio.codec2talkie.protocol.position.Position;
import com.radio.codec2talkie.settings.PreferenceKeys;
import com.radio.codec2talkie.tools.AudioTools;
import com.radio.codec2talkie.tools.StorageTools;
import com.radio.codec2talkie.transport.Transport;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;

public class Recorder implements Protocol {

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

    private static final int ROTATION_DELAY_MS = 5000;

    private File _storage;
    private FileOutputStream _activeStream;
    private Timer _fileRotationTimer;

    final Protocol _childProtocol;
    int _codec2ModeId;

    private String _prevSrcCallsign;
    private String _prevDstCallsign;

    private final SharedPreferences _sharedPreferences;
    private ProtocolCallback _parentProtocolCallback;

    public Recorder(Protocol childProtocol, SharedPreferences sharedPreferences) {
        _childProtocol = childProtocol;
        _sharedPreferences = sharedPreferences;

        _prevSrcCallsign = null;
        _prevDstCallsign = null;
    }

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

        String codec2ModeName = _sharedPreferences.getString(PreferenceKeys.CODEC2_MODE, context.getResources().getStringArray(R.array.codec2_modes)[0]);
        _codec2ModeId = AudioTools.extractCodec2ModeId(codec2ModeName);
    }

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

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

    @Override
    public void sendTextMessage(TextMessage textMessage) throws IOException {
        _childProtocol.sendTextMessage(textMessage);
    }

    @Override
    public void sendPcmAudio(String src, String dst, short[] pcmFrame) throws IOException {
        _childProtocol.sendPcmAudio(src, dst, pcmFrame);
    }

    @Override
    public void sendData(String src, String dst, String path, byte[] dataPacket) throws IOException {
        _childProtocol.sendData(src, 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) {
            _parentProtocolCallback.onReceivePcmAudio(src, dst, pcmFrame);
        }

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

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

        @Override
        protected void onReceiveData(String src, String dst, String path, byte[] data) {
            _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) {
            _parentProtocolCallback.onTransmitPcmAudio(src, dst, 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 {
        _childProtocol.sendPosition(position);
    }

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

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

    private void writeToFile(String src, String dst, byte[] rawData)  {
        stopRotationTimer();
        createStreamIfNotExists(src, dst);
        writeToStream(rawData);
        startRotationTimer();
    }

    private void writeToStream(byte[] rawData) {
        try {
            if (_activeStream != null) {
                _activeStream.write(rawData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void createStreamIfNotExists(String src, String dst) {
        if (_activeStream == null) {
            try {
                Date date = new Date();
                File newDirectory = new File(_storage, getNewDirectoryName(date));
                if (!newDirectory.exists() && !newDirectory.mkdirs()) {
                    Log.e(TAG, "Failed to create directory for voicemails");
                }
                File newAudioFile = new File(newDirectory, getNewFileName(date, src, dst));
                _activeStream = new FileOutputStream(newAudioFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    private String getNewDirectoryName(Date date) {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd", Locale.US);
        return df.format(date);
    }

    private String getNewFileName(Date date, String src, String dst) {
        SimpleDateFormat tf = new SimpleDateFormat("HHmmss", Locale.ENGLISH);
        String codec2mode = String.format(Locale.ENGLISH, "%02d", _codec2ModeId);
        String fileName = codec2mode + "_" + tf.format(date);
        if (src != null && dst != null) {
            fileName += "_" + src + "_" + dst;
        }
        fileName += ".c2";
        return fileName;
    }

    private void rotateIfNewSrcOrDstCallsign(String newSrcCallsign, String newDstCallsign) {
        if (!TextUtils.equals(_prevSrcCallsign, newSrcCallsign) || !TextUtils.equals(_prevDstCallsign, newDstCallsign)) {
            _prevSrcCallsign = newSrcCallsign;
            _prevDstCallsign = newDstCallsign;
            if (_activeStream != null) {
                try {
                    _activeStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                _activeStream = null;
            }
        }
    }

    private void startRotationTimer() {
        _fileRotationTimer = new Timer();
        _fileRotationTimer.schedule(new TimerTask() {
            @Override
            public void run() {
            if (_activeStream != null) {
                try {
                    _activeStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            _activeStream = null;
            }
        }, ROTATION_DELAY_MS);
    }

    private void stopRotationTimer() {
        try {
            if (_fileRotationTimer != null) {
                _fileRotationTimer.cancel();
                _fileRotationTimer.purge();
            }
        } catch (IllegalStateException ignored) {}
    }
}
