package org.briarproject.api.introduction;

import static org.briarproject.api.introduction.IntroductionAction.ACK;
import static org.briarproject.api.introduction.IntroductionAction.LOCAL_REQUEST;
import static org.briarproject.api.introduction.IntroductionAction.REMOTE_ACCEPT;
import static org.briarproject.api.introduction.IntroductionAction.REMOTE_DECLINE;

public enum IntroducerProtocolState {

	ERROR(0),
	PREPARE_REQUESTS(1) {
		@Override
		public IntroducerProtocolState next(IntroductionAction a) {
			if (a == LOCAL_REQUEST) return AWAIT_TWO_RESPONSES;
			return ERROR;
		}
	},
	AWAIT_TWO_RESPONSES(2) {
		@Override
		public IntroducerProtocolState next(IntroductionAction a) {
			if (a == REMOTE_ACCEPT) return ACCEPTED_AWAIT_RESPONSE;
			if (a == REMOTE_DECLINE) return DECLINED_AWAIT_RESPONSE;
			return ERROR;
		}
	},
	ACCEPTED_AWAIT_RESPONSE(3) {
		@Override
		public boolean isOutOfOrder(IntroductionAction a) {
			return a == ACK;
		}
		@Override
		public IntroducerProtocolState next(IntroductionAction a) {
			if (a == REMOTE_ACCEPT) return AWAIT_TWO_ACKS;
			if (a == REMOTE_DECLINE) return FINISHED;
			return ERROR;
		}
	},
	DECLINED_AWAIT_RESPONSE(4) {
		@Override
		public IntroducerProtocolState next(IntroductionAction a) {
			if (a == REMOTE_ACCEPT) return FINISHED;
			if (a == REMOTE_DECLINE) return FINISHED;
			return ERROR;
		}
	},
	AWAIT_TWO_ACKS(5) {
		@Override
		public IntroducerProtocolState next(IntroductionAction a) {
			if (a == ACK) return AWAIT_ONE_ACK;
			return ERROR;
		}
	},
	AWAIT_ONE_ACK(6) {
		@Override
		public IntroducerProtocolState next(IntroductionAction a) {
			if (a == ACK) return FINISHED;
			return ERROR;
		}
	},
	FINISHED(7);

	private final int value;

	IntroducerProtocolState(int value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}

	public static IntroducerProtocolState fromValue(int value) {
		for (IntroducerProtocolState s : values()) {
			if (s.value == value) return s;
		}
		throw new IllegalArgumentException();
	}

	public static boolean isOngoing(IntroducerProtocolState state) {
		return state != FINISHED && state != ERROR;
	}

	public boolean isOutOfOrder(IntroductionAction a) {
		return false;
	}

	public IntroducerProtocolState next(IntroductionAction a) {
		return this;
	}
}