class QuorumState {

    public void initialize(OffsetAndEpoch logEndOffsetAndEpoch) throws IOException, IllegalStateException {
        // We initialize in whatever state we were in on shutdown. If we were a leader
        // or candidate, probably an election was held, but we will find out about it
        // when we send Vote or BeginEpoch requests.

        ElectionState election;
        try {
            election = store.readElectionState();
            if (election == null) {
                election = ElectionState.withUnknownLeader(0, voters);
            }
        } catch (final IOException e) {
            // For exceptions during state file loading (missing or not readable),
            // we could assume the file is corrupted already and should be cleaned up.
            log.warn("Clearing local quorum state store after error loading state {}",
                store.toString(), e);
            store.clear();
            election = ElectionState.withUnknownLeader(0, voters);
        }

        final EpochState initialState;
        if (!election.voters().isEmpty() && !voters.equals(election.voters())) {
            throw new IllegalStateException("Configured voter set: " + voters
                + " is different from the voter set read from the state file: " + election.voters() +
                ". Check if the quorum configuration is up to date, " +
                "or wipe out the local state file if necessary");
        } else if (election.epoch < logEndOffsetAndEpoch.epoch) {
            log.warn("Epoch from quorum-state file is {}, which is " +
                "smaller than last written epoch {} in the log",
                election.epoch, logEndOffsetAndEpoch.epoch);
            initialState = new UnattachedState(
                time,
                logEndOffsetAndEpoch.epoch,
                voters,
                randomElectionTimeoutMs()
            );
        } else if (election.isLeader(localId)) {
            // If we were previously a leader, then we will start out as unattached
            // in the same epoch. This protects the invariant that each record
            // is uniquely identified by offset and epoch, which might otherwise
            // be violated if unflushed data is lost after restarting.
            initialState = new UnattachedState(
                time,
                election.epoch,
                voters,
                randomElectionTimeoutMs()
            );
        } else if (election.isCandidate(localId)) {
            initialState = new CandidateState(
                time,
                localId,
                election.epoch,
                voters,
                1,
                randomElectionTimeoutMs()
            );
        } else if (election.hasVoted()) {
            initialState = new VotedState(
                time,
                election.epoch,
                election.votedId(),
                voters,
                randomElectionTimeoutMs()
            );
        } else if (election.hasLeader()) {
            initialState = new FollowerState(
                time,
                election.epoch,
                election.leaderId(),
                voters,
                fetchTimeoutMs
            );
        } else {
            initialState = new UnattachedState(
                time,
                election.epoch,
                voters,
                randomElectionTimeoutMs()
            );
        }

        transitionTo(initialState);
    }

    public  QuorumState(int localId,
                       Set<Integer> voters,
                       int electionTimeoutMs,
                       int fetchTimeoutMs,
                       QuorumStateStore store,
                       Time time,
                       LogContext logContext,
                       Random random);

}