content: /*
   * Copyright (C) 2015 AChep@xda <artemchep@gmail.com>
   *
   * 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 General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   * MA  02110-1301, USA.
   */
  package com.achep.base.async;
  
  import androidx.annotation.NonNull;
  
  import com.achep.base.interfaces.IThreadFinishable;
  
  import java.util.Iterator;
  import java.util.Queue;
  import java.util.concurrent.ConcurrentLinkedQueue;
  
  import timber.log.Timber;
  
  /**
   * Created by Artem Chepurnoy on 17.04.2015.
   */
  public abstract class TaskQueueThread<T> extends Thread implements IThreadFinishable {
  
      private static final String TAG = "TaskQueueThread";
  
      private final Queue<T> mQueue = new ConcurrentLinkedQueue<>();
      private boolean mWaiting = false;
  
      protected boolean mRunning = true;
  
      @Override
      public void finish() {
          finish(false);
      }
  
      public void finish(boolean clearAllTasks) {
          if (isAlive()) {
              mRunning = false;
              if (clearAllTasks) {
                  synchronized (this) {
                      mQueue.clear();
                      if (mWaiting) notifyAll();
                  }
              }
              while (true) {
                  try {
                      join();
                      break;
                  } catch (InterruptedException e) { /* pretty please! */ }
              }
          }
      }
  
      @Override
      public void run() {
          Timber.tag(TAG).d("Starting thread...");
          super.run();
  
          Queue<T> queue = new ConcurrentLinkedQueue<>();
          while (mRunning) {
              synchronized (this) {
                  if (mQueue.isEmpty())
                      try {
                          // Wait for a next #sendEvent(Event),
                          // where this thread will be unlocked.
                          mWaiting = true;
                          wait();
                      } catch (InterruptedException ignored) {
                      } finally {
                          mWaiting = false;
                      }
  
                  // Move all pending events to a local copy, so we don't need
                  // to block main queue.
                  while (!mQueue.isEmpty()) {
                      queue.add(mQueue.poll());
                  }
              }
  
              if (isLost()) {
                  mRunning = false;
                  break;
              }
  
              Iterator<T> iterator = queue.iterator();
              while (iterator.hasNext()) {
                  T object = iterator.next();
                  // ~~
                  onHandleTask(object);
                  // ~~
                  iterator.remove();
              }
          }
  
  
          Timber.tag(TAG).d("Stopping thread...");
      }
  
      public void sendTask(@NonNull T object) {
          synchronized (this) {
              mQueue.add(object);
  
              // Release the thread lock if needed.
              if (mWaiting) notifyAll();
          }
      }
  
      public void clearTask(@NonNull T object) {
          synchronized (this) {
              mQueue.remove(object);
          }
      }
  
      public void clearAllTasks() {
          synchronized (this) {
              mQueue.clear();
          }
      }
  
      protected abstract void onHandleTask(T object);
  
      protected abstract boolean isLost();
  
  }
  