package com.ms_square.debugoverlay.modules;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
import android.util.Log;

import com.ms_square.debugoverlay.OverlayViewDelegateFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.atomic.AtomicReference;

public class LogcatModule extends BaseOverlayModule<LogcatLine> {

    private static final String TAG = LogcatModule.class.getSimpleName();

    private static final int LINE_UPDATED = 10000;

    private final Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message m) {
            if (m.what == LINE_UPDATED) {
                latestLine = (LogcatLine) m.obj;
                notifyObservers();
            }
        }
    };

    private ReaderThread logcatReaderThread;

    private LogcatLine latestLine;

    public LogcatModule() {
        super(0);
    }

    public LogcatModule(@NonNull OverlayViewDelegateFactory<LogcatLine> viewDelegateFactory) {
        super(0, viewDelegateFactory);
    }

    @Override
    public void start() {
        if (logcatReaderThread == null || !logcatReaderThread.isAlive()) {
            logcatReaderThread = new ReaderThread();
            logcatReaderThread.start();
        }
    }

    @Override
    public void stop() {
        handler.removeMessages(LINE_UPDATED);
        if (logcatReaderThread != null) {
            logcatReaderThread.cancel();
            try {
                logcatReaderThread.join();
            } catch (InterruptedException ignore) {}
            logcatReaderThread = null;
        }
    }

    @Override
    protected OverlayViewDelegateFactory<LogcatLine> getDefaultFactory() {
        return new LogcatViewDelegate.Factory();
    }

    @Override
    protected LogcatLine getLatestData() {
        return latestLine;
    }

    class ReaderThread extends Thread {

        private AtomicReference<Process> logcatProcess = new AtomicReference<>();
        private BufferedReader logcatReader;

        @Override
        public void run() {
            try {
                clearLogcatBuffer();
                openLogcatProcess();
                openLogcatReader();
                while (!Thread.currentThread().isInterrupted()) {
                    String logLine;
                    try {
                        logLine = logcatReader.readLine();
                    } catch (IOException ie) {
                        Log.w(TAG, "Failed reading logcat message; allow thread to exit - " + ie.getMessage());
                        break;
                    }
                    if (logLine != null && logLine.length() > 0 && !logLine.startsWith("--------- beginning of ")) {
                        handler.obtainMessage(LINE_UPDATED, new LogcatLine(logLine)).sendToTarget();
                    }
                }
            } finally {
                closeLogcatProcess();
                closeLogcatReader();
            }
        }

        public void cancel() {
            interrupt();
            closeLogcatProcess();
        }

        private void openLogcatProcess() {
            try {
                logcatProcess.compareAndSet(null, Runtime.getRuntime().exec(new String[]{"logcat", "-v", "threadtime"}));
            } catch (IOException e) {
                Log.w(TAG, "Can not execute logcat - " + e.getMessage());
            }
        }

        private void openLogcatReader() {
            Process process = logcatProcess.get();
            if (process != null) {
                if (logcatReader == null) {
                    logcatReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                }
            }
        }

        private void closeLogcatProcess() {
            Process process = logcatProcess.getAndSet(null);
            if (process != null) {
                process.destroy();
            }
        }

        private void closeLogcatReader() {
            try {
                if (logcatReader != null) {
                    logcatReader.close();
                    logcatReader = null;
                }
            } catch (IOException ignore) {}
        }

        private void clearLogcatBuffer() {
            Process process = null;
            try {
                process = Runtime.getRuntime().exec(new String[] {"logcat", "-c"});
                process.waitFor();
            } catch (InterruptedException | IOException e) {
                Log.w(TAG, "Clearing logcat buffer failed - " + e.getMessage());
            } finally {
                if (process != null) {
                    process.destroy();
                }
            }
        }
    }
}

@@ -347,57 +347,50 @@
 er;%0A
+%0A
 import 
-java.util.concurrent.atomic.AtomicReferenc
+static java.lang.Runtime.getRuntim
 e;%0A%0A
@@ -2016,32 +2016,15 @@
 ate 
-AtomicReference%3C
 Process
-%3E
  log
@@ -2037,34 +2037,8 @@
 cess
- = new AtomicReference%3C%3E()
 ;%0A  
@@ -3184,35 +3184,51 @@
 ) %7B%0A            
-try
+synchronized (this)
  %7B%0A             
@@ -3226,24 +3226,28 @@
             
+if (
 logcatProces
@@ -3251,28 +3251,85 @@
 cess
-.compareAndSet(null,
+ == null) %7B%0A                    try %7B%0A                        logcatProcess =
  Run
@@ -3394,19 +3394,26 @@
 dtime%22%7D)
-)
 ;%0A
+        
         
@@ -3450,32 +3450,40 @@
                 
+    
+    
 Log.w(TAG, %22Can 
@@ -3516,32 +3516,72 @@
 .getMessage());%0A
+                    %7D%0A                %7D%0A
             %7D%0A  
@@ -3610,32 +3610,34 @@
 te void 
-open
+close
 Logcat
-Reader
+Process
 () %7B%0A   
@@ -3649,47 +3649,34 @@
     
-Process process = logcatProcess.get();%0A
+synchronized (this) %7B%0A    
     
@@ -3679,33 +3679,39 @@
             if (
-p
+logcatP
 rocess != null) 
@@ -3732,35 +3732,36 @@
     
-if (
+    
 logcat
-Reader == null) %7B
+Process.destroy();
 %0A   
@@ -3787,84 +3787,22 @@
 gcat
-Reader = new BufferedReader(new InputStreamReader(process.getInputStream()))
+Process = null
 ;%0A  
@@ -3863,34 +3863,32 @@
 te void 
-close
+open
 Logcat
-Process
+Reader
 () %7B%0A   
@@ -3900,57 +3900,81 @@
     
-Process process = logcatProcess.getAndSet(null);%0A
+synchronized (this) %7B%0A                if (logcatProcess != null) %7B%0A      
     
@@ -3977,37 +3977,44 @@
             
+  
 if (
-process !
+logcatReader =
 = null) %7B%0A  
@@ -4027,34 +4027,153 @@
         
+ 
-process.destroy();
+       logcatReader = new BufferedReader(new InputStreamReader(logcatProcess.getInputStream()));%0A                    %7D%0A                %7D
 %0A       
@@ -4570,32 +4570,24 @@
   process = 
-Runtime.
 getRuntime()

fix synchronization in Logcat module