package models;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import core.services.OptionService;
import core.services.SlideService;
import play.Logger;

@Entity
public class Question extends ModelUuid {

	public static enum TransitionType {
		time, click, enter, action, answer;

		public static final String TRANSITION_TYPES_SEPARATOR = " ";

		public static List<TransitionType> tryBuildTransitionTypesFromValues(List<String> vals) {
			if (vals == null || vals.isEmpty()) {
				return null;
			}

			List<TransitionType> ret = new LinkedList<>();

			TransitionType[] allEnumVals = values();
			for (String val : vals) {
				boolean ok = false;
				for (TransitionType transitionType : allEnumVals) {
					if (transitionType.name().equals(val)) {
						ok = true;
						ret.add(transitionType);
						break;
					}
				}
				if (!ok) {
					return null;
				}
			}
			return ret;
		}

		public static List<TransitionType> buildTransitionTypesListFromString(String transitionTypes) {

			List<String> tempsToList = buildTransitionTypesStringListFromString(transitionTypes);

			List<TransitionType> ret = TransitionType.tryBuildTransitionTypesFromValues(tempsToList);

			return ret;

		}

		public static String buildTransitionTypesStringFromList(List<TransitionType> vals) {
			if (vals == null) {
				return null;
			}

			String ret = "";
			String sep = "";
			for (TransitionType transitionType : vals) {
				ret += sep + transitionType.name();
				sep = TransitionType.TRANSITION_TYPES_SEPARATOR;
			}
			return ret;
		}

		public static List<String> buildTransitionTypesStringListFromString(String transitionTypes) {
			List<String> ret = new LinkedList<String>();

			if (transitionTypes == null || transitionTypes.isEmpty()) {
				return ret;
			}

			String[] temps = transitionTypes.split(TransitionType.TRANSITION_TYPES_SEPARATOR);
			if (temps == null || temps.length <= 0) {
				return ret;
			}

			for (String temp : temps) {
				ret.add(temp);
			}

			return ret;
		}

	}

	/* @Basic
	 @Enumerated(EnumType.STRING)
	 private TransitionType transitionType;*/

	@Basic
	private String transitionType;

	@OneToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.REMOVE })
	private List<Option> options = new LinkedList<Option>();

	@OneToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.DETACH })
	private List<Slide> slides = new LinkedList<Slide>();

	@Basic
	@ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.DETACH })
	private Test test;

	@Basic
	private Boolean deleted = false;

	@Basic
	private String name;
	
	@Basic
	private Date creationDate = new Date();
	
	@Basic
	private Integer orderNum = 0;

	public String getTransitionType() {
		return transitionType;
	}

	public List<TransitionType> retrieveTransitionTypes() {
		String transitionTypes = getTransitionType();
		List<TransitionType> ret = TransitionType.buildTransitionTypesListFromString(transitionTypes);
		return ret;
	}

	public void setupTransitionTypes(List<TransitionType> transitionTypesList) {
		String transitionTypes = TransitionType.buildTransitionTypesStringFromList(transitionTypesList);
		setTransitionType(transitionTypes);
	}

	public Date getCreationDate() {
		return creationDate;
	}

	public void setCreationDate(Date creationDate) {
		this.creationDate = creationDate;
	}

	public Integer getOrder() {
		return orderNum;
	}

	public void setOrder(Integer order) {
		this.orderNum = order;
	}

	public void setTransitionType(String transitionType) {
		this.transitionType = transitionType;
	}

	public List<Option> getOptions() {
		return options;
	}

	public void setOptions(List<Option> options) {
		this.options = options;
	}

	public List<Slide> getSlides() {
		return orderSlide(slides);
	}
	
	private List<Slide> orderSlide(List<Slide> slides) {
		Slide[] tmp= new Slide[slides.size()];
		LinkedList<Slide> ret = new LinkedList<Slide>();
		for(Slide slide : slides){
			tmp[slide.getOrder()] = slide;
		}
		for(int i=0; i<tmp.length; i++){
			Slide slide = tmp[i];
			ret.add(slide);
		}
		return ret;
	}

	public List<Slide> retrieveSlidesByDeleted(Boolean deleted) {
		List<Slide> ret = new LinkedList<Slide>();
		if (deleted == null) {
			ret = getSlides();
		} else {
			for (Slide slide : getSlides()) {
				if (slide.getDeleted() == deleted) {
					ret.add(slide);
				}
			}
		}
		return ret;
	}

	public void setSlides(List<Slide> slides) {
		this.slides = slides;
	}

	public Test getTest() {
		return test;
	}

	public void setTest(Test test) {
		this.test = test;
	}

	public Boolean getDeleted() {
		return deleted;
	}

	public void setDeleted(Boolean deleted) {
		this.deleted = deleted;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public boolean addOption(Option option) {
		if (option == null)
			return false;
		if (this.options.contains(option)) {
			return false;
		}
		return this.options.add(option);
	}

	public boolean addSlide(Slide slide) {
		if (slide == null)
			return false;
		if (this.slides.contains(slide)) {
			return false;
		}
		slide.setOrder(this.slides.size());
		slide.save();
		return this.slides.add(slide);
	}

	/*flag dontAddQuestionToTestQuestionList serves because, without, in Test clonation
	//is thrown a ConcurrentModification Exception*/
	public Question clone(Test test, boolean addQuestionToTestQuestionList) {
		if (test == null)
			throw new RuntimeException("Test is null!");

		try {
			OptionService optionService = OptionService.instance();
			SlideService slideService = SlideService.instance();
			Question clonedQuestion = new Question();
			clonedQuestion.setDeleted(this.getDeleted());
			clonedQuestion.setName(this.getName());
			clonedQuestion.setTest(test);
			clonedQuestion.setTransitionType(this.getTransitionType());
			clonedQuestion.save();
			
			if(addQuestionToTestQuestionList){
				test.addQuestion(clonedQuestion);
				test.save();
			}
			
			List<Option> clonedOptions = optionService.cloneOptionsList(null, clonedQuestion, null, null,
					this.getOptions());
			clonedQuestion.setOptions(clonedOptions);

			List<Slide> clonedSlides = slideService.cloneSlidesList(clonedQuestion, this.getSlides());
			clonedQuestion.setSlides(clonedSlides);

			clonedQuestion.save();
			return clonedQuestion;
		} catch (Exception ex) {
			Logger.error(ex, "Exception during Question clonation...return null");
			return null;
		}
	}
}