package com.github.yaoshengzhe.yraft;

import com.github.yaoshengzhe.yraft.protobuf.generated.RaftProtos.AppendEntriesRequest;
import com.github.yaoshengzhe.yraft.protobuf.generated.RaftProtos.AppendEntriesResponse;
import com.github.yaoshengzhe.yraft.protobuf.generated.RaftProtos.LogEntry;
import com.github.yaoshengzhe.yraft.protobuf.generated.RaftProtos.PersistentState;
import com.github.yaoshengzhe.yraft.protobuf.generated.RaftProtos.VoteRequest;
import com.github.yaoshengzhe.yraft.protobuf.generated.RaftProtos.VoteResponse;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.FileWriteMode;
import com.google.common.io.Files;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public class RaftServer {

  private Roles role = Roles.Follower;

  // Persistent States
  private PersistentState state = PersistentState.newBuilder()
          .setCurrentTerm(0)
          .setVoteFor(0)
          .build();

  private long currentTerm = 0;
  private long voteFor = -1;
  private List<LogEntry> entries = Lists.newArrayList();

  // Volatile States
  private long commmitIndex = 0;
  private long lastApplied = 0;

  // Leader only
  private Map<String, Long> nextIndex = Maps.newHashMap();
  private Map<String, Long> matchIndex = Maps.newHashMap();

  public void asRole(Roles role) {
    this.role = role;
  }

  private void onAppendEntriesRequest(AppendEntriesRequest request) {

  }

  private void onAppendEntriesResponse(AppendEntriesResponse response) {

    if (response.getTerm() > this.currentTerm) {
      onTransition(Roles.Follower);
    }
  }

  private void onVoteRequest(VoteRequest request) {

  }

  private void onVoteResponse(VoteResponse response) {
    if (response.getTerm() > this.currentTerm) {
      onTransition(Roles.Follower);
    }
  }

  private void onTimeout() {
    switch (this.role) {
      case Follower:
        onTransition(Roles.Candidate);
        startLeaderElection();
        break;
      case Candidate:
        startLeaderElection();
        break;
    }
  }

  private void onTransition(Roles newRole) {
    if (this.role == Roles.Follower && newRole == Roles.Candidate) {
      this.currentTerm++;
    } else if (this.role == Roles.Candidate && newRole == Roles.Leader) {

    } else if (this.role == Roles.Candidate && newRole == Roles.Follower) {

    } else if (this.role == Roles.Leader && newRole == Roles.Follower) {

    }
    this.asRole(newRole);
  }

  private void startLeaderElection() {
    VoteRequest request = VoteRequest.newBuilder()
            .setLastLogIndex(this.lastApplied)
            .setTerm(this.currentTerm)
            .build();
    resetTimer();
  }

  private void resetTimer() {

  }

  private void persist() throws IOException {
    byte[] data = this.state.toByteArray();
    Files.asByteSink(new File(""), FileWriteMode.APPEND).write(data);
  }

  public void run() {
    resetTimer();
  }

  public static void main(String[] args) {

    RaftServer node = new RaftServer();
    node.run();
  }
}

@@ -519,32 +519,167 @@
 s.VoteResponse;%0A
+import com.github.yaoshengzhe.yraft.statemachine.LocalDiskStateMachine;%0Aimport com.github.yaoshengzhe.yraft.statemachine.StateMachine;%0A
 import com.googl
@@ -942,16 +942,495 @@
 rver %7B%0A%0A
+  public static class Builder %7B%0A    private StateMachine stateMachine;%0A%0A    public Builder setStateMachine(StateMachine stateMachine) %7B%0A      this.stateMachine = stateMachine;%0A      return this;%0A    %7D%0A%0A    public RaftServer build() %7B%0A      return new RaftServer(this);%0A    %7D%0A  %7D%0A%0A  private RaftServer(Builder builder) %7B%0A    this.stateMachine = builder.stateMachine;%0A  %7D%0A%0A  public static Builder newBuilder() %7B%0A    return new Builder();%0A  %7D%0A%0A  private StateMachine stateMachine;%0A%0A
   privat
@@ -3385,173 +3385,8 @@
  %7D%0A%0A
-  private void persist() throws IOException %7B%0A    byte%5B%5D data = this.state.toByteArray();%0A    Files.asByteSink(new File(%22%22), FileWriteMode.APPEND).write(data);%0A  %7D%0A%0A
   pu
@@ -3465,25 +3465,24 @@
 g%5B%5D args) %7B%0A
-%0A
     RaftServ
@@ -3490,20 +3490,16 @@
 r node =
- new
  RaftSer
@@ -3501,20 +3501,123 @@
 ftServer
+.newBuilder()%0A            .setStateMachine(new LocalDiskStateMachine(new File(%22%22)))%0A            .build
 ();%0A
+%0A
     node

use builder pattern for RaftServer