package com.secureline.server;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Process;
import android.os.SystemClock;
import android.util.Slog;

/**
 * Lemenzo Voice cellular pipeline v8.
 *
 * Architecture:
 *   Pixel native PSTN/AoC call remains in MODE_IN_CALL and keeps ownership of
 *   RX, earpiece/speaker routing, microphone topology and acoustic processing.
 *
 *   VOICE_UPLINK native incall capture -> Lemenzo DSP -> Android's privileged
 *   PSTN call-uplink injection AudioTrack. AudioManager keeps MODE_IN_CALL and
 *   marks only that track for call redirection; the stock audio-policy
 *   STRATEGY_CALL_ASSISTANT routes it to AUDIO_DEVICE_OUT_TELEPHONY_TX. Pixel's
 *   vendor HAL then owns the native cellular TX replacement / restore lifecycle.
 *
 * This controller never calls AudioManager.setMode(), never creates a custom
 * downlink, never patches AudioPolicy routing, and never asks AudioFlinger to
 * manufacture a Call-TX software bridge.
 */
// LEMENZO_VOICE_NATIVE_TX_CONTROLLER_V8
final class LemenzoNativeCallTxController {
    private static final String TAG = "LemenzoNativeCallTxV8";
    private static final int SAMPLE_RATE = 48000;
    private static final int FRAMES_10_MS = 480;
    private static final int MONITOR_MS = 100;
    private static final int START_STABLE_MS = 300;
    private static final int NATIVE_TX_RESTORE_MS = 120;

    private final AudioManager mAudioManager;
    private final Object mLock = new Object();

    private volatile boolean mAlive;
    private volatile boolean mEnabled;
    private volatile boolean mRunning;
    private volatile boolean mStopRequested;
    private volatile boolean mSuppressUntilCallEnds;
    private volatile long mEligibleSinceMs;

    private Thread mMonitorThread;
    private Thread mAudioThread;
    private AudioRecord mUplinkRecord;
    private AudioTrack mNativeTxTrack;

    private static native void nativeProcess(short[] audio, int frames, int sampleRate);

    LemenzoNativeCallTxController(Context context) {
        mAudioManager = context.getSystemService(AudioManager.class);
        if (mAudioManager == null) {
            throw new IllegalStateException("AudioManager unavailable");
        }
    }

    void start(boolean enabled) {
        synchronized (mLock) {
            mEnabled = enabled;
            if (mAlive) return;
            mAlive = true;
            mMonitorThread = new Thread(this::monitorLoop, "LemenzoNativeTxMonitorV8");
            mMonitorThread.start();
        }
        Slog.i(TAG, "controller started; device=" + Build.DEVICE
                + " supported=" + isSupportedPixel() + " enabled=" + enabled);
    }

    void setEnabled(boolean enabled) {
        mEnabled = enabled;
        if (!enabled) {
            requestStop("Voice disabled");
        }
        Slog.i(TAG, "enabled=" + enabled);
    }

    private boolean isSupportedPixel() {
        switch (Build.DEVICE) {
            case "bluejay": // Pixel 6a
            case "panther": // Pixel 7
            case "lynx":    // Pixel 7a
            case "shiba":   // Pixel 8
            case "akita":   // Pixel 8a
            case "tokay":   // Pixel 9
            case "tegu":    // Pixel 9a
            case "caiman":  // Pixel 9 Pro
                return true;
            default:
                return false;
        }
    }

    private boolean isPstnCall() {
        if (mAudioManager.getMode() != AudioManager.MODE_IN_CALL) return false;
        try {
            return mAudioManager.isPstnCallAudioInterceptable();
        } catch (Throwable t) {
            Slog.w(TAG, "isPstnCallAudioInterceptable failed", t);
            return false;
        }
    }

    private void monitorLoop() {
        while (mAlive) {
            try {
                final int mode = mAudioManager.getMode();

                if (mode != AudioManager.MODE_IN_CALL) {
                    mEligibleSinceMs = 0;
                    mSuppressUntilCallEnds = false;
                }

                final boolean eligible = mEnabled
                        && isSupportedPixel()
                        && !mSuppressUntilCallEnds
                        && mode == AudioManager.MODE_IN_CALL
                        && isPstnCall();
                final long now = SystemClock.elapsedRealtime();

                if (eligible && !mRunning) {
                    if (mEligibleSinceMs == 0) mEligibleSinceMs = now;
                    if (now - mEligibleSinceMs >= START_STABLE_MS) {
                        startPipeline();
                    }
                } else if (!eligible && !mRunning) {
                    mEligibleSinceMs = 0;
                }

                if (mRunning && (!mEnabled || mode != AudioManager.MODE_IN_CALL)) {
                    requestStop("call/Voice no longer eligible; mode=" + mode);
                }

                Thread.sleep(MONITOR_MS);
            } catch (InterruptedException ignored) {
            } catch (Throwable t) {
                Slog.e(TAG, "monitor failure", t);
                try {
                    Thread.sleep(250);
                } catch (InterruptedException ignored) {
                }
            }
        }
    }

