package com.baselet.element.sequence_aio.facet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baselet.control.basics.Line1D;
import com.baselet.control.basics.geom.Line;
import com.baselet.control.basics.geom.PointDouble;
import com.baselet.control.enums.AlignHorizontal;
import com.baselet.control.enums.AlignVertical;
import com.baselet.control.enums.LineType;
import com.baselet.diagram.draw.DrawHandler;
import com.baselet.diagram.draw.TextSplitter;
import com.baselet.diagram.draw.helper.ColorOwn;
import com.baselet.element.relation.helper.RelationDrawer;
import com.baselet.element.relation.helper.RelationDrawer.ArrowEndType;
import com.baselet.element.sequence_aio.facet.Message.ArrowType;

/**
 * Class for representing a lost or a found message.
 * Lost Messages are always on the right side of the lifeline and found messages are always on the left side of the lifeline.
 * Therefore the message always travels from left to right.
 *
 */
public class LostOrFoundMessage implements LifelineOccurrence {

	private static final Logger log = LoggerFactory.getLogger(LostOrFoundMessage.class);
	private static final double CIRCLE_RADIUS = 10;
	private static final double LIFELINE_TEXT_PADDING = Math.max(RelationDrawer.ARROW_LENGTH, CIRCLE_RADIUS * 2) + 3;

	private final Lifeline lifeline;
	// private final Lifeline to;
	private final int sendTick;
	private final String[] textLines;
	private final ArrowType arrowType;
	private final LineType lineType;
	private final boolean found;

	/**
	 *
	 * @param lifeline
	 * @param found true if it is a found message, if false it is a lost message
	 * @param sendTick
	 * @param text can be more than one line, but if so the lines must be separated by a \n
	 * @param arrowType
	 * @param lineType
	 */
	public LostOrFoundMessage(Lifeline lifeline, boolean found, int sendTick, String text, ArrowType arrowType, LineType lineType) {
		super();
		this.lifeline = lifeline;
		this.found = found;
		this.sendTick = sendTick;
		textLines = text.split("\n");
		this.arrowType = arrowType;
		this.lineType = lineType;
	}

	public double getCenterXOffset() {
		if (found) {
			return -lifeline.getLifelineLeftPartWidth(sendTick);
		}
		else {
			return lifeline.getLifelineRightPartWidth(sendTick);
		}
	}

	@Override
	public Line1D draw(DrawHandler drawHandler, PointDouble topLeft, PointDouble size) {
		RelationDrawer.ArrowEndType arrowEndType = ArrowEndType.NORMAL;
		boolean fillArrow = false;
		switch (arrowType) {
			case OPEN:
				arrowEndType = ArrowEndType.NORMAL;
				fillArrow = false;
				break;
			case FILLED:
				arrowEndType = ArrowEndType.CLOSED;
				fillArrow = true;
				break;
			default:
				log.error("Encountered unhandled enumeration value '" + arrowType + "'.");
				break;
		}
		LineType oldLt = drawHandler.getLineType();
		drawHandler.setLineType(lineType);
		double centerY = topLeft.y + size.y / 2;
		double lineXStart, lineXEnd;
		ColorOwn oldBg = drawHandler.getBackgroundColor();
		drawHandler.setBackgroundColor(drawHandler.getForegroundColor());
		if (found) {
			drawHandler.drawCircle(topLeft.x + CIRCLE_RADIUS, centerY, CIRCLE_RADIUS);
			lineXStart = topLeft.x + CIRCLE_RADIUS * 2;
			lineXEnd = topLeft.x + size.x / 2 + getCenterXOffset();
		}
		else {
			drawHandler.drawCircle(topLeft.x + size.x - CIRCLE_RADIUS, topLeft.y + size.y / 2, CIRCLE_RADIUS);
			lineXStart = topLeft.x + size.x / 2 + getCenterXOffset();
			lineXEnd = topLeft.x + size.x - CIRCLE_RADIUS * 2;
		}
		drawHandler.setBackgroundColor(oldBg);

		TextSplitter.drawText(drawHandler, textLines, lineXStart, topLeft.y,
				lineXEnd - lineXStart, size.y / 2, AlignHorizontal.CENTER, AlignVertical.BOTTOM);
		PointDouble arrowPoint = new PointDouble(lineXEnd, centerY);
		Line line = new Line(new PointDouble(lineXStart, centerY), arrowPoint);
		drawHandler.drawLine(line);
		RelationDrawer.drawArrowToLine(arrowPoint, drawHandler, line, false, arrowEndType, fillArrow, false);
		drawHandler.setLineType(oldLt);
		return null;
	}

	@Override
	public double getMinWidth(DrawHandler drawHandler) {
		return (TextSplitter.getTextMinWidth(textLines, drawHandler) + LIFELINE_TEXT_PADDING * 2 + Math.abs(getCenterXOffset())) * 2;
	}

	@Override
	public double getAdditionalYHeight(DrawHandler drawHandler, PointDouble size) {
		return TextSplitter.getSplitStringHeight(textLines,
				size.x / 2 - LIFELINE_TEXT_PADDING * 2 - Math.abs(getCenterXOffset()), drawHandler) * 2 - size.y;
	}

	public OccurrenceSpecification sendOccurrenceSpecification() {
		if (found) {
			throw new IllegalStateException("A found message has no send occurrence specification.");
		}
		return new LostOrFoundOccurrenceSpecification();
	}

	public OccurrenceSpecification receiveOccurrenceSpecification() {
		if (!found) {
			throw new IllegalStateException("A lost message has no receive occurrence specification.");
		}
		return new LostOrFoundOccurrenceSpecification();
	}

	private class LostOrFoundOccurrenceSpecification implements OccurrenceSpecification {

		@Override
		public Lifeline getLifeline() {
			return lifeline;
		}

		@Override
		public boolean hasFixedPosition() {
			return true;
		}

		@Override
		public AlignHorizontal getFixedPositionAlignment() {
			if (getCenterXOffset() >= 0) {
				return AlignHorizontal.RIGHT;
			}
			else {
				return AlignHorizontal.LEFT;
			}
		}

		@Override
		public double getHorizonatlPosition(HorizontalDrawingInfo hDrawingInfo) {
			return hDrawingInfo.getHDrawingInfo(getLifeline()).getHorizontalCenter() + getCenterXOffset();
		}

		@Override
		public double getHorizontalPosition(HorizontalDrawingInfo llHDrawingInfo, boolean left) {
			return 0;
		}

		@Override
		public PointDouble getPosition(DrawingInfo drawingInfo) {
			return new PointDouble(getHorizonatlPosition(drawingInfo), drawingInfo.getVerticalCenter(sendTick));
		}

		@Override
		public PointDouble getPosition(DrawingInfo drawingInfo, boolean left) {
			return new PointDouble(0, 0);
		}

	}

}
