/* (C) 2024 by sysmocom s.f.m.c. GmbH
 * All Rights Reserved
 *
 * Author: Philipp Maier
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 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 .
 *
 */
package org.osmocom.androidApduProxy;
import androidx.appcompat.app.AppCompatActivity;
import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.List;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
    private Omapi omapi;
    SharedPreferences pref;
    //Message types that can be passed between the UI thread and OmapiCallbackHandlers
    public static final int IND_ATR_TRAFFIC = 1;
    public static final int IND_APDU_TRAFFIC = 2;
    public static final int IND_CHANNEL_OPEN = 3;
    public static final int IND_CHANNEL_CLOSE = 4;
    public static final int IND_ERROR = 5;
    public static final int IND_SHUTDOWN = 6;
    public static final int IND_SLOT_SCAN_RESULT = 7;
    //Helper method to display a message box on the screen
    public void msgBox(String prompt, String title){
        Log.i("MAIN","message box: " + title + ": " + prompt + "\n");
        AlertDialog.Builder alertDlgBuilder = new AlertDialog.Builder(this);
        alertDlgBuilder.setMessage(prompt);
        alertDlgBuilder.setTitle(title);
        AlertDialog alertDlg = alertDlgBuilder.create();
        alertDlg.show();
    }
    //Reset the indicator lights on the UI interface
    private void resetIndicatorLights()
    {
        final RadioButton indAtrTraffic=findViewById(R.id.indAtrTraffic);
        final RadioButton indApduTraffic=findViewById(R.id.indApduTraffic);
        final RadioButton indChannel=findViewById(R.id.indChannel);
        indAtrTraffic.setChecked(false);
        indApduTraffic.setChecked(false);
        indChannel.setChecked(false);
    }
    //Lock UI elements depending on the connected/disconnected state
    private void setUiConnected(boolean connected)
    {
        final Spinner spnSlot = findViewById(R.id.spnSlot);
        final TextView txtRemoteHost = findViewById(R.id.txtRemoteHost);
        final TextView txtRemotePort = findViewById(R.id.txtRemotePort);
        final Button btnConnect = findViewById(R.id.btnConnect);
        final Button btnDisconnect = findViewById(R.id.btnDisconnect);
        spnSlot.setEnabled(!connected);
        txtRemoteHost.setEnabled(!connected);
        txtRemotePort.setEnabled(!connected);
        btnConnect.setEnabled(!connected);
        btnDisconnect.setEnabled(connected);
    }
    Handler uiHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message inputMessage) {
            final RadioButton indAtrTraffic=findViewById(R.id.indAtrTraffic);
            final RadioButton indApduTraffic=findViewById(R.id.indApduTraffic);
            final RadioButton indChannel=findViewById(R.id.indChannel);
            final Spinner spnSlot = findViewById(R.id.spnSlot);
            switch(inputMessage.what) {
                case IND_ATR_TRAFFIC:
                    indAtrTraffic.setChecked(!indAtrTraffic.isChecked());
                    break;
                case IND_APDU_TRAFFIC:
                    indApduTraffic.setChecked(!indApduTraffic.isChecked());
                    break;
                case IND_CHANNEL_OPEN:
                    indChannel.setChecked(true);
                    break;
                case IND_CHANNEL_CLOSE:
                    indChannel.setChecked(false);
                    indApduTraffic.setChecked(false);
                    break;
                case IND_ERROR:
                    msgBox(inputMessage.obj.toString(),"Error");
                    break;
                case IND_SHUTDOWN:
                    resetIndicatorLights();
                    setUiConnected(false);
                    break;
                case IND_SLOT_SCAN_RESULT:
                    ArrayAdapter dataAdapter = new ArrayAdapter(getBaseContext(),
                            android.R.layout.simple_spinner_item, (List)inputMessage.obj);
                    dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                    spnSlot.setAdapter(dataAdapter);
                    omapi.shutdown();
                    break;
            }
        }
    };
    private String getInfoString() {
        PackageManager packageManager = this.getPackageManager();
        PackageInfo packageInfo = null;
        String version;
        try {
            packageInfo = packageManager.getPackageInfo(getBaseContext().getPackageName(), 0);
            version = packageInfo.versionName;
        } catch (Exception e) {
            version = "(unknown)";
        }
        return getString(R.string.app_name) + " version " + version + "\n" +
                "(C) 2024 by sysmocom - s.f.m.c. GmbH\n" +
                "running on Android " + Build.VERSION.RELEASE + " (API level " + Build.VERSION.SDK_INT + ")";
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        final TextView txtRemoteHost=findViewById(R.id.txtRemoteHost);
        final TextView txtRemotePort=findViewById(R.id.txtRemotePort);
        final TextView lblInfo=findViewById(R.id.lblInfo);
        PackageManager packageManager = this.getPackageManager();
        Log.i("MAIN", "Welcome to " + getInfoString() + "\n");
        lblInfo.setText(getInfoString());
        //Check if this device even supports APDU access to an UICC/eUICC card, if not we display
        //an error message and continue normally, maybe we are lucky and it works anyway.
        //(This check only works on Android 11 / API-level 30 and higher,
        //see also https://developer.android.com/develop/connectivity/telecom/dialer-app/telephony-ids)
        if (Build.VERSION.SDK_INT >= 30 &&
                !packageManager.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC)) {
            msgBox("Feature FEATURE_SE_OMAPI_UICC is not available on this device, APDU access to UICC/eUICC not possible!",
                    "Error");
        }
        //Scan for usable card slots
        omapi = new Omapi(this, new OmapiCallbackHandlerScan(uiHandler));
        //Populate remote host settings
        pref = getSharedPreferences("pref", Context.MODE_PRIVATE);
        txtRemoteHost.setText(pref.getString("REMOTE_HOST", "192.168.1.156"));
        pref = getSharedPreferences("pref", Context.MODE_PRIVATE);
        txtRemotePort.setText(pref.getString("REMOTE_PORT", "8000"));
        setUiConnected(false);
    }
    public void onClickBtnConnect(View view)
    {
        Log.i("MAIN","user has pressed connect button");
        final Spinner spnSlot = findViewById(R.id.spnSlot);
        final TextView txtRemoteHost = findViewById(R.id.txtRemoteHost);
        final TextView txtRemotePort = findViewById(R.id.txtRemotePort);
        setUiConnected(true);
        resetIndicatorLights();
        //Start APDU proxy
        if (spnSlot.getSelectedItem() == null) {
            msgBox("no UICC/eUICC card selected!", "Error");
            return;
        }
        String omapiReader = spnSlot.getSelectedItem().toString();
        String remoteHost = txtRemoteHost.getText().toString();
        Integer remotePort = Integer.valueOf((txtRemotePort.getText().toString()));
        omapi = new Omapi(this, new OmapiCallbackHandlerVpcd(uiHandler, omapiReader, remoteHost, remotePort));
    }
    public void onClickBtnDisconnect(View view)
    {
        Log.i("MAIN","user has pressed disconnect button");
        omapi.shutdown();
        setUiConnected(false);
        resetIndicatorLights();
    }
    public void onClickBtnExit(View view)
    {
        Log.i("MAIN","user has pressed exit button");
        omapi.shutdown();
        this.finishAffinity();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        final TextView txtRemoteHost = findViewById(R.id.txtRemoteHost);
        final TextView txtRemotePort = findViewById(R.id.txtRemotePort);
        //Save remote host settings
        SharedPreferences.Editor editor = pref.edit();
        editor.putString("REMOTE_HOST", txtRemoteHost.getText().toString());
        editor.putString("REMOTE_PORT", txtRemotePort.getText().toString());
        editor.commit();
    }
}