package meerkat.voting.output;

import com.google.protobuf.BoolValue;
import com.google.protobuf.ByteString;
import meerkat.crypto.DigitalSignature;
import meerkat.crypto.DigitalSignatureGenerator;
import meerkat.pollingstation.PollingStationScanner;
import meerkat.pollingstation.ScannerClientAPI;
import meerkat.protobuf.PollingStation;
import meerkat.protobuf.PollingStation.ScannedData;
import meerkat.protobuf.Voting.SignedEncryptedBallot;
import meerkat.rest.Constants;
import meerkat.rest.ProtobufMessageBodyReader;
import meerkat.rest.ProtobufMessageBodyWriter;
import meerkat.voting.output.outputcommands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.client.*;
import javax.ws.rs.core.Response;
import java.io.IOException;

import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;

/**
 * A ballot output device for the network. It simply sends details over the wire
 */
public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice {

    private static final Logger logger = LoggerFactory.getLogger(NetworkVirtualPrinter.class);
    private ByteString channelIdentifier;
    private SignedEncryptedBallot signedEncryptedBallot;

    PollingStationScanner.ScannerClient scannerClient;

    public NetworkVirtualPrinter(PollingStation.ConnectionServerData serverData, DigitalSignature printerSigner) {
        super();
        logger.info("A NetworkVirtualPrinter is constructed");
        scannerClient = new ScannerClientAPI(printerSigner);
        if (!scannerClient.connect(serverData)) {
            logger.error("Couldn't connect to polling station scan server!");
            // TODO: Use a checked exception?
            throw new RuntimeException("Connection Error");
        }
        resetState();
    }


    /**
     * The NetworkVirtualPrinter actually does nothing for committing.
     * It simply keeps the ballot details for later.
     * When the voter chooses to Cast the ballot, these details are sent over the wire.
     * @param command a CommitOutputCommand with the signed encryption of the ballot
     */
    @Override
    public void doCommitToBallot(CommitOutputCommand command) {
        logger.debug("entered method doCommitToBallot");
        channelIdentifier = command.getChannelIdentifierByteString();
        signedEncryptedBallot = command.getSignedEncryptedBallot();
        command.getCallback().onSuccess(null);
    }


    /**
     * The NetworkVirtualPrinter actually does nothing for auditing.
     * @param command a AuditOutputCommand with the details and the callback
     */
    @Override
    public void doAudit(AuditOutputCommand command) {
        logger.debug("entered method doAudit");
        resetState();
        command.getCallback().onSuccess(null);
    }


    /**
     * This is where the magic happens. The signed encrypted ballot is transmitted over the wire
     * @param command a CastOutputCommand with the details and the callback
     */
    @Override
    public void doCastBallot(CastOutputCommand command) {
        logger.debug("entered method doCastBallot");
        PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.newBuilder()
                .setChannel(channelIdentifier)
                .setSignedEncryptedBallot(this.signedEncryptedBallot)
                .build();

        boolean b = scannerClient.newScan(scannedBallot);

        resetState();

        if (b) {
            command.getCallback().onSuccess(null);
        }
        else {
            command.getCallback().onFailure(new IOException());
        }
    }


    /**
     * The NetworkVirtualPrinter actually does nothing for canceling.
     * @param command a CancelOutputCommand with the callback
     */
    @Override
    public void doCancel(CancelOutputCommand command) {
        logger.debug("entered method doCancel");
        resetState();
        command.getCallback().onSuccess(null);
    }


    private void resetState() {
        channelIdentifier = null;
        signedEncryptedBallot = null;
    }

}
