package com.persagy.adm.diagram.core.model.template;

import cn.hutool.core.collection.CollUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.gson.annotations.Expose;
import com.persagy.adm.diagram.core.model.AbstractLine;
import com.persagy.adm.diagram.core.model.base.Container;
import com.persagy.adm.diagram.core.model.base.IContainer;
import com.persagy.adm.diagram.core.model.base.IEquipHolder;
import com.persagy.adm.diagram.core.model.base.XY;
import com.persagy.adm.diagram.core.model.logic.DataFilter;

import java.util.ArrayList;
import java.util.List;

/**
 * 干管/线
 * @author zhaoyk
 */
public class MainPipe extends AbstractLine implements IEquipHolder {

	public static final String TYPE = "mainPipe";

	@Expose
	private String remark;

	@Expose
	private String pipeType;

	@Expose
	private List<String> passbyRels; //要经过干管的关系类型，格式: graphCode/relCode

	@Expose
	private List<String> connectEquips; //可以直连干管的设备类型

	@Expose
	private List<String> equipmentTypes; //可以放置在干管上的设备类型；或是绑定的设备类型

	@Expose
	private boolean bindEquipment; //干管是否绑定具体设备

	/**
	 * 设备数据过滤器
	 */
	@Expose
	private DataFilter dataFilter;

	@Expose
	private List<List<MainPipePoint>> path;

	/**
	 * 关联的容器id
	 */
	@Expose
	private List<String> relatedContainers;

	/**
	 * 连线的坐标点序列，多条线
	 */
	@Expose
	private List<List<XY>> locationPath;

	/**
	 * 该干管为动态源时，驱动的目标组件id
	 */
	private String dynTarget;

	/**
	 * 关联的容器对象，运行时
	 */
	@JsonIgnore
	private List<Container> relatedContainerList;

	/**
	 * 父容器对象，运行时
	 */
	@JsonIgnore
	private Container parentContainer;

	public MainPipe() {
		this.compType = TYPE;
	}

	public void init(DiagramTemplate template) {
		if(CollUtil.isNotEmpty(relatedContainers)) {
			relatedContainerList = new ArrayList<>(relatedContainers.size());
			for(String conId : relatedContainers) {
				Container con = template.getContainerById(conId);
				if(con != null)
					relatedContainerList.add(con);
			}

			calcParentContainer();
		}
	}

	private void calcParentContainer(){
		IContainer p = null;
		for(Container con : relatedContainerList) {
			if (p == null) {
				p = con.getParent();
			} else {
				p = getCommonParent(p, con.getParent());
				if(p == null){
					break;
				}
			}
		}
		if(p instanceof Container && !DiagramTemplate.FRAME_ID.equals(p.getId())) {
			parentContainer = (Container) p;
			parentContainer.addChildMainPipe(this);
		}
	}

	private IContainer getCommonParent(IContainer p1, IContainer p2){
		IContainer cp = p1;
		while (true){
			IContainer tmp = p2;
			while (true) {
				if(cp == tmp){
					return cp;
				}
				tmp = tmp.getParent();
				if(tmp == null){
					break;
				}
			}
			cp = cp.getParent();
			if(cp == null){
				break;
			}
		}
		return null;
	}

	public void resetRelatedCons() {
		if(parentContainer != null) {
			parentContainer.removeChildMainPipe(this);
		}
		parentContainer = null;
		relatedContainerList = null;
	}

