package com.mobileer.oboetester;

import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * Run an automated test from a UI, gather logs,
 * and display a summary.
 */
public  class AutomatedTestRunner extends LinearLayout implements Runnable {

    private Button       mStartButton;
    private Button       mStopButton;
    private Button       mShareButton;
    private TextView     mAutoTextView;
    private ScrollView   mAutoTextScroller;
    private TextView     mSingleTestIndex;
    private StringBuffer mFailedSummary;
    private StringBuffer mSummary;
    private StringBuffer mFullSummary;
    private int          mTestCount;
    private int          mPassCount;
    private int          mFailCount;
    private TestAudioActivity  mActivity;

    private Thread            mAutoThread;
    private volatile boolean  mThreadEnabled;
    private CachedTextViewLog mCachedTextView;

    public AutomatedTestRunner(Context context) {
        super(context);
        initializeViews(context);
    }

    public AutomatedTestRunner(Context context, AttributeSet attrs) {
        super(context, attrs);
        initializeViews(context);
    }

    public AutomatedTestRunner(Context context,
                               AttributeSet attrs,
                               int defStyle) {
        super(context, attrs, defStyle);
        initializeViews(context);
    }

    public TestAudioActivity getActivity() {
        return mActivity;
    }

    public void setActivity(TestAudioActivity activity) {
        this.mActivity = activity;
        mCachedTextView = new CachedTextViewLog(activity, mAutoTextView);
    }

