// Path: shelly/src/main/java/xiaofei/library/shelly/util/Player.java
// public class Player<T> {
// 
//     private final AugmentedListCanary<PlayerInputs> mInputs;
// 
//     private final DoubleKeyMap mStash;
// 
//     private Scheduler mScheduler;
// 
//     public Player(List<T> input) {
//         mInputs = new AugmentedListCanary<PlayerInputs>();
//         mInputs.add(new PlayerInputs((List<Object>) input, 0));
//         mStash = new DoubleKeyMap();
//         mScheduler = new DefaultScheduler();
//     }
// 
//     public void setScheduler(Scheduler scheduler) {
//         mScheduler = scheduler;
//     }
// 
//     public Scheduler getScheduler() {
//         return mScheduler;
//     }
// 
//     public void prepare(Function function) {
//         if (function instanceof StashFunction) {
//             ((StashFunction) function).setStash(mStash);
//         }
//     }
// 
//     protected Runnable getRunnable(final Tile<T, ?> tile) {
//         return new Runnable() {
//             private int mIndex = mInputs.size() - 1;
//             @Override
//             public void run() {
//                 tile.call((CopyOnWriteArrayList<T>) mInputs.get(mIndex).getInputs());
//             }
//         };
//     }
// 
//     //This method is not thread-safe! But we always call this in a single thread.
//     public final <R> Player<R> playRunnable(List<? extends Runnable> runnables) {
//         synchronized (this) {
//             if (mScheduler.isRunning()) {
//                 int size = mInputs.size();
//                 for (Runnable runnable : runnables) {
//                     mScheduler.call(new ScheduledRunnable<T>(this, runnable, size));
//                 }
//             }
//             return (Player<R>) this;
//         }
//     }
// 
//     public final <R> Player<R> playFunction(List<? extends Function1<CopyOnWriteArrayList<T>, CopyOnWriteArrayList<R>>> functions) {
//         synchronized (this) {
//             if (mScheduler.isRunning()) {
//                 int index = mInputs.add(new PlayerInputs(functions.size())) - 1;
//                 for (Function1<CopyOnWriteArrayList<T>, CopyOnWriteArrayList<R>> function : functions) {
//                     mScheduler.call(new ScheduledRunnable<T>(this, new BlockingRunnable<T, R>(this, function, index), index));
//                 }
//             }
//             return (Player<R>) this;
//         }
//     }
// 
//     //This method is not thread-safe! But we always call this in a single thread.
//     public final void play(Tile<T, ?> tile) {
//         synchronized (this) {
//             if (mScheduler.isRunning()) {
//                 playRunnable(Collections.singletonList(getRunnable(tile)));
//             }
//         }
//     }
// 
//     public void appendAt(int index, final CopyOnWriteArrayList<Object> object) {
//         mInputs.action(index, new Action<PlayerInputs>() {
//             @Override
//             public void call(PlayerInputs o) {
//                 o.getInputs().addAll(object);
//                 o.getFinishedNumber().getAndIncrement();
//             }
//         });
//     }
// 
//     public final CopyOnWriteArrayList<Object> waitForFinishing() {
//         int index = mInputs.size() - 1;
//         return mInputs.get(index, new Condition<PlayerInputs>() {
//             @Override
//             public boolean satisfy(PlayerInputs o) {
//                 return o.getFinishedNumber().get() == o.getFunctionNumber();
//             }
//         }).getInputs();
//     }
// 
//     public CopyOnWriteArrayList<Object> getInput(int index) {
//         return mInputs.get(index).getInputs();
//     }
// 
//     public AugmentedListCanary<PlayerInputs> getInputs() {
//         return mInputs;
//     }
// 
// }
// 
// Path: shelly/src/main/java/xiaofei/library/shelly/util/PlayerInputs.java
// public class PlayerInputs {
// 
//     private final CopyOnWriteArrayList<Object> mInputs;
// 
//     private final int mFunctionNumber;
// 
//     private final AtomicInteger mFinishedNumber;
// 
//     public PlayerInputs(List<Object> list, int functionNumber) {
//         if (list instanceof CopyOnWriteArrayList<?>) {
//             mInputs = (CopyOnWriteArrayList<Object>) list;
//         } else {
//             mInputs = new CopyOnWriteArrayList<Object>(list);
//         }
//         mFunctionNumber = functionNumber;
//         mFinishedNumber = new AtomicInteger();
//     }
// 
//     public PlayerInputs(int functionNumber) {
//         mInputs = new CopyOnWriteArrayList<Object>();
//         mFunctionNumber = functionNumber;
//         mFinishedNumber = new AtomicInteger();
//     }
// 
//     public AtomicInteger getFinishedNumber() {
//         return mFinishedNumber;
//     }
// 
//     public int getFunctionNumber() {
//         return mFunctionNumber;
//     }
// 
//     public CopyOnWriteArrayList<Object> getInputs() {
//         return mInputs;
//     }
// }