	public void parseLines(List<List<XY>> lines, DiagramTemplate template) {
		List<Container> refContainers = relatedContainerList;
		if(CollUtil.isEmpty(refContainers)) {
			refContainers = template.getContainers();
		}
		List<ContainerRefPoint> allRefPoints = new ArrayList<>();
		for(Container con : refContainers) {
			if (!DiagramTemplate.FRAME_ID.equals(con.getId()) && !con.isAbsolutePosition()) {
				allRefPoints.addAll(ContainerRefPoint.getRefPoints(con, con.locationToRoot()));
			}
		}

		path = new ArrayList<>();
		for (List<XY> line : lines) {
			List<MainPipePoint> ps = new ArrayList<>();
			path.add(ps);

			for (XY point : line) {
				double min = Double.MAX_VALUE;
				ContainerRefPoint targetRp = null;
				for (ContainerRefPoint rp : allRefPoints) {
					double d = distance(point, rp.getLocation());
					if (d < min) {
						min = d;
						targetRp = rp;
					}
				}
				MainPipePoint mp = new MainPipePoint();
				mp.setContainerId(targetRp.getContainerId());
				mp.setRefPointName(targetRp.getName());
				mp.setOffset(new XY(point.x - targetRp.getLocation().x, point.y - targetRp.getLocation().y));
				ps.add(mp);
			}
		}
	}

	private double distance(XY p1, XY p2) {
		return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
	}

	@Override
	public void layout(Object layoutContext) {
		DiagramTemplate template = (DiagramTemplate)layoutContext;
		locationPath = new ArrayList<>(path.size());
		for (List<MainPipePoint> line : path) {
			ArrayList<XY> list = new ArrayList<>();
			locationPath.add(list);

			for(MainPipePoint p : line) {
				Container container = template.getContainerById(p.getContainerId());
				if(container != null && !container.isAbsolutePosition()) {
					ContainerRefPoint rp = ContainerRefPoint.getRefPoint(container, p.getRefPointName(), container.locationToRoot());
					list.add(new XY(rp.getLocation().x + p.getOffset().x , rp.getLocation().y + p.getOffset().y));
				} else {
					//参考点丢失，给出指示线
					if(list.size() > 0) {
						XY mark = new XY(list.get(list.size() - 1));
						if(mark.y != -10) {
							mark.y = -10;
						} else {
							mark.x += 50;
						}
						list.add(mark);
					} else
						list.add(new XY(-10, -10));
				}
			}
		}
	}

	@Override
	public void clearLayout() {
		locationPath = null;
	}

	@Override
	public String getId() {
		return id;
	}

	@Override
	public void setId(String id) {
		this.id = id;
	}

	@Override
	public String getName() {
		return name;
	}

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

	public String getRemark() {
		return remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

	public String getPipeType() {
		return pipeType;
	}

	public void setPipeType(String pipeType) {
		this.pipeType = pipeType;
	}

	public List<String> getPassbyRels() {
		return passbyRels;
	}

	public void setPassbyRels(List<String> passbyRels) {
		this.passbyRels = passbyRels;
	}

	public List<String> getConnectEquips() {
		return connectEquips;
	}

	public void setConnectEquips(List<String> connectEquips) {
		this.connectEquips = connectEquips;
	}

	@Override
	public List<String> getEquipmentTypes() {
		return equipmentTypes;
	}

	public void setEquipmentTypes(List<String> equipmentTypes) {
		this.equipmentTypes = equipmentTypes;
	}

	public boolean isBindEquipment() {
		return bindEquipment;
	}

	public void setBindEquipment(boolean bindEquipment) {
		this.bindEquipment = bindEquipment;
	}

	@Override
	public DataFilter getDataFilter() {
		return dataFilter;
	}

	public void setDataFilter(DataFilter dataFilter) {
		this.dataFilter = dataFilter;
	}

	public List<List<MainPipePoint>> getPath() {
		return path;
	}

	public void setPath(List<List<MainPipePoint>> path) {
		this.path = path;
	}

	public List<String> getRelatedContainers() {
		return relatedContainers;
	}

	public void setRelatedContainers(List<String> relatedContainers) {
		this.relatedContainers = relatedContainers;
	}

	public List<Container> getRelatedContainerList() {
		return relatedContainerList;
	}

	public Container getParentContainer() {
		return parentContainer;
	}

	public List<List<XY>> getLocationPath() {
		return locationPath;
	}

	public void setLocationPath(List<List<XY>> locationPath) {
		this.locationPath = locationPath;
	}

	public String getDynTarget() {
		return dynTarget;
	}

	public void setDynTarget(String dynTarget) {
		this.dynTarget = dynTarget;
	}

}
