package com.chaquo.python.demo;

import android.os.*;
import android.support.v7.app.*;
import android.text.*;
import android.util.*;
import android.view.*;
import android.widget.*;
import com.chaquo.python.*;
import java.util.*;

public class ConsoleActivity extends AppCompatActivity {

    protected Python py = Python.getInstance();
    protected ScrollView svBuffer;
    protected TextView tvBuffer;

    protected static class State {
        boolean pendingNewline = false;  // Prevent empty line at bottom of screen
        boolean scrolledToBottom = false;
    }
    protected State state;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        svBuffer = (ScrollView) findViewById(R.id.svBuffer);
        tvBuffer = (TextView) findViewById(R.id.tvBuffer);
        if (Build.VERSION.SDK_INT >= 23) {
            tvBuffer.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
        }

        PyObject demo_app = py.getModule("demo_app");
        PyObject stream = demo_app.callAttr("ForwardingOutputStream", this, "append");
        PyObject sys = py.getModule("sys");
        sys.put("stdout", stream);
        sys.put("stderr", stream);

        state = (State) getLastCustomNonConfigurationInstance();
        if (state == null) {
            state = initState();
        }

        svBuffer.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                state.scrolledToBottom = isScrolledToBottom();
            }
        });

        // Triggered when the keyboard is hidden or shown. Also triggered by the text selection
        // toolbar appearing and disappearing, on Android versions which use it.
        svBuffer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                adjustScroll();
            }
        });
    }

    protected State initState() {
        return new State();
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        // Don't restore the REPL UI scrollback if the Python InteractiveConsole object can't be
        // restored as well (saw this happen once).
        if (getLastCustomNonConfigurationInstance() != null) {
            super.onRestoreInstanceState(savedInstanceState);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        adjustScroll();  // Necessary after a screen rotation
    }

    private void adjustScroll() {
        if (state.scrolledToBottom  &&  ! isScrolledToBottom()) {
            scroll(View.FOCUS_DOWN);
        }
    }

    private boolean isScrolledToBottom() {
        int svBufferHeight = (svBuffer.getHeight() -
        svBuffer.getPaddingTop() -
        svBuffer.getPaddingBottom());
        int maxScroll = Math.max(0, tvBuffer.getHeight() - svBufferHeight);
        return (svBuffer.getScrollY() >= maxScroll);
    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        return state;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater mi = getMenuInflater();
        mi.inflate(R.menu.top_bottom, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_top: {
                scroll(View.FOCUS_UP);
            } break;

            case R.id.menu_bottom: {
                scroll(View.FOCUS_DOWN);
            } break;

            default: return false;
        }
        return true;
    }

    public void append(CharSequence text) {
        if (text.length() == 0) return;
        for (String line : text.toString().split("\n")) {
            Log.d("append", line);
        }

        final List<CharSequence> fragments = new ArrayList<>();
        if (state.pendingNewline) {
            fragments.add("\n");
            state.pendingNewline = false;
        }
        if (text.charAt(text.length() - 1) == '\n') {
            fragments.add(text.subSequence(0, text.length() - 1));
            state.pendingNewline = true;
        } else {
            fragments.add(text);
        }
        
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                for (CharSequence frag : fragments) {
                    tvBuffer.append(frag);
                }
                svBuffer.post(new Runnable() {
                    @Override
                    public void run() {
                        scroll(View.FOCUS_DOWN);
                    }
                });
            }
        });
    }

    protected void scroll(int direction) {
        svBuffer.fullScroll(direction);
    }

}

@@ -592,16 +592,62 @@
 state;%0A%0A
+    private PyObject prevStdout, prevStderr;%0A%0A
     @Ove
@@ -1000,264 +1000,8 @@
  %7D%0A%0A
-        PyObject demo_app = py.getModule(%22demo_app%22);%0A        PyObject stream = demo_app.callAttr(%22ForwardingOutputStream%22, this, %22append%22);%0A        PyObject sys = py.getModule(%22sys%22);%0A        sys.put(%22stdout%22, stream);%0A        sys.put(%22stderr%22, stream);%0A%0A
     
@@ -2305,22 +2305,21 @@
  void on
-Resume
+Start
 () %7B%0A   
@@ -2335,14 +2335,13 @@
 r.on
-Resume
+Start
 ();%0A
@@ -2397,24 +2397,503 @@
 een rotation
+%0A%0A        PyObject demo_app = py.getModule(%22demo_app%22);%0A        PyObject stream = demo_app.callAttr(%22ForwardingOutputStream%22, this, %22append%22);%0A        PyObject sys = py.getModule(%22sys%22);%0A        prevStdout = sys.put(%22stdout%22, stream);%0A        prevStderr = sys.put(%22stderr%22, stream);%0A    %7D%0A%0A    @Override%0A    protected void onStop() %7B%0A        super.onStop();%0A        PyObject sys = py.getModule(%22sys%22);%0A        sys.put(%22stdout%22, prevStdout);%0A        sys.put(%22stderr%22, prevStderr);
 %0A    %7D%0A%0A    

Fix stdout and stderr handling when switching between ConsoleActivity subclasses