import xiaofei.library.concurrentutils.util.Condition;
import xiaofei.library.shelly.util.Player;
import xiaofei.library.shelly.util.PlayerInputs;
    public ScheduledRunnable(Player<R> player, Runnable runnable, int waiting) {
        mPlayer = player;
        mRunnable = runnable;
        mWaiting = waiting;
    }

    public Runnable getRunnable() {
        return mRunnable;
    }

    public void waitForInput() {
        int waitingIndex = mWaiting - 1;
        mPlayer.getInputs().wait(waitingIndex, new Condition<PlayerInputs>() {
            @Override
            public boolean satisfy(PlayerInputs o) {
                return o.getFinishedNumber().get() == o.getFunctionNumber();
            }
        });
    }

    public boolean inputSet() {
        return mPlayer.getInputs().satisfy(mWaiting - 1, new Condition<PlayerInputs>() {
            @Override
            public boolean satisfy(PlayerInputs o) {
                return o.getFinishedNumber().get() == o.getFunctionNumber();
            }
        });
    }

    @Override
// Path: shelly/src/main/java/xiaofei/library/shelly/util/Player.java
// public class Player<T> {
// 
//     private final AugmentedListCanary<PlayerInputs> mInputs;
// 
//     private final DoubleKeyMap mStash;
// 
//     private Scheduler mScheduler;
// 
//     public Player(List<T> input) {
//         mInputs = new AugmentedListCanary<PlayerInputs>();
//         mInputs.add(new PlayerInputs((List<Object>) input, 0));
//         mStash = new DoubleKeyMap();
//         mScheduler = new DefaultScheduler();
//     }
// 
//     public void setScheduler(Scheduler scheduler) {
//         mScheduler = scheduler;
//     }
// 
//     public Scheduler getScheduler() {
//         return mScheduler;
//     }
// 
//     public void prepare(Function function) {
//         if (function instanceof StashFunction) {
//             ((StashFunction) function).setStash(mStash);
//         }
//     }
// 
//     protected Runnable getRunnable(final Tile<T, ?> tile) {
//         return new Runnable() {
//             private int mIndex = mInputs.size() - 1;
//             @Override
//             public void run() {
//                 tile.call((CopyOnWriteArrayList<T>) mInputs.get(mIndex).getInputs());
//             }
//         };
//     }
// 
//     //This method is not thread-safe! But we always call this in a single thread.
//     public final <R> Player<R> playRunnable(List<? extends Runnable> runnables) {
//         synchronized (this) {
//             if (mScheduler.isRunning()) {
//                 int size = mInputs.size();
//                 for (Runnable runnable : runnables) {
//                     mScheduler.call(new ScheduledRunnable<T>(this, runnable, size));
//                 }
//             }
//             return (Player<R>) this;
//         }
//     }
// 
//     public final <R> Player<R> playFunction(List<? extends Function1<CopyOnWriteArrayList<T>, CopyOnWriteArrayList<R>>> functions) {
//         synchronized (this) {
//             if (mScheduler.isRunning()) {
//                 int index = mInputs.add(new PlayerInputs(functions.size())) - 1;
//                 for (Function1<CopyOnWriteArrayList<T>, CopyOnWriteArrayList<R>> function : functions) {
//                     mScheduler.call(new ScheduledRunnable<T>(this, new BlockingRunnable<T, R>(this, function, index), index));
//                 }
//             }
//             return (Player<R>) this;
//         }
//     }
// 
//     //This method is not thread-safe! But we always call this in a single thread.
//     public final void play(Tile<T, ?> tile) {
//         synchronized (this) {
//             if (mScheduler.isRunning()) {
//                 playRunnable(Collections.singletonList(getRunnable(tile)));
//             }
//         }
//     }
// 
//     public void appendAt(int index, final CopyOnWriteArrayList<Object> object) {
//         mInputs.action(index, new Action<PlayerInputs>() {
//             @Override
//             public void call(PlayerInputs o) {
//                 o.getInputs().addAll(object);
//                 o.getFinishedNumber().getAndIncrement();
//             }
//         });
//     }
// 
//     public final CopyOnWriteArrayList<Object> waitForFinishing() {
//         int index = mInputs.size() - 1;
//         return mInputs.get(index, new Condition<PlayerInputs>() {
//             @Override
//             public boolean satisfy(PlayerInputs o) {
//                 return o.getFinishedNumber().get() == o.getFunctionNumber();
//             }
//         }).getInputs();
//     }
// 
//     public CopyOnWriteArrayList<Object> getInput(int index) {
//         return mInputs.get(index).getInputs();
//     }
// 
//     public AugmentedListCanary<PlayerInputs> getInputs() {
//         return mInputs;
//     }
// 
// }
// 
// Path: shelly/src/main/java/xiaofei/library/shelly/util/PlayerInputs.java
// public class PlayerInputs {
// 
//     private final CopyOnWriteArrayList<Object> mInputs;
// 
//     private final int mFunctionNumber;
// 
//     private final AtomicInteger mFinishedNumber;
// 
//     public PlayerInputs(List<Object> list, int functionNumber) {
//         if (list instanceof CopyOnWriteArrayList<?>) {
//             mInputs = (CopyOnWriteArrayList<Object>) list;
//         } else {
//             mInputs = new CopyOnWriteArrayList<Object>(list);
//         }
//         mFunctionNumber = functionNumber;
//         mFinishedNumber = new AtomicInteger();
//     }
// 
//     public PlayerInputs(int functionNumber) {
//         mInputs = new CopyOnWriteArrayList<Object>();
//         mFunctionNumber = functionNumber;
//         mFinishedNumber = new AtomicInteger();
//     }
// 
//     public AtomicInteger getFinishedNumber() {
//         return mFinishedNumber;
//     }
// 
//     public int getFunctionNumber() {
//         return mFunctionNumber;
//     }
// 
//     public CopyOnWriteArrayList<Object> getInputs() {
//         return mInputs;
//     }
// }


// Path: shelly/src/main/java/xiaofei/library/shelly/runnable/ScheduledRunnable.java
import xiaofei.library.concurrentutils.util.Condition;
import xiaofei.library.shelly.util.Player;
import xiaofei.library.shelly.util.PlayerInputs;

    public ScheduledRunnable(Player<R> player, Runnable runnable, int waiting) {
        mPlayer = player;
        mRunnable = runnable;
        mWaiting = waiting;
    }

    public Runnable getRunnable() {
        return mRunnable;
    }

    public void waitForInput() {
        int waitingIndex = mWaiting - 1;
        mPlayer.getInputs().wait(waitingIndex, new Condition<PlayerInputs>() {
            @Override
            public boolean satisfy(PlayerInputs o) {
                return o.getFinishedNumber().get() == o.getFunctionNumber();
            }
        });
    }

    public boolean inputSet() {
        return mPlayer.getInputs().satisfy(mWaiting - 1, new Condition<PlayerInputs>() {
            @Override
            public boolean satisfy(PlayerInputs o) {
                return o.getFinishedNumber().get() == o.getFunctionNumber();
            }
        });
    }

    @Override
    public void run() {