    /**
     * Inflates the views in the layout.
     *
     * @param context
     *           the current context for the view.
     */
    private void initializeViews(Context context) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.auto_test_runner, this);

        mStartButton = (Button) findViewById(R.id.button_start);
        mStartButton.setOnClickListener( new OnClickListener() {
            @Override
            public void onClick(View v) {
                startTest();
            }
        });

        mStopButton = (Button) findViewById(R.id.button_stop);
        mStopButton.setOnClickListener( new OnClickListener() {
            @Override
            public void onClick(View v) {
                stopTest();
            }
        });

        mShareButton = (Button) findViewById(R.id.button_share);
        mShareButton.setOnClickListener( new OnClickListener() {
            @Override
            public void onClick(View v) {
                shareResult();
                mShareButton.setEnabled(true);
            }
        });
        mShareButton.setEnabled(false);

        mSingleTestIndex = (TextView) findViewById(R.id.single_test_index);

        mAutoTextScroller = (ScrollView) findViewById(R.id.text_log_auto_scroller);
        mAutoTextView = (TextView) findViewById(R.id.text_log_auto);

        mFailedSummary = new StringBuffer();
        mSummary = new StringBuffer();
        mFullSummary = new StringBuffer();
    }

    private void updateStartStopButtons(boolean running) {
        mStartButton.setEnabled(!running);
        mStopButton.setEnabled(running);
    }

    public int getTestCount() {
        return mTestCount;
    }

    public boolean isThreadEnabled() {
        return mThreadEnabled;
    }

    public void appendFailedSummary(String text) {
        mFailedSummary.append(text);
    }

    public void appendSummary(String text) {
        mSummary.append(text);
    }

    public void incrementFailCount() {
        mFailCount++;
    }
    public void incrementPassCount() {
        mPassCount++;
    }
    public void incrementTestCount() {
        mTestCount++;
    }

    // Write to scrollable TextView
    public void log(final String text) {
        if (text == null) return;
        Log.d(TestAudioActivity.TAG, "LOG - " + text);
        mCachedTextView.append(text + "\n");
        mFullSummary.append(text + "\n");
        scrollToBottom();
    }

    public void scrollToBottom() {
        mAutoTextScroller.fullScroll(View.FOCUS_DOWN);
    }

    // Flush any logs that are stuck in the cache.
    public void flushLog() {
        mCachedTextView.flush();
        scrollToBottom();
    }

    private void logClear() {
        mCachedTextView.clear();
        mFullSummary.delete(0, mFullSummary.length());
        mSummary.delete(0, mSummary.length());
        mFailedSummary.delete(0, mFailedSummary.length());
    }

    protected String getFullLogs() {
        return mFullSummary.toString();
    }

    private void startAutoThread() {
        mThreadEnabled = true;
        mAutoThread = new Thread(this);
        mAutoThread.start();
    }

    private void stopAutoThread() {
        try {
            if (mAutoThread != null) {
                log("Disable background test thread.");
                new RuntimeException("Disable background test thread.").printStackTrace();
                mThreadEnabled = false;
                mAutoThread.interrupt();
                mAutoThread.join(100);
                mAutoThread = null;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    protected void setTestIndexText(int newTestIndex) {
        if (newTestIndex >= 0) {
            mSingleTestIndex.setText(String.valueOf(newTestIndex));
        } else {
            mSingleTestIndex.setText("");
        }
    }

    private void updateTestIndex() {
        CharSequence chars = mSingleTestIndex.getText();
        String text = chars.toString();
        int testIndex = -1;
        String trimmed = chars.toString().trim();
        if (trimmed.length() > 0) {
            try {
                testIndex = Integer.parseInt(text);
            } catch (NumberFormatException e) {
                mActivity.showErrorToast("Badly formated callback size: " + text);
                mSingleTestIndex.setText("");
            }
        }
        mActivity.setSingleTestIndex(testIndex);
    }

    protected void startTest() {
        updateTestIndex();
        updateStartStopButtons(true);
        startAutoThread();
    }

    public void stopTest() {
        stopAutoThread();
    }

    // Only call from UI thread.
    public void onTestFinished() {
        updateStartStopButtons(false);
        mShareButton.setEnabled(true);
    }

    public static String getTimestampString() {
        DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
        Date now = Calendar.getInstance().getTime();
        return df.format(now);
    }

    // Share text from log via GMail, Drive or other method.
    public void shareResult() {
        Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
        sharingIntent.setType("text/plain");

        String subjectText = "OboeTester-" + mActivity.getTestName()
                + "-" + Build.MANUFACTURER
                + "-" + Build.MODEL
                + "-" + getTimestampString();
        subjectText = subjectText.replace(' ', '-');
        sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subjectText);

        String shareBody = mAutoTextView.getText().toString();
        sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody);

        mActivity.startActivity(Intent.createChooser(sharingIntent, "Share using:"));
    }

    @Override
    public void run() {
        logClear();
        log("=== STARTED at " + new Date());
        log(mActivity.getTestName());
        log(MainActivity.getVersionText());
        log(Build.MANUFACTURER + ", " + Build.MODEL + ", " + Build.PRODUCT);
        log(Build.DISPLAY);
        appendFailedSummary("Summary\n");
        mTestCount = 0;
        mPassCount = 0;
        mFailCount = 0;
        try {
            mActivity.runTest();
            log("Tests finished without exception.");
        } catch(Exception e) {
            log("EXCEPTION: " + e.getMessage());
        } finally {
            mActivity.stopTest();
            if (!mThreadEnabled) {
                log("== TEST STOPPED ==");
            }
            log("\n==== SUMMARY ========");
            log(mSummary.toString());
            if (mFailCount > 0) {
                log("These tests FAILED:");
                log(mFailedSummary.toString());
                log("------------");
            } else if (mPassCount > 0) {
                log("All " + mPassCount + " tests PASSED.");
            } else {
                log("No tests were run!");
            }
            int skipped = mTestCount - (mPassCount + mFailCount);
            log(mPassCount + " passed. "
                    + mFailCount + " failed. "
                    + skipped + " skipped. ");
            log("== FINISHED at " + new Date());

            flushLog();

            mActivity.saveIntentLog();

            mActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    onTestFinished();
                    flushLog();
                }
            });
        }
    }

}
