package com.cureselect.weighingscale;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;
import com.cureselect.weighingscale.R;
import chipsea.bias.v235.CSBiasAPI;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private static final int REQUEST_PERMISSIONS = 1;
    private static final long SCAN_DURATION = 30000; // 30 seconds
    private static final long RETRY_DELAY = 2000; // 2s delay on failure

    private EditText txtHeight, txtAge, txtWeight, txtR;
    private RadioButton radioMale;
    private TextView txtTest;

    // BLE
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothLeScanner bluetoothLeScanner;
    private ScanCallback scanCallback;
    private Handler handler = new Handler(Looper.getMainLooper());
    private Runnable stopScanRunnable;
    private AtomicBoolean isScanning = new AtomicBoolean(false); // Thread-safe flag

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Init views
        txtHeight = findViewById(R.id.txtHeight);
        txtAge = findViewById(R.id.txtAge);
        txtWeight = findViewById(R.id.txtWeight);
        txtR = findViewById(R.id.txtR);
        radioMale = findViewById(R.id.radioMale);
        txtTest = findViewById(R.id.txtTest);

        // Init BLE
        initBluetooth();
    }

    private void initBluetooth() {
        BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();
        if (bluetoothAdapter == null) {
            Toast.makeText(this, "Bluetooth not available.", Toast.LENGTH_LONG).show();
            return;
        }

        bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
        if (bluetoothLeScanner == null) {
            Toast.makeText(this, "BLE Scanner not available.", Toast.LENGTH_SHORT).show();
            return;
        }

        if (!bluetoothAdapter.isEnabled()) {
            Toast.makeText(this, "Bluetooth not enabled. Please enable it.", Toast.LENGTH_LONG).show();
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivity(enableBtIntent);
            return;
        }

        // Delay 5s for Samsung stack to settle
        handler.postDelayed(this::setupScanCallback, 5000);
    }

    private float lastDetectedWeight = -1f; // Track last weight to avoid spam

    private void parseAAAScaleData(byte[] fullRecord, String deviceName) {
        // AAA scale format analysis
        // Looking for manufacturer data after 0F FF marker
        for (int i = 0; i < fullRecord.length - 15; i++) {
            // Find manufacturer data marker: 0x0F 0xFF
            if (fullRecord[i] == 0x0F && fullRecord[i+1] == (byte)0xFF) {
                Log.d(TAG, "Found manufacturer data at offset " + i);
                // Extract manufacturer data (next 14 bytes typically)
                int dataStart = i + 2;
                if (dataStart + 14 <= fullRecord.length) {
                    byte[] mfgData = Arrays.copyOfRange(fullRecord, dataStart, dataStart + 14);
                    Log.d(TAG, "Manufacturer data: " + bytesToHex(mfgData));

                    float finalWeight = 0;
                    String detectionMethod = "";
                    float bestWeight = Float.MAX_VALUE;  // NEW: Track "best" (lowest plausible) to avoid fixed values

                    // REORDER: Start offsets from END (likely weight position) to prioritize dynamic bytes
                    for (int offset = mfgData.length - 2; offset >= 0; offset--) {  // Reverse order: end first
                        int rawBE = ((mfgData[offset] & 0xFF) << 8) | (mfgData[offset + 1] & 0xFF);
                        int rawLE = ((mfgData[offset + 1] & 0xFF) << 8) | (mfgData[offset] & 0xFF);

                        float[] divisors = {100.0f, 10.0f, 200.0f, 1.0f};  // Keep, but now later offsets first

                        for (float divisor : divisors) {
                            float testWeightBE = rawBE / divisor;
                            float testWeightLE = rawLE / divisor;

                            Log.d(TAG, String.format("Candidate: offset=%d BE/%.0f=%.3fkg (raw=%d), LE/%.0f=%.3fkg (raw=%d)",
                                    offset, divisor, testWeightBE, rawBE, divisor, testWeightLE, rawLE));  // More decimals for precision

                            // CHANGED: Lower min to 0.1 kg (allows 12.5 kg), raise max to 200 kg
                            boolean isValidBE = (testWeightBE >= 0.1f && testWeightBE <= 200.0f);
                            boolean isValidLE = (testWeightLE >= 0.1f && testWeightLE <= 200.0f);

                            // NEW: Only consider if "better" than current (e.g., lower value, avoids fixed high like 88.3)
                            if (isValidBE && testWeightBE < bestWeight) {
                                bestWeight = testWeightBE;
                                detectionMethod = String.format("BE offset=%d divisor=%.0f raw=%d", offset, divisor, rawBE);
                            }
                            if (isValidLE && testWeightLE < bestWeight) {  // Prioritize LE if tie
                                bestWeight = testWeightLE;
                                detectionMethod = String.format("LE offset=%d divisor=%.0f raw=%d", offset, divisor, rawLE);
                            }
                        }
                    }

                    if (bestWeight < Float.MAX_VALUE && bestWeight >= 0.1f) {  // NEW: Use best instead of first
                        finalWeight = bestWeight;
                        Log.d(TAG, "✓ Best valid weight: " + finalWeight + "kg via " + detectionMethod);
                    } else {
                        Log.w(TAG, "No valid weight found—check format");
                        return;
                    }

                    if (finalWeight > 0) {
                        // Only update if significantly different from last (live updates without spam)
                        if (Math.abs(finalWeight - lastDetectedWeight) > 0.1f) {
                            lastDetectedWeight = finalWeight;
                            final float detectedWeight = finalWeight;
                            final String method = detectionMethod;
                            runOnUiThread(() -> {
                                txtWeight.setText(String.format("%.1f", detectedWeight));
                                txtR.setText("500"); // Default impedance
                                Toast.makeText(MainActivity.this,
                                        String.format("Weight: %.1fkg (%s)", detectedWeight, method),
                                        Toast.LENGTH_SHORT).show(); // Short toast for live updates
                                newClick(null); // Auto-calculate
                            });
                        }
                        // DO NOT stopScanning() - keep live updating during scan
                        return;
                    } else {
                        Log.w(TAG, "No valid weight found in manufacturer data");
                    }
                }
            }
        }
    }
    private String bytesToHex(byte[] bytes) {
        if (bytes == null) return "null";
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }

    private void setupScanCallback() {
        scanCallback = new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                super.onScanResult(callbackType, result);
                BluetoothDevice device = result.getDevice();
                String deviceName = "Unnamed";
                try {
                    if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) {
                        deviceName = device.getName() != null ? device.getName() : "Unnamed";
                    }
                } catch (SecurityException e) {
                    Log.e(TAG, "Permission error", e);
                }
                Log.d(TAG, "Device: " + deviceName + " | MAC: " + device.getAddress() + " | RSSI: " + result.getRssi());

                // Check for AAA002 scale
                if (deviceName != null && deviceName.startsWith("AAA")) {
                    Log.d(TAG, "🎯 Found AAA scale!");
                    if (result.getScanRecord() != null) {
                        byte[] fullRecord = result.getScanRecord().getBytes();
                        Log.d(TAG, "Full data: " + bytesToHex(fullRecord));
                        parseAAAScaleData(fullRecord, deviceName);
                    }
                }
            }

            @Override
            public void onScanFailed(int errorCode) {
                super.onScanFailed(errorCode);
                Log.e(TAG, "Scan failed: " + errorCode);
                isScanning.set(false);
                runOnUiThread(() -> {
                    String msg = "Scan failed (code " + errorCode + "). ";
                    if (errorCode == ScanCallback.SCAN_FAILED_ALREADY_STARTED) {
                        msg += "Already started—retrying in 2s.";
                        handler.postDelayed(MainActivity.this::startScanning, RETRY_DELAY); // Auto-retry
                    } else if (errorCode == ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED) {
                        msg += "App registration failed—restart app/BT.";
                    } else if (errorCode == ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED) {
                        msg += "BLE unsupported—check device.";
                    } else {
                        msg += "Try toggling Bluetooth/location.";
                    }
                    Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show();
                });
            }

            @Override
            public void onBatchScanResults(List<ScanResult> results) {
                super.onBatchScanResults(results);
                Log.d(TAG, "Batch results: " + results.size() + " devices (batched delivery)");
                // Process each as in onScanResult if needed
                for (ScanResult result : results) {
                    onScanResult(0, result); // Reuse logic
                }
            }
        };

        stopScanRunnable = () -> {
            stopScanning();
            runOnUiThread(() -> Toast.makeText(this, "Scan stopped. No data found—try stepping on scale.", Toast.LENGTH_SHORT).show());
        };
    }

    public void startScan(View view) {
        // Permission check
        String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT};
        boolean allGranted = true;
        for (String perm : permissions) {
            if (ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) {
                allGranted = false;
                break;
            }
        }
        if (!allGranted) {
            ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSIONS);
            return;
        }

        if (isScanning.get()) {
            Toast.makeText(this, "Scan already in progress—wait or toggle BT.", Toast.LENGTH_SHORT).show();
            return;
        }

        startScanning();
    }

    private void startScanning() {
        if (isScanning.get() || bluetoothLeScanner == null) {
            Log.w(TAG, "Scan blocked: already scanning or null scanner.");
            return;
        }

        isScanning.set(true);
        Log.d(TAG, "Starting scan... (unfiltered, low latency for testing)");

        try {
            ScanSettings settings = new ScanSettings.Builder()
                    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // More aggressive detection
                    .setReportDelay(0) // No batching—immediate callbacks
                    .build();

            // Optional loose filter for AAA to reduce noise (uncomment if needed)
            // List<ScanFilter> filters = Collections.singletonList(
            //     new ScanFilter.Builder().setDeviceName("AAA").build()
            // );
            // bluetoothLeScanner.startScan(filters, settings, scanCallback);

            bluetoothLeScanner.startScan(null, settings, scanCallback); // Unfiltered for now
            Log.d(TAG, "Scan started successfully—no errors.");
        } catch (SecurityException e) {
            Log.e(TAG, "Security error starting scan: " + e.getMessage());
            isScanning.set(false);
            runOnUiThread(() -> Toast.makeText(this, "Scan error: Check permissions/location.", Toast.LENGTH_SHORT).show());
        } catch (Exception e) {
            Log.e(TAG, "Unexpected error starting scan: " + e.getMessage());
            isScanning.set(false);
        }

        handler.postDelayed(stopScanRunnable, SCAN_DURATION);
        runOnUiThread(() -> Toast.makeText(this, "Scanning (low latency mode)... Step on the scale!", Toast.LENGTH_LONG).show());
    }

    private void stopScanning() {
        if (!isScanning.get() || bluetoothLeScanner == null) {
            return;
        }

        isScanning.set(false);
        Log.d(TAG, "Stopping scan...");
        try {
            bluetoothLeScanner.stopScan(scanCallback);
        } catch (Exception e) {
            Log.e(TAG, "Error stopping scan: " + e.getMessage());
        }
        handler.removeCallbacks(stopScanRunnable);
    }

    private void parseChipseaData(byte[] data) {
        Log.d(TAG, "Raw data: " + Arrays.toString(data));

        // OKOK V1.1 Broadcast: CA 11 0F [cmd 00/01] 01 [wt high] [wt low] [product 4B] [properties] [reserved 6B] [checksum]
        if (data.length != 19 || data[0] != (byte) 0xCA || data[1] != (byte) 0x11 || data[2] != (byte) 0x0F || data[4] != (byte) 0x01) {
            Log.d(TAG, "No OKOK match—header mismatch or wrong length.");
            return;
        }

        // Verify checksum: XOR bytes 0-17 == data[18]
        byte checksum = 0;
        for (int i = 0; i < 18; i++) {
            checksum ^= data[i];
        }
        if (checksum != data[18]) {
            Log.w(TAG, "Checksum mismatch: expected " + checksum + ", got " + data[18]);
            return; // Invalid packet
        }

        // Weight: big-endian uint16 (data[5]<<8 | data[6]), scaled by decimals
        int weightRaw = ((data[5] & 0xFF) << 8) | (data[6] & 0xFF);
        byte properties = data[11];
        int decPlaces = (properties >> 4) & 0x03; // Bits 5-4

        double scaleFactor;
        switch (decPlaces) {
            case 0: scaleFactor = 10.0; break; // 1 decimal
            case 1: scaleFactor = 1.0; break; // 0 decimals
            case 2: scaleFactor = 100.0; break; // 2 decimals
            default: scaleFactor = 10.0; // Default
        }

        final float weight = (float) (weightRaw / scaleFactor);

        // Unit: Bits 7-6 (assume KG=00; log if other)
        int unit = (properties >> 6) & 0x03;
        String unitStr = (unit == 0) ? "kg" : (unit == 1) ? "Jin" : (unit == 2) ? "lb" : "st:lb";
        if (unit != 0) {
            Log.w(TAG, "Non-KG unit detected: " + unitStr + " - convert if needed.");
            // Optional: Convert to kg (e.g., lb to kg: weight * 0.453592)
        }

        // R not in broadcast—manual/default
        final float defaultR = 500.0f; // From your samples

        runOnUiThread(() -> {
            txtWeight.setText(String.format("%.1f", weight));
            txtR.setText(String.format("%.1f", defaultR)); // Manual R for calc
            newClick(null); // Auto-compute
            Toast.makeText(MainActivity.this, String.format("Fetched: %.1f %s (R manual: %.1fΩ)", weight, unitStr, defaultR), Toast.LENGTH_SHORT).show();
        });

        stopScanning();
        Log.d(TAG, "Parsed: Weight=" + weight + " " + unitStr + " (decimals=" + decPlaces + "), R=manual");
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSIONS && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            startScanning();
        } else {
            Toast.makeText(this, "Permissions denied. Cannot scan.", Toast.LENGTH_SHORT).show();
        }
    }

    public void newClick(View view) {
        try {
            float height = Float.parseFloat(txtHeight.getText().toString().trim());
            int age = Integer.parseInt(txtAge.getText().toString().trim());
            float weight = Float.parseFloat(txtWeight.getText().toString().trim());
            float r = Float.parseFloat(txtR.getText().toString().trim());

            if (height <= 0 || age <= 0 || weight <= 0 || r <= 0) {
                txtTest.setText("Error: All inputs must be positive numbers.");
                return;
            }

            byte sex = radioMale.isChecked() ? (byte) 1 : (byte) 0;
            CSBiasAPI.CSBiasV235Resp resp = CSBiasAPI.cs_bias_v235(0, sex, age, (int) height, (int) (weight * 10), (int) (r * 10), 2018);

            StringBuilder sb = new StringBuilder();
            if (resp.result == CSBiasAPI.CSBIAS_OK) {
                sb.append("Input\n");
                sb.append("Gender:" + sex + " Height:" + height + " Age:" + age + " Resistance:" + r + " Weight:" + weight + "\n");
                sb.append("**************************************\n");
                sb.append("Body Fat %:" + resp.data.BFP + "\n");
                sb.append("Muscle Mass kg:" + resp.data.SLM + "\n");
                sb.append("Water %:" + resp.data.BWP + "\n");
                sb.append("Bone Mass:" + resp.data.BMC + "\n");
                sb.append("Visceral Fat:" + resp.data.VFR + "\n");
                sb.append("Protein %:" + resp.data.PP + "\n");
                sb.append("Skeletal Muscle kg:" + resp.data.SMM + "\n");
                sb.append("BMR:" + resp.data.BMR + "\n");
                sb.append("BMI:" + resp.data.BMI + "\n");
                sb.append("Standard Weight kg:" + resp.data.SBW + "\n");
                sb.append("Muscle Control:" + (resp.data.MC != 0 ? resp.data.MC : "-") + "\n");
                sb.append("Weight Control:" + (resp.data.WC != 0 ? resp.data.WC : "-") + "\n");
                sb.append("Fat Control:" + (resp.data.FC != 0 ? resp.data.FC : "-") + "\n");
                sb.append("Body Age:" + resp.data.MA + "\n");
                sb.append("Score:" + resp.data.SBC + "\n");
                sb.append("**************************************\n");
            } else {
                sb.append("Error: " + resp.result);
            }
            txtTest.setText(sb.toString());
        } catch (NumberFormatException e) {
            txtTest.setText("Error: Please enter valid numbers.");
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        initBluetooth(); // Re-init for Samsung stability
    }

    @Override
    protected void onPause() {
        super.onPause();
        stopScanning();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopScanning();
        handler.removeCallbacksAndMessages(null);
    }
}