package org.torproject.artitoyvpn.vpn;

import static org.torproject.artitoyvpn.vpn.VpnStatusObservable.Status.STOPPED;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import org.torproject.artitoyvpn.utils.Utils;
import org.torproject.onionmasq.circuit.Circuit;
import org.torproject.onionmasq.circuit.CircuitStore;
import org.torproject.onionmasq.events.OnionmasqEvent;
import org.torproject.onionmasq.logging.LogObservable;

import java.util.ArrayList;

public class VpnStatusObservable {
    public enum Status {
        STOPPED,
        STARTING,
        RUNNING,
        STOPPING,
        ERROR
    }

    private final MutableLiveData<DataUsage> dataUsage;

    private final MutableLiveData<VpnStatusObservable.Status> statusLiveData;

    private final CircuitStore circuitStore;

    private static VpnStatusObservable instance;

    private VpnStatusObservable() {
        statusLiveData = new MutableLiveData<>(STOPPED);
        dataUsage = new MutableLiveData<>(new DataUsage());
        circuitStore = new CircuitStore();
    }

    public static VpnStatusObservable getInstance() {
        if (instance == null) {
            instance = new VpnStatusObservable();
        }
        return instance;
    }

    public static void update(Status status) {
        if (Utils.isRunningOnMainThread()) {
            instance.statusLiveData.setValue(status);
        } else {
            instance.statusLiveData.postValue(status);
        }
        LogObservable.getInstance().addLog(status.toString());
    }

    public static void updateDataUsage(long downstream, long upstream) {
        DataUsage lastDataUsage = instance.dataUsage.getValue();
        DataUsage updatedDataUsage = new DataUsage();
        updatedDataUsage.downstreamData = downstream;
        updatedDataUsage.upstreamData = upstream;
        long timeDelta = Math.max((updatedDataUsage.timeStamp - lastDataUsage.timeStamp) / 1000, 1);
        updatedDataUsage.upstreamDataPerSec = (updatedDataUsage.upstreamData - lastDataUsage.upstreamData) / timeDelta;
        updatedDataUsage.downstreamDataPerSec = (updatedDataUsage.downstreamData - lastDataUsage.downstreamData) / timeDelta;
        if (Utils.isRunningOnMainThread()) {
            instance.dataUsage.setValue(updatedDataUsage);
        } else {
            instance.dataUsage.postValue(updatedDataUsage);
        }
    }

    public static void resetData() {
        if (Utils.isRunningOnMainThread()) {
            instance.dataUsage.setValue(new DataUsage());
        } else {
            instance.dataUsage.postValue(new DataUsage());
        }
        instance.circuitStore.reset();
    }

    public static LiveData<DataUsage> getDataUsage() {
        return instance.dataUsage;
    }

    public static LiveData<Status> getStatus() {
        return instance.statusLiveData;
    }

    public static void handleConnectionEvent(OnionmasqEvent event) {
        instance.circuitStore.handleEvent(event);
    }

    public static @NonNull ArrayList<Circuit> getCircuitListForUid(int appUID) {
        return instance.circuitStore.getCircuitsForAppUid(appUID);
    }

}