    private void startPipeline() {
        synchronized (mLock) {
            if (mRunning || !mEnabled || mSuppressUntilCallEnds || !isSupportedPixel()) return;
            if (!isPstnCall()) return;

            mRunning = true;
            mStopRequested = false;
            mAudioThread = new Thread(this::runPipeline, "LemenzoNativeTxAudioV8");
            mAudioThread.start();
        }
    }

    private void runPipeline() {
        long framesReadTotal = 0;
        long framesWrittenTotal = 0;
        long lastStatsMs = SystemClock.elapsedRealtime();
        boolean nativeTxStarted = false;

        try {
            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);

            if (!mEnabled || !isSupportedPixel() || !isPstnCall()) {
                throw new IllegalStateException("PSTN call no longer eligible before setup");
            }

            final int minRecordBytes = AudioRecord.getMinBufferSize(
                    SAMPLE_RATE,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT);
            if (minRecordBytes <= 0) {
                throw new IllegalStateException("invalid VOICE_UPLINK min buffer=" + minRecordBytes);
            }
            final int recordBytes = Math.max(minRecordBytes * 2, FRAMES_10_MS * 2 * 8);

            // Native Pixel incall uplink tap. AudioFlinger global Lemenzo DSP explicitly
            // excludes AUDIO_SOURCE_VOICE_UPLINK, so cellular audio is transformed only once
            // by nativeProcess() below.
            final AudioRecord record = new AudioRecord(
                    MediaRecorder.AudioSource.VOICE_UPLINK,
                    SAMPLE_RATE,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    recordBytes);
            synchronized (mLock) {
                mUplinkRecord = record;
            }
            if (record.getState() != AudioRecord.STATE_INITIALIZED) {
                throw new IllegalStateException("VOICE_UPLINK init failed state=" + record.getState());
            }
            if (record.getSampleRate() != SAMPLE_RATE || record.getChannelCount() != 1) {
                throw new IllegalStateException("VOICE_UPLINK unexpected format rate="
                        + record.getSampleRate() + " channels=" + record.getChannelCount());
            }

            record.startRecording();
            if (record.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
                throw new IllegalStateException("VOICE_UPLINK start failed state="
                        + record.getRecordingState());
            }

            final short[] mono = new short[FRAMES_10_MS];
            final short[] stereo = new short[FRAMES_10_MS * 2];

            // Do not touch modem TX until the native uplink tap has proven it can read.
            final int probeFrames = record.read(
                    mono, 0, mono.length, AudioRecord.READ_BLOCKING);
            if (probeFrames <= 0) {
                throw new IllegalStateException("VOICE_UPLINK probe failed=" + probeFrames);
            }
            if (!mEnabled || !isPstnCall()) {
                throw new IllegalStateException("call changed after VOICE_UPLINK probe");
            }

            final AudioFormat txFormat = new AudioFormat.Builder()
                    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                    .setSampleRate(SAMPLE_RATE)
                    .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
                    .build();

            // Android 16 already exposes the privileged PSTN uplink-injection
            // primitive we need. In MODE_IN_CALL AudioManager classifies this as
            // CALL_REDIRECT_PSTN, marks only the AudioTrack with
            // FLAG_CALL_REDIRECTION/USAGE_CALL_ASSISTANT, and stock AudioPolicy
            // routes STRATEGY_CALL_ASSISTANT to TELEPHONY_TX. Crucially, this does
            // NOT require changing the global phone mode to MODE_CALL_REDIRECT.
            final AudioTrack track = mAudioManager.getCallUplinkInjectionAudioTrack(txFormat);
            synchronized (mLock) {
                mNativeTxTrack = track;
            }
            if (track.getState() != AudioTrack.STATE_INITIALIZED) {
                throw new IllegalStateException("native TELEPHONY_TX init failed state="
                        + track.getState());
            }
            if (track.getSampleRate() != SAMPLE_RATE || track.getChannelCount() != 2) {
                throw new IllegalStateException("native TELEPHONY_TX unexpected format rate="
                        + track.getSampleRate() + " channels=" + track.getChannelCount());
            }

            track.play();
            if (track.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
                throw new IllegalStateException("native TELEPHONY_TX did not enter PLAYING");
            }

            // First write activates Android's stock call-uplink injection output,
            // which AudioPolicy routes to TELEPHONY_TX. On GS201 this enters the
            // vendor TELEPHONY_TX playback usecase and Pixel re-evaluates its own
            // native voice TX route. We do not modify MODE_IN_CALL or call RX.
            nativeProcess(mono, probeFrames, SAMPLE_RATE);
            monoToStereo(mono, stereo, probeFrames);
            writeAllStereo(track, stereo, probeFrames);
            nativeTxStarted = true;
            framesReadTotal += probeFrames;
            framesWrittenTotal += probeFrames;

            Slog.i(TAG, "ACTIVE: native VOICE_UPLINK -> Lemenzo DSP -> stock PSTN "
                    + "uplink injection -> TELEPHONY_TX; Pixel RX/routing untouched; device="
                    + Build.DEVICE);

            while (mAlive && mRunning && mEnabled) {
                if (mAudioManager.getMode() != AudioManager.MODE_IN_CALL) break;

                final int frames = record.read(
                        mono, 0, mono.length, AudioRecord.READ_BLOCKING);
                if (frames < 0) {
                    throw new IllegalStateException("VOICE_UPLINK read failed=" + frames);
                }
                if (frames == 0) continue;

                nativeProcess(mono, frames, SAMPLE_RATE);
                monoToStereo(mono, stereo, frames);
                writeAllStereo(track, stereo, frames);
                framesReadTotal += frames;
                framesWrittenTotal += frames;

                final long now = SystemClock.elapsedRealtime();
                if (now - lastStatsMs >= 5000) {
                    Slog.i(TAG, "stream healthy readFrames=" + framesReadTotal
                            + " writtenFrames=" + framesWrittenTotal
                            + " mode=" + mAudioManager.getMode());
                    lastStatsMs = now;
                }
            }
        } catch (Throwable t) {
            if (!mStopRequested) {
                mSuppressUntilCallEnds = true;
                Slog.e(TAG, "native TX pipeline failed; suppressing retries until call ends; "
                        + "Pixel native call fallback requested", t);
            } else {
                Slog.i(TAG, "pipeline stopped after requested teardown: " + t);
            }
        } finally {
            cleanupNativeFirst(nativeTxStarted);
        }
    }

    private static void monoToStereo(short[] mono, short[] stereo, int frames) {
        for (int i = 0; i < frames; i++) {
            final short sample = mono[i];
            final int out = i * 2;
            stereo[out] = sample;
            stereo[out + 1] = sample;
        }
    }

    private void writeAllStereo(AudioTrack track, short[] stereo, int frames) {
        final int shorts = frames * 2;
        int offset = 0;
        while (offset < shorts && mRunning && mEnabled) {
            final int written = track.write(
                    stereo, offset, shorts - offset, AudioTrack.WRITE_BLOCKING);
            if (written < 0) {
                throw new IllegalStateException("native TELEPHONY_TX write failed=" + written);
            }
            if (written == 0) {
                Thread.yield();
                continue;
            }
            offset += written;
        }
        if (offset != shorts && mRunning && mEnabled) {
            throw new IllegalStateException("short native TELEPHONY_TX write=" + offset
                    + "/" + shorts);
        }
    }

    private void requestStop(String reason) {
        if (!mRunning) return;
        Slog.i(TAG, "stop requested: " + reason);
        mStopRequested = true;
        mRunning = false;

        final AudioTrack track;
        final Thread thread;
        synchronized (mLock) {
            track = mNativeTxTrack;
            thread = mAudioThread;
        }

        // Critical teardown ordering: remove remote/native TELEPHONY_TX first so Pixel's
        // vendor HAL can restore INTERNAL_MIC_TX. Only then stop the VOICE_UPLINK tap.
        // Stop the TX track immediately. Do not stop VOICE_UPLINK from this thread:
        // the audio loop reads in 10 ms blocks and will exit on mRunning=false, then its
        // finally block releases TELEPHONY_TX before stopping the capture. This ordering
        // gives Pixel HAL a clean opportunity to restore native microphone TX first.
        stopNativeTxTrack(track);
        if (thread != null) thread.interrupt();
    }

    private static void stopNativeTxTrack(AudioTrack track) {
        if (track == null) return;
        try {
            track.pause();
        } catch (Throwable ignored) {
        }
        try {
            track.flush();
        } catch (Throwable ignored) {
        }
        try {
            track.stop();
        } catch (Throwable ignored) {
        }
    }

    private void cleanupNativeFirst(boolean nativeTxStarted) {
        final AudioTrack track;
        final AudioRecord record;
        synchronized (mLock) {
            track = mNativeTxTrack;
            mNativeTxTrack = null;
            record = mUplinkRecord;
            mUplinkRecord = null;
        }

        stopNativeTxTrack(track);
        if (track != null) {
            try {
                track.release();
            } catch (Throwable ignored) {
            }
        }
        if (nativeTxStarted && track != null) {
            // Vendor HAL's native TX replacement is cleared when this dedicated output
            // becomes inactive/standby. Release first, then give that state transition a
            // short head start before tearing down the uplink capture.
            SystemClock.sleep(NATIVE_TX_RESTORE_MS);
        }

        if (record != null) {
            try {
                record.stop();
            } catch (Throwable ignored) {
            }
            try {
                record.release();
            } catch (Throwable ignored) {
            }
        }

        synchronized (mLock) {
            mAudioThread = null;
            mRunning = false;
            mStopRequested = false;
            mEligibleSinceMs = 0;
        }

        Slog.i(TAG, "pipeline stopped; Pixel mode left untouched; currentMode="
                + mAudioManager.getMode() + " suppressUntilCallEnds=" + mSuppressUntilCallEnds);
    }
}
