/*
 * Copyright 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.statemachine.state;

import org.springframework.statemachine.StateContext;
import org.springframework.util.Assert;

/**
 * History implementation of a {@link PseudoState}.
 *
 * @author Janne Valkealahti
 *
 * @param <S> the type of state
 * @param <E> the type of event
 */
public class HistoryPseudoState<S, E> extends AbstractPseudoState<S, E> {

	private final StateHolder<S, E> defaultState;
	private final StateHolder<S, E> containingState;
	private State<S, E> state;

	/**
	 * Instantiates a new history pseudo state.
	 *
	 * @param kind the kind
	 * @param containingState the parent containing state
	 */
	public HistoryPseudoState(PseudoStateKind kind, StateHolder<S, E> containingState) {
		this(kind, containingState, new StateHolder<S, E>(null));
	}

	/**
	 * Instantiates a new history pseudo state.
	 *
	 * @param kind the kind
	 * @param defaultState the default history state
	 * @param containingState the parent containing state
	 */
	public HistoryPseudoState(PseudoStateKind kind, StateHolder<S, E> defaultState, StateHolder<S, E> containingState) {
		super(kind);
		Assert.isTrue(PseudoStateKind.HISTORY_SHALLOW == kind || PseudoStateKind.HISTORY_DEEP == kind,
				"Pseudo state must be either shallow or deep");
		Assert.notNull(defaultState, "Holder defaultState must be set");
		Assert.notNull(containingState, "Holder containingState must be set");
		this.defaultState = defaultState;
		this.containingState = containingState;
	}

	@Override
	public State<S, E> entry(StateContext<S, E> context) {
		// if no logged history or history is final state,
		// go to default state. go to containing parent if
		// we have no history and there's no default state.
		if (state == null) {
			if (defaultState.getState() == null) {
				return containingState.getState();
			} else {
				return defaultState.getState();
			}
		} else {
			if (defaultState.getState() != null && state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.END) {
				return defaultState.getState();
			} else {
				return state;
			}
		}
	}

	/**
	 * Sets the current recorded state.
	 *
	 * @param state the state
	 */
	public void setState(State<S, E> state) {
		this.state = state;
	}

	/**
	 * Gets the current recorded state.
	 *
	 * @return the current recorded state.
	 */
	public State<S, E> getState() {
		return state;
	}
}
