package com.eactive.eai.transformer.layout.mapper;

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

import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Named;
import org.mapstruct.ObjectFactory;

import com.eactive.eai.data.entity.onl.transformer.layout.LayoutEntity;
import com.eactive.eai.data.entity.onl.transformer.layout.LayoutItemEntity;
import com.eactive.eai.data.mapper.BaseMapperConfig;
import com.eactive.eai.data.mapper.GenericMapper;
import com.eactive.eai.data.mapper.StringTrimMapper;
import com.eactive.eai.transformer.dao.TransformerDaoException;
import com.eactive.eai.transformer.layout.Item;
import com.eactive.eai.transformer.layout.ItemFactory;
import com.eactive.eai.transformer.layout.LayoutTypeKey;
import com.eactive.eai.transformer.message.BytesMessage;
import com.eactive.eai.transformer.message.CardMessage;
import com.eactive.eai.transformer.message.CommonBytesMessage;
import com.eactive.eai.transformer.message.CommonEbcdicMessage;
import com.eactive.eai.transformer.message.DelimiterMessage;
import com.eactive.eai.transformer.message.EbcdicMessage;
import com.eactive.eai.transformer.message.FullBytesMessage;
import com.eactive.eai.transformer.message.ISO8583Message;
import com.eactive.eai.transformer.message.ISO8583MessageFactory;
import com.eactive.eai.transformer.message.JSONMessage;
import com.eactive.eai.transformer.message.MonitoringVBytesMessage;
import com.eactive.eai.transformer.message.MonitoringVEbcdicMessage;
import com.eactive.eai.transformer.message.ObjectMessage;
import com.eactive.eai.transformer.message.OddBytesMessage;
import com.eactive.eai.transformer.message.VariableBytesMessage;
import com.eactive.eai.transformer.message.VariableEbcdicMessage;
import com.eactive.eai.transformer.message.VariableFieldMessage;
import com.eactive.eai.transformer.message.XMLMessage;

@Mapper(config = BaseMapperConfig.class)
public abstract class LayoutItemMapper implements GenericMapper<Item, LayoutItemEntity> {

	@Mapping(source = "id.loutname", target = "layoutName")
	@Mapping(source = "id.loutitemname", target = "name")
	@Mapping(source = "id.loutitemserno", target = "seqNo")
	@Mapping(source = "loutitemnodeptrnidname", target = "nodeTypeId")
	@Mapping(source = "loutitemidname", target = "itemId")
	@Mapping(source = "parntloutitemidname", target = "parentId")
	@Mapping(source = "loutitemptrnidname", target = "dataTypeId")
	@Mapping(source = "loutitemdesc", target = "description")
	@Mapping(source = "loutitempathname", target = "path")
	@Mapping(source = "loutitemlencnt", target = "length")
	@Mapping(source = "loutitemrefinfo", target = "refInfo")
	@Mapping(source = "loutitemrefinfo2", target = "occRef", qualifiedBy = StringTrimMapper.class)
	@Mapping(source = "loutitemoccurptrndstcd", target = "occType")
	@Mapping(source = "loutitemmaxoccurnoitm", target = "occMax")
	@Mapping(source = "loutitemminoccurnoitm", target = "occMin")
	@Mapping(source = "loutitembascval", target = "defaultValue", qualifiedBy = StringTrimMapper.class)
	@Mapping(source = "loutitemmskyn", target = "mask")
	@Mapping(source = "loutitemmasklength", target = "maskLength")
	@Mapping(source = "loutitemmaskoffset", target = "maskOffset")
	@Mapping(source = "multilang", target = "multiLang", qualifiedBy = StringTrimMapper.class)
	@Mapping(source = "loutdptcnt", target = "depth")
	@Mapping(source = "decptlencnt", target = "decimalPointLength")
	@Mapping(source = "loutitemrefptrnidname", target = "dataTypeRef")
	@Override
	public abstract Item toVo(LayoutItemEntity entity);

	@InheritInverseConfiguration
	@Mapping(source = "defaultValue", target = "loutitembascval", qualifiedByName = "defaultValueToLoutitembascval")
	public abstract LayoutItemEntity toEntity(Item item);

	@Named("defaultValueToLoutitembascval")
	String defaultValueToLoutitembascval(Object defaultValue) {
		if (defaultValue == null) {
			return "";
		}

		if (defaultValue instanceof byte[]) {
			return new String((byte[]) defaultValue);
		} else {
			return defaultValue.toString();
		}
	}

	@ObjectFactory
	public Item createVo(LayoutItemEntity layoutItemEntity) {
		Item item = null;
		LayoutEntity layoutEntity = layoutItemEntity.getLayoutEntity();
		if (layoutItemEntity.getLoutitemnodeptrnidname() == Item.NODE_MESSAGE) {
			// root item
			item = createRootItem(layoutEntity);
		} else {
			// child item
			int nodeTypeId = layoutItemEntity.getLoutitemnodeptrnidname();
			item = ItemFactory.createItem(nodeTypeId);
		}
		return item;
	}
	
	public List<LayoutItemEntity> mapLayoutItemEntity(Item item) {
		List<LayoutItemEntity> entities = new ArrayList<>();
		mapLayoutItemEntity(item, entities);
		return entities;
	}
	
