/*
 * 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
 */
package com.scytl.products.ov.channel.cc.commons.keymanagement;

import java.time.Duration;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Implementation of {@link Cache}.
 */
class CacheImpl implements Cache {
    private final ConcurrentMap<KeysId, KeysContainer> containers =
        new ConcurrentHashMap<>();

    private final Timer timer;

    private final Duration expiryPeriod;

    /**
     * Constructor.
     * 
     * @param expiryPeriod
     */
    public CacheImpl(Duration expiryPeriod) {
        this(new Timer(), expiryPeriod);
    }

    /**
     * Constructor. For internal use only.
     * 
     * @param timer
     * @param expiryPeriod
     */
    CacheImpl(Timer timer, Duration expiryPeriod) {
        this.timer = timer;
        this.expiryPeriod = expiryPeriod;
    }

    @Override
    public ChoiceCodeKeys getChoiceCodeKeys(String electionEventId,
            String verificationCardSetId) {
        KeysId id = KeysId.getChoiceCodeInstance(electionEventId,
            verificationCardSetId);
        return (ChoiceCodeKeys) doGet(id);
    }

    @Override
    public ElectionSigningKeys getElectionSigningKeys(
            String electionEventId) {
        KeysId id = KeysId.getElectionSigningInstance(electionEventId);
        return (ElectionSigningKeys) doGet(id);
    }

    @Override
    public MixingKeys getMixingKeys(String electionEventId,
            String electoralAuthorityId) {
        KeysId id = KeysId.getMixingInstance(electionEventId,
            electoralAuthorityId);
        return (MixingKeys) doGet(id);
    }

    @Override
    public void putChoiceCodeKeys(String electionEventId,
            String verificationCardSetId, ChoiceCodeKeys keys) {
        KeysId id = KeysId.getChoiceCodeInstance(electionEventId,
            verificationCardSetId);
        doPut(id, keys);
    }

    @Override
    public void putElectionSigningKeys(String electionEventId,
            ElectionSigningKeys keys) {
        KeysId id = KeysId.getElectionSigningInstance(electionEventId);
        doPut(id, keys);
    }

    @Override
    public void putMixingKeys(String electionEventId,
            String electoralAuthorityId, MixingKeys keys) {
        KeysId id = KeysId.getMixingInstance(electionEventId,
            electoralAuthorityId);
        doPut(id, keys);
    }

    @Override
    public void shutdown() {
        timer.cancel();
    }

    @Override
    public void startup() {
        long period = expiryPeriod.toMillis();
        timer.schedule(new RemoveExpiredTask(), period, period);
    }

    private Object doGet(KeysId id) {
        KeysContainer container = containers.get(id);
        return container != null ? container.getKeys() : null;
    }

    private void doPut(KeysId id, Object keys) {
        containers.put(id, new KeysContainer(keys, expiryPeriod));
    }

    private void removeExpired() {
        Iterator<KeysContainer> iterator = containers.values().iterator();
        while (iterator.hasNext()) {
            KeysContainer container = iterator.next();
            if (container.isExpired()) {
                iterator.remove();
            }
        }
    }

    private class RemoveExpiredTask extends TimerTask {
        @Override
        public void run() {
            removeExpired();
        }
    }
}
