package org.bdware.sc.conn;

import com.google.gson.JsonPrimitive;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import org.bdware.sc.ContractResult;
import org.bdware.sc.ContractResult.Status;
import org.bdware.sc.util.JsonUtil;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SyncResult {
    public static final HashedWheelTimer HASHED_WHEEL_TIMER =
            new HashedWheelTimer(
                    Executors.defaultThreadFactory(),
                    5,
                    TimeUnit.MILLISECONDS,
                    2);
    static final String TimeoutStr =
            JsonUtil.toJson(new ContractResult(Status.Error, new JsonPrimitive("Timeout!")));
    //    private static final org.apache.logging.log4j.Logger LOGGER =
    //    org.apache.logging.log4j.LogManager.getLogger(SyncResult.class);
    Map<Long, ResultCallback> waitObj = new ConcurrentHashMap<>();

    public void wakeUp(long requestID, String result) {
        ResultCallback ob = waitObj.get(requestID);
        waitObj.remove(requestID);
        if (ob != null) {
            ob.onResult(result);
        } else {
            //   logger.info("[Missing callback]" + requestID);
        }
    }

    public void sleep(final long requestID, ResultCallback cb) {
        TimerTask tt = timeout -> wakeUp(requestID, TimeoutStr);
        // TODO: for now, if executing confidential function, need to increase this value to at
        HASHED_WHEEL_TIMER.newTimeout(tt, 10, TimeUnit.SECONDS);
    }

    public void sleepWithTimeout(final Long requestID, ResultCallback cb, int timeOut) {
        if (!waitObj.containsKey(requestID)) {
            waitObj.put(requestID, cb);
        }
        TimerTask tt =
                new TimerTask() {
                    @Override
                    public void run(Timeout timeout) {
                        wakeUp(requestID, TimeoutStr);
                    }
                };
        HASHED_WHEEL_TIMER.newTimeout(tt, timeOut, TimeUnit.SECONDS);
    }

    public String syncSleep(long requestID, ContractResult cr, ResultCallback cb) {
        sleep(requestID, cb);
        synchronized (cb) {
            try {
                if (waitObj.containsKey(requestID))
                    cb.wait(10000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (cr.status == Status.Success) {
            return null == cr.result ? null : cr.result.getAsString();
        } else {
            return JsonUtil.toJson(cr);
        }
    }
}