/*
 * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
 *
 * 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
 *
 *     http://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.palantir.conjure.java.okhttp;

import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;

/**
 * A 'fair' queue which prioritizes by submitting thread. Requests are executed either synchronously
 * or asynchronously, more frequently synchronously. In the former case, one will block on the response;
 * in the latter case, one might submit many requests from a single thread, which if consumed in a fifo
 * fashion will potentially starve the shared resource.
 * <p>
 * Here, we hand out permits in a per-thread fifo, globally round robin fashion, so a thread which makes
 * many requests will see its requests fairly prioritized behind other threads.
 */
@NotThreadSafe
final class ThreadWorkQueue<T> {
    private final Set<Long> activeQueuedThreads = new LinkedHashSet<>();
    private final Map<Long, Queue<T>> queuedRequests = new HashMap<>();

    boolean isEmpty() {
        return activeQueuedThreads.isEmpty();
    }

    void add(T element) {
        long threadId = Thread.currentThread().getId();
        if (!activeQueuedThreads.contains(threadId)) {
            activeQueuedThreads.add(threadId);
        }
        queue(threadId).add(element);
    }

    T remove() {
        long id = nextThread();
        Queue<T> workQueue = queuedRequests.get(id);
        T result = workQueue.remove();
        if (workQueue.isEmpty()) {
            queuedRequests.remove(id);
        } else {
            activeQueuedThreads.add(id);
        }
        return result;
    }

    private long nextThread() {
        Iterator<Long> iterator = activeQueuedThreads.iterator();
        long result = iterator.next();
        iterator.remove();
        return result;
    }

    private Queue<T> queue(long id) {
        return queuedRequests.computeIfAbsent(id, key -> new ArrayDeque<>(2));
    }
}

@@ -705,34 +705,8 @@
 ue;%0A
-import java.util.HashMap;%0A
 impo
@@ -755,19 +755,19 @@
 nkedHash
-Set
+Map
 ;%0Aimport
@@ -810,30 +810,8 @@
 ue;%0A
-import java.util.Set;%0A
 impo
@@ -1475,81 +1475,8 @@
 %3E %7B%0A
-    private final Set%3CLong%3E activeQueuedThreads = new LinkedHashSet%3C%3E();%0A
     
@@ -1530,16 +1530,22 @@
 s = new 
+Linked
 HashMap%3C
@@ -1589,34 +1589,29 @@
  return 
-activeQueuedThread
+queuedRequest
 s.isEmpt
@@ -1708,120 +1708,8 @@
 ();%0A
-        if (!activeQueuedThreads.contains(threadId)) %7B%0A            activeQueuedThreads.add(threadId);%0A        %7D%0A
     
@@ -1778,39 +1778,23 @@
     
-long id = nextThread();%0A       
+Map.Entry%3CLong,
  Que
@@ -1798,16 +1798,17 @@
 Queue%3CT%3E
+%3E
  workQue
@@ -1816,29 +1816,17 @@
 e = 
-queuedRequests.get(id
+nextTask(
 );%0A 
@@ -1853,16 +1853,27 @@
 rkQueue.
+getValue().
 remove()
@@ -1886,16 +1886,17 @@
     if (
+!
 workQueu
@@ -1897,16 +1897,27 @@
 rkQueue.
+getValue().
 isEmpty(
@@ -1952,75 +1952,52 @@
 sts.
-remove(id);%0A        %7D else %7B%0A            activeQueuedThreads.add(id
+put(workQueue.getKey(), workQueue.getValue()
 );%0A 
@@ -2051,23 +2051,42 @@
 ate 
-long
+Map.Entry%3CLong, Queue%3CT%3E%3E
  nextT
-hread
+ask
 () %7B
@@ -2107,69 +2107,117 @@
 tor%3C
-Long%3E iterator = activeQueuedThreads.iterator();%0A        long
+Map.Entry%3CLong, Queue%3CT%3E%3E%3E iterator = queuedRequests.entrySet().iterator();%0A        Map.Entry%3CLong, Queue%3CT%3E%3E
  res

Simplify threadworkqueue (#1006)