/*
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.google.ce.media.contentuploader.exec;

import com.google.ce.media.contentuploader.config.EnvConfig;
import com.google.ce.media.contentuploader.message.TaskInfo;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created in gcs-uploader on 2020-01-10.
 */
@Component
public class UploadTaskPool {
  private final EnvConfig envConfig;

  private final ExecutorService executorService;

  private final ExecutorService stitcherService = Executors.newFixedThreadPool(5);

  private final BlockingQueue<Runnable> blockingQueue;

  private final List<Thread> uploaderThreads;

  private final Object QUEUE_LOCK = new Object();

  private final Map<TaskInfo, Runnable> runnableMap = new HashMap<>();


  public UploadTaskPool(EnvConfig envConfig) {
    this.envConfig = envConfig;

    executorService = Executors.newFixedThreadPool(envConfig.getTaskWorkerPoolSize());
    blockingQueue = new ArrayBlockingQueue<>(1);
//    blockingQueue = new ArrayBlockingQueue<>(envConfig.getUploadQueueSize());
    uploaderThreads = new ArrayList<>(envConfig.getUploadWorkerPoolSize());

    for (int i = 0; i < envConfig.getUploadWorkerPoolSize(); i++) {
      Thread thread = new Thread(new UploaderRunnable(blockingQueue));
      uploaderThreads.add(thread);
      thread.start();
    }
  }

  public void enqueue(UploadTask uploadTask) {
    synchronized (QUEUE_LOCK) {
      executorService.submit(uploadTask);
    }
  }

  public void enqueue(CompositeUploadTask uploadTask) {
    synchronized (QUEUE_LOCK) {
      executorService.submit(uploadTask);
    }
  }

  public void enqueue(StitchTask stitchTask) {
    stitcherService.submit(stitchTask);
  }

  public void enqueue(BlobUploadTask blobUploadTask) throws InterruptedException {
    blockingQueue.put(blobUploadTask);
  }

  private final class UploaderRunnable implements Runnable {
    private final BlockingQueue<Runnable> queue;

    private UploaderRunnable(BlockingQueue<Runnable> queue) {
      this.queue = queue;
    }

    @Override
    public void run() {
      while (true) {
        try {
          Runnable current = queue.take();
          current.run();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  }



}

@@ -2757,24 +2757,70 @@
 able%3E queue;
+%0A    private final Object LOCK = new Object();
 %0A%0A    privat
@@ -2991,146 +2991,801 @@
     
-Runnable current = queue.take();%0A          current.run();%0A        %7D catch (Exception e) %7B%0A          e.printStackTrace();%0A        %7D%0A      %7D
+int runningCount = 0;%0A          boolean ok = false;%0A          Runnable current = queue.take();%0A          while (runningCount %3C 3 && !ok) %7B%0A            try %7B%0A              runningCount++;%0A              ok = runCurrent(current);%0A            %7D catch (Exception e) %7B%0A              e.printStackTrace();%0A              synchronized (LOCK) %7B%0A                System.out.println(%22%3E%3E%3E WAITING due to exception: %22 + e.getMessage());%0A                LOCK.wait(runningCount * 1000L);%0A                System.out.println(%22%3E%3E%3E RESUMING from exception: %22 + e.getMessage());%0A              %7D%0A            %7D%0A          %7D%0A        %7D catch (Exception e) %7B%0A          e.printStackTrace();%0A        %7D%0A      %7D%0A    %7D%0A%0A    private boolean runCurrent(Runnable current) throws Exception %7B%0A      current.run();%0A      return true;
 %0A   

update with wait and retry logic