package jackpal.androidterm.sample.telnet;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.method.TextKeyListener;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Button;
import android.widget.EditText;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import androidx.appcompat.app.AppCompatActivity;
import jackpal.androidterm.emulatorview.EmulatorView;
import jackpal.androidterm.emulatorview.TermSession;


/**
 * This sample activity demonstrates the use of EmulatorView.
 * <p>
 * This activity also demonstrates how to set up a simple TermSession connected
 * to a local program.  The Telnet connection demonstrates a more complex case;
 * see the TelnetSession class for more details.
 */
public class TermActivity extends AppCompatActivity {
    final private static String TAG = "TermActivity";
    private static final int MSG_CONNECTED = 1;
    Socket mSocket;
    private EditText mEntry;
    private EmulatorView mEmulatorView;
    private TermSession mSession;
    /**
     * Handler which will receive the message from the Telnet connect thread
     * that the connection has been established.
     */
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MSG_CONNECTED) {
                createTelnetSession();
            }
        }
    };

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.term_activity);

        /* Text entry box at the bottom of the activity.  Note that you can
           also send input (whether from a hardware device or soft keyboard)
           directly to the EmulatorView. */
        mEntry = findViewById(R.id.term_entry);
        mEntry.setOnEditorActionListener((v, action, ev) -> {
            // Ignore enter-key-up events
            if (ev != null && ev.getAction() == KeyEvent.ACTION_UP) {
                return false;
            }
            // Don't try to send something if we're not connected yet
            TermSession session = mSession;
            if (mSession == null) {
                return true;
            }

            Editable e = (Editable) v.getText();
            // Write to the terminal session
            session.write(e.toString());
            session.write('\r');
            TextKeyListener.clear(e);
            return true;
        });

        /* Sends the content of the text entry box to the terminal, without
           sending a carriage return afterwards */
        Button sendButton = findViewById(R.id.term_entry_send);
        sendButton.setOnClickListener(v -> {
            // Don't try to send something if we're not connected yet
            TermSession session = mSession;
            if (mSession == null) {
                return;
            }
            Editable e = mEntry.getText();
            session.write(e.toString());
            TextKeyListener.clear(e);
        });

        /**
         * EmulatorView setup.
         */
        EmulatorView view = findViewById(R.id.emulatorView);
        mEmulatorView = view;

        /* Let the EmulatorView know the screen's density. */
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        view.setDensity(metrics);

        /* Create a TermSession. */
        Intent myIntent = getIntent();
        String sessionType = myIntent.getStringExtra("type");
        TermSession session;

        if (sessionType != null && sessionType.equals("telnet")) {
			/* Telnet connection: we need to do the network connect on a
               separate thread, so kick that off and wait for it to finish. */
            connectToTelnet(myIntent.getStringExtra("host"));
            return;
        } else {
            // Create a local shell session.
            session = createLocalTermSession();
            if (session == null) {
                finish();
                return;
            }
            mSession = session;
        }

        /* Attach the TermSession to the EmulatorView. */
        view.attachSession(this, session);

        /* That's all you have to do!  The EmulatorView will call the attached
           TermSession's initializeEmulator() automatically, once it can
           calculate the appropriate screen size for the terminal emulator. */
    }

    @Override
    protected void onResume() {
        super.onResume();

        /* You should call this to let EmulatorView know that it's visible
           on screen. */
        mEmulatorView.onResume();

        mEntry.requestFocus();
    }

    @Override
    protected void onPause() {
        /* You should call this to let EmulatorView know that it's no longer
           visible on screen. */
        mEmulatorView.onPause();

        super.onPause();
    }

    @Override
    protected void onDestroy() {
        /**
         * Finish the TermSession when we're destroyed.  This will free
         * resources, stop I/O threads, and close the I/O streams attached
         * to the session.
         *
         * For the local session, closing the streams will kill the shell; for
         * the Telnet session, it closes the network connection.
         */
        if (mSession != null) {
            mSession.finish();
        }

        super.onDestroy();
    }

    /**
     * Create a TermSession connected to a local shell.
     */
    private TermSession createLocalTermSession() {
        /* Instantiate the TermSession ... */
        TermSession session = new TermSession();

        /* ... create a process ... */
        /* TODO:Make local session work without execpty.
        String execPath = LaunchActivity.getDataDir(this) + "/bin/execpty";
        ProcessBuilder execBuild =
                new ProcessBuilder(execPath, "/system/bin/sh", "-");
        */
        ProcessBuilder execBuild =
                new ProcessBuilder("/system/bin/sh", "-");
        execBuild.redirectErrorStream(true);
        Process exec = null;
        try {
            exec = execBuild.start();
        } catch (Exception e) {
            Log.e(TAG, "Could not start terminal process.", e);
            return null;
        }

        /* ... and connect the process's I/O streams to the TermSession. */
        session.setTermIn(exec.getInputStream());
        session.setTermOut(exec.getOutputStream());

        /* You're done! */
        return session;

        /**
         * NB: You can invoke a program without using execpty or a native code
         * method, but the results may not be what you expect, because the
         * process will be connected to a pipe, not a tty device.  tty devices
         * provide services such as flow control and input/output translation
         * which many programs expect.
         *
         * If you do connect a program directly to a TermSession without using
         * a tty, you should probably at a minimum translate '\r' (sent by the
         * Enter key) to '\n' (which most programs expect as their newline
         * input) in write(), and translate '\n' (sent by most programs to
         * indicate a newline) to '\r\n' (which the terminal emulator needs to
         * actually start a new line without overdrawing text or "staircase
         * effect") in processInput(), before sending it to the terminal
         * emulator.
         *
         * For an example of how to obtain and use a tty device in native code,
         * see assets-src/execpty.c.
         */
    }

    /**
     * Connect to the Telnet server.
     */
    public void connectToTelnet(String server) {
        String[] telnetServer = server.split(":", 2);
        final String hostname = telnetServer[0];
        int port = 23;
        if (telnetServer.length == 2) {
            port = Integer.parseInt(telnetServer[1]);
        }
        final int portNum = port;

        /* On Android API >= 11 (Honeycomb and later), network operations
           must be done off the main thread, so kick off a new thread to
           perform the connect. */
        new Thread() {
            public void run() {
                // Connect to the server
                try {
                    Socket socket = new Socket(hostname, portNum);
                    mSocket = socket;
                } catch (IOException e) {
                    Log.e(TAG, "Could not create socket", e);
                    return;
                }

                // Notify the main thread of the connection
                mHandler.sendEmptyMessage(MSG_CONNECTED);
            }
        }.start();
    }

    /* Create the TermSession which will handle the Telnet protocol and
       terminal emulation. */
    private void createTelnetSession() {
        Socket socket = mSocket;

        // Get the socket's input and output streams
        InputStream termIn;
        OutputStream termOut;
        try {
            termIn = socket.getInputStream();
            termOut = socket.getOutputStream();
        } catch (IOException e) {
            // Handle exception here
            return;
        }

        /* Create the TermSession and attach it to the view.  See the
           TelnetSession class for details. */
        TermSession session = new TelnetSession(termIn, termOut);
        mEmulatorView.attachSession(this, session);
        mSession = session;
    }
}
