package org.allmyinfo.opmeta.nodes.core;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.allmyinfo.nodes.NodeKey;
import org.allmyinfo.operation.BaseOperation;
import org.allmyinfo.operation.MethodDefinition;
import org.allmyinfo.operation.Operation;
import org.allmyinfo.operation.meta.BaseOperationDescriptorImpl;
import org.allmyinfo.operation.meta.BaseSimpleOperandDescriptor;
import org.allmyinfo.operation.meta.FactoryContext;
import org.allmyinfo.operation.meta.OperandConflictException;
import org.allmyinfo.operation.meta.OperandDescriptor;
import org.allmyinfo.operation.meta.OperandReader;
import org.allmyinfo.operation.meta.OperandWriter;
import org.allmyinfo.operation.nodes.core.GetNodeByKey;
import org.allmyinfo.operation.nodes.core.GetNodeByKeyWithCreateOption;
import org.allmyinfo.operation.nodes.core.GetNodeByKeyWithHandler;
import org.eclipse.jdt.annotation.NonNull;

public class GetNodeByKeyDescriptor extends BaseOperationDescriptorImpl {
	private final @NonNull Set<OperandDescriptor> operands;
	public final @NonNull OperandDescriptor keyOperand = new BaseSimpleOperandDescriptor() {
		@Override
		public @NonNull String getName() {
			return "key";
		}

		@Override
		protected @NonNull Class<?> getDataType() {
			return NodeKey.class;
		}
	};
	public final @NonNull OperandDescriptor handlerOperand = new BaseSimpleOperandDescriptor() {
		@Override
		public @NonNull String getName() {
			return "handler";
		}

		@Override
		protected @NonNull Class<?> getDataType() {
			return MethodDefinition.class;
		}

		@Override
		public boolean isRequired() {
			return false;
		}
	};
	public final @NonNull OperandDescriptor createOperand = new BaseSimpleOperandDescriptor() {
		@Override
		public @NonNull String getName() {
			return "create";
		}

		@Override
		protected @NonNull Class<?> getDataType() {
			return Boolean.class;
		}

		@Override
		public boolean isRequired() {
			return false;
		}
	};

	public GetNodeByKeyDescriptor() {
		final Set<OperandDescriptor> map = new HashSet<OperandDescriptor>();
		map.add(keyOperand);
		map.add(handlerOperand);
		map.add(createOperand);
		@SuppressWarnings("null")
		final @NonNull Set<OperandDescriptor> operands = Collections.unmodifiableSet(map);
		this.operands = operands;
	}

	@Override
	public @NonNull String getName() {
		return "getNodeByKey";
	}

	@Override
	public @NonNull Set<OperandDescriptor> getOperands() {
		return operands;
	}

	@Override
	public @NonNull Operation getOperation(final @NonNull FactoryContext context,
			final @NonNull OperandReader reader) {
		context.setOperation(this);
		final Operation keyOperation = require(context, reader, keyOperand);
		final Operation handlerOperation = get(context, reader, handlerOperand);
		final Operation createOperation = get(context, reader, createOperand);
		if (handlerOperation != null) {
			if (createOperation != null)
				throw new OperandConflictException(handlerOperand, createOperand, this);
			return new GetNodeByKeyWithHandler(keyOperation, handlerOperation);
		}
		if (createOperation != null)
			return new GetNodeByKeyWithCreateOption(keyOperation, createOperation);
		return new GetNodeByKey(keyOperation);
	}

	@Override
	public void writeOperation(final @NonNull BaseOperation operation,
			final @NonNull OperandWriter writer) {
		if (operation instanceof GetNodeByKeyWithHandler) {
			final GetNodeByKeyWithHandler castOp = (GetNodeByKeyWithHandler) operation;
			writer.writeSimpleOperand("key", castOp.keyOp);
			writer.writeSimpleOperand("handler", castOp.handlerOp);
		}
		if (operation instanceof GetNodeByKeyWithCreateOption) {
			final GetNodeByKeyWithCreateOption castOp = (GetNodeByKeyWithCreateOption) operation;
			writer.writeSimpleOperand("key", castOp.keyOp);
			writer.writeSimpleOperand("create", castOp.createOp);
		}
		final GetNodeByKey castOp = (GetNodeByKey) operation;
		writer.writeSimpleOperand("key", castOp.keyOp);
	}
}