	public void mapLayoutItemEntity(Item item, @MappingTarget List<LayoutItemEntity> entities) {
		LayoutItemEntity layoutItemEntity = toEntity(item);
		entities.add(layoutItemEntity);

		if (!item.isField() && !item.isAttr() && item.getChildCount() != 0) {
			for (int i = 0; i < item.getChildCount(); i++) {
				mapLayoutItemEntity(item.getChild(i), entities);
			}
		}
	}

	public Item mapRootItem(List<LayoutItemEntity> entities) {
		
		LayoutItemEntity layoutItemEntity = entities.stream().findFirst()
				.orElseThrow(() -> new TransformerDaoException("LayoutItem이 없습니다."));
		String layoutName = layoutItemEntity.getId().getLoutname();

		try {

			Iterator<LayoutItemEntity> iter = entities.iterator();
			Item root = toVo(iter.next());

			if (root.getParentId() != 0) {
				throw new TransformerDaoException("ROOT Item ParntLoutItemIDName는 0 이어야 합니다. - " + root.getParentId());
			}

			if (root.getNodeTypeId() != Item.NODE_MESSAGE) {
				throw new TransformerDaoException(
						"ROOT Item Node Type은 MESSAGE TYPE이어야 합니다. - " + root.getNodeTypeId());
			}

			Item parent = root;
			while (iter.hasNext()) {
				Item child = toVo(iter.next());

				parent = getRecursiveParent(child, parent, layoutName);

				switch (child.getNodeTypeId()) {
				case Item.NODE_MESSAGE:
				case Item.NODE_GROUP:
					parent.addChild(child);
					child.setParent(parent);
					parent = child;
					break;
				case Item.NODE_FIELD:
				case Item.NODE_ATTR:
					parent.addChild(child);
					child.setParent(parent);
					break;
				default:
					throw new TransformerDaoException("Layout Item 의 타입이 정의된 값이 아닙니다. : " + child);
				}
			}

			return root;
		} catch (Exception e) {
			throw new TransformerDaoException(e.getMessage() + "[" + layoutName + "]", e);
		}

	}

	public Item getRecursiveParent(Item child, Item parent, String layoutName) {
		if (parent == null) {
			StringBuilder sb = new StringBuilder();
			sb.append("item의 parent id 지정이 잘못 되었읍니다. - ");
			sb.append("layout=" + layoutName + ", ");
			sb.append("child=" + child.getName() + ", ");
			sb.append("child's parent id=" + child.getParentId());
			throw new TransformerDaoException(sb.toString());
		}

		if (child.getParentId() == parent.getItemId())
			return parent;
		else
			return getRecursiveParent(child, parent.getParent(), layoutName);
	}

	public Item createRootItem(LayoutEntity layoutEntity) {
		Item item = null;
		String layoutType = layoutEntity.getLoutptrnname();

		if (layoutType.equals(LayoutTypeKey.BYTES)) {
			item = new BytesMessage();
		} else if (layoutType.equals(LayoutTypeKey.OBJECT)) {
			item = new ObjectMessage();
		} else if (layoutType.equals(LayoutTypeKey.XML)) {
			item = new XMLMessage();
		} else if (layoutType.equals(LayoutTypeKey.JSON)) {
			item = new JSONMessage();
		} else if (layoutType.equals(LayoutTypeKey.EBCDIC)) {
			item = new EbcdicMessage();
		} else if (layoutType.equals(LayoutTypeKey.CARD)) {
			item = new CardMessage();
		} else if (layoutType.equals(LayoutTypeKey.COMBYTES)) {
			item = new CommonBytesMessage();
		} else if (layoutType.equals(LayoutTypeKey.COMEBCDIC)) {
			item = new CommonEbcdicMessage();
		} else if (layoutType.equals(LayoutTypeKey.VBYTES)) {
			item = new VariableBytesMessage();
		} else if (layoutType.equals(LayoutTypeKey.VFIELD)) {
			item = new VariableFieldMessage();
		} else if (layoutType.equals(LayoutTypeKey.VEBCDIC)) {
			item = new VariableEbcdicMessage();
		} else if (layoutType.equals(LayoutTypeKey.FULLBYTES)) {// 바이패스를 위해서
			item = new FullBytesMessage();
		} else if (layoutType.equals(LayoutTypeKey.MVBYTES)) {// 오류 파싱을 위해서
			item = new MonitoringVBytesMessage();
		} else if (layoutType.equals(LayoutTypeKey.MVEBCDIC)) {// 오류 파싱을 위해서
			item = new MonitoringVEbcdicMessage();
		} else if (layoutType.equals(LayoutTypeKey.ODDBYTES)) {// 금결원 응답전문
			item = new OddBytesMessage();
		} else if (layoutType.equals(LayoutTypeKey.DELIMITER)) {
			item = new DelimiterMessage();
		} else if (layoutType.equals(LayoutTypeKey.ISO8583)) {
			item = new ISO8583Message();
		} 
//		else if (layoutType.equals(LayoutTypeKey.ISO8583_H2H)) {
//			item = new ISO8583H2HMessage();
//		}
		else if (ISO8583MessageFactory.isISO8583Layout(layoutType)) {
			item = ISO8583MessageFactory.create(layoutType);
		}
	
		if (item == null) {
			throw new TransformerDaoException("unsupported layout type - " + layoutType);
		}
		return item;
	}

}
