/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
/* global require */
/* global OV */
/* global CL */
/* global self */
/* jshint -W020 */

var Q = require('q');
var cryptolib = require('../../crypto/cryptolib.js');
var policies = require('../../crypto/policies.js')(cryptolib.Config);

var model = require('./model/model.coffee');

// ov client api export
var _CL = null;
var OV = {

    // private initialization for unit tests
    // do not use: low entropy!

    _init: function(userCallback) {

        'use strict';

        cryptolib.cryptoPRNG.startEntropyCollection();
        cryptolib.cryptoPRNG.stopEntropyCollectionAndCreatePRNG();
        var cryptolibCallback = function(box) {
            _CL = box;
            if (typeof self !== 'undefined') {
                self.CL = _CL;
            } else {
                global.CL = _CL;
            }
            if (typeof userCallback === 'function') {
                userCallback();
            }
        };

        cryptolibCallback.policies = policies;
        cryptolibCallback.prng = cryptolib.cryptoPRNG.getPRNG();
        cryptolib('*', cryptolibCallback);

    },


    initWorker: function(name, seed, config) {

        'use strict';

        var deferred = Q.defer();

        if (self) {
            self.name = name;
        }
        cryptolib.cryptoPRNG.createPRNGFromSeed(seed);

        OV.config(JSON.parse(config));

        var cryptolibCallback = function(box) {
            _CL = box;
            if (typeof self !== 'undefined') {
                self.CL = _CL;
            } else {
                global.CL = _CL;
            }
            console.log('**', name, 'worker ready!');
            deferred.resolve('ok');
        };

        cryptolibCallback.policies = policies;
        cryptolibCallback.prng = cryptolib.cryptoPRNG.getPRNG();
        cryptolib('*', cryptolibCallback);

        return deferred.promise;

    },


    // High level API

    // main vote cycle
    authenticate: require('./client/authenticate.js').authenticate,
    requestBallot: require('./client/request-ballot.js').requestBallot,
    sendVote: require('./client/send-vote.js').sendVote,
    castVote: require('./client/cast-vote.js').castVote,
    requestReceipt: require('./client/request-receipt.js'),

    // cycle completions
    requestChoiceCodes: require('./client/request-choicecodes.js'),
    requestVoteCastCode: require('./client/request-votecastcode.js'),

    // ballot object model
    // NOTE: all models marked for deprecation
    // ballot parsing will be done by voting portal
    // Do not add/extend more models here!
    model: model,
    Ballot: model.ballot.Ballot,
    ListsAndCandidates: model.ballot.ListsAndCandidates,
    Options: model.ballot.Options,
    Question: model.ballot.Question,
    Option: model.ballot.Option,
    List: model.ballot.List,
    Candidate: model.ballot.Candidate,

    // session and config
    config: require('./client/config.js'),
    session: require('./client/session.js'),
    updateConfig: function(configData) {
        'use strict';
        OV.config(JSON.parse(configData));
    },
    getAuthentication: function() {
        'use strict';
        return OV.session('authenticationToken');
    },
    getSerializedEncryptionParams: function() {
        'use strict';
        var ep = OV.session('encParams');
        return {
            serializedP: ep.serializedP,
            serializedG: ep.serializedG,
            serializedOptionsEncryptionKey: ep.serializedOptionsEncryptionKey,
            serializedChoiceCodesEncryptionKey: ep.serializedChoiceCodesEncryptionKey
        };
    },
    getSerializedVerificationCardSecret: function() {
        'use strict';
        return OV.session('verificationCardSecret').toString();
    },
    getSessionData: function(key) {
        'use strict';
        return OV.session(key);
    },
    getRandomInt: function(length) {
        'use strict';
        var prng = new CL.primitives.securerandom.factory.SecureRandomFactory().getCryptoRandomInteger();
        return prng.nextRandom(length).toString();
    },
    getRandomBytes: function(length) {
        'use strict';
        var prng = new CL.primitives.securerandom.factory.SecureRandomFactory().getCryptoRandomBytes();
        return forge.util.encode64(prng.nextRandom(length));
    },

    // message processing API / vote cycle
    processInformationsResponse: require('./client/request-ballot.js').processInformationsResponse,
    processTokensResponse: require('./client/request-ballot.js').processTokensResponse,
    createVoteRequest: require('./client/send-vote.js').createVoteRequest,
    processVoteResponse: require('./client/send-vote.js').processVoteResponse,
    createConfirmRequest: require('./client/cast-vote.js').createConfirmRequest,
    processConfirmResponse: require('./client/cast-vote.js').processConfirmResponse,

    // message processing API / cycle completions
    processCastCodeResponse: require('./client/cast-vote.js').processConfirmResponse,
    processChoiceCodesResponse: require('./client/send-vote.js').processVoteResponse,

    // parsers

    BallotParser: require('./parsers/ballot-parser.js'), // marked for deprecation
    OptionsParser: require('./parsers/options.js'),
    ListsAndCandidatesParser: require('./parsers/lists-and-candidates.js'),
    parseStartVotingKey: require('./parsers/start-voting-key.js'), // marked for deprecation
    parseServerChallenge: require('./parsers/challenge.js'),
    parseBallotResponse: require('./parsers/ballot-response.js'),
    parseEncryptionParams: require('./parsers/encryption-params.js'),
    parseTokenResponse: require('./parsers/auth-token.js').validateAuthTokenResponse,
    parseToken: require('./parsers/auth-token.js').validateAuthToken,
    parseCastResponse: require('./parsers/cast-response.js'),
    parseVerificationCard: require('./parsers/verification-card.js'),
    parseCredentials: require('./parsers/credential-extractor.js'),
    parseSignedVote: require('./parsers/signed-vote.js'),
    writeInsParser: require('./parsers/write-ins.js'),

    // precomuptations
    precomputeEncrypterValues: require('./protocol/precompute/encrypter-values.js').precomputeEncryptionValues,
    deserializeEncrypterValues: require('./protocol/precompute/encrypter-values.js').deserializeEncryptionValues,
    precomputePartialChoiceCode: require('./protocol/precompute/partial-choice-codes.js').precomputePartialChoiceCode,
    getPrecomputedPartialChoiceCodes: require('./protocol/precompute/partial-choice-codes.js')
        .getPrecomputedPartialChoiceCodes,
    precomputeProofs: require('./protocol/precompute/proofs.js').precomputeProofs,

    // writein
    encode: require('./protocol/writein.js').encode,
    decode: require('./protocol/writein.js').decode,
    makeGroupElement: require('./protocol/writein.js').makeGroupElement,
    squareToEnsureIsGroupElement: require('./protocol/writein.js').squareToEnsureIsGroupElement,
    sqrtN: require('./protocol/writein.js').sqrtN,

    // encryption
    encryptOptions: require('./protocol/cipher.js').encryptOptions,
    encryptOptionsAndWriteins: require('./protocol/cipher.js').encryptOptionsAndWriteins,
    encryptPartialChoiceCodes: require('./protocol/cipher.js').encryptPartialChoiceCodes,

    // decryption
    symmetricDecrypt: require('./protocol/cipher.js').symmetricDecrypt,

    // proofs
    generateSchnorrProof: require('./protocol/proof.js').generateSchnorrProof,
    generateCipherTextExponentiations: require('./protocol/proof.js').generateCipherTextExponentiations,
    generateExponentiationProof: require('./protocol/proof.js').generateExponentiationProof,
    generatePlaintextEqualityProof: require('./protocol/proof.js').generatePlaintextEqualityProof,

    // choice codes
    generatePartialChoiceCodes: require('./protocol/partial-choice-codes.js'),
    generateConfirmationKey: require('./protocol/confirmation-key.js'),
    generateBallotCastingKey: require('./protocol/ballot-casting-key.js'),

    // siganature
    signData: require('./signature/signature.js').signData,
    verifySignature: require('./signature/signature.js').verifySignature,

    // certificate chain validation
    validateCertificateChain: require('./certificate/certificate-chain.js')

};


if (typeof self !== 'undefined') {
    self.OV = OV;
    self.CL = _CL;
} else {
    module.exports = OV;
}

/* jshint ignore: end */
