package tests;

import org.junit.Test;

import algorithms.TypeInference;
import models.algebra.Expression;
import models.algebra.InvalidMessage;
import models.algebra.ParameterizedIdentifierIsFutureWork;
import models.algebra.UnificationFailed;
import models.algebra.ValueUndefined;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.ResourcePath;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import parser.Parser;
import parser.Parser.TokenStream;
import parser.exceptions.ExpectedColon;
import parser.exceptions.ExpectedDoubleQuotation;
import parser.exceptions.ExpectedRightBracket;
import parser.exceptions.WrongJsonExpression;
import simulator.Event;
import simulator.Resource;
import simulator.Simulator;
import simulator.SystemState;
import simulator.interfaces.INativeReceiver;
import simulator.interfaces.NativeSender;

public class NativeAccessTest {
	
	@Test
	public void test() {
		try {
			TokenStream stream = new Parser.TokenStream();
			Parser parser = new Parser(stream);
			
			// Construct a data transfer architecture model.
			DataTransferModel model = new DataTransferModel();
			ResourcePath sampleText = new ResourcePath("sampleText");
			model.addResourcePath(sampleText);
			
			DataTransferChannel nativeInputEventChannel = new DataTransferChannel("NativeInput");
			nativeInputEventChannel.setNative(true);
			ChannelMember textIn = new ChannelMember(sampleText);
			stream.addLine("curText:Str");
			Expression curStateIn = parser.parseTerm(stream, model);
			stream.addLine("input(nextText)");
			Expression messageIn = parser.parseTerm(stream, model);
			stream.addLine("nextText");
			Expression nextStateIn = parser.parseTerm(stream, model);
			textIn.getStateTransition().setCurStateExpression(curStateIn);
			textIn.getStateTransition().setMessageExpression(messageIn);
			textIn.getStateTransition().setNextStateExpression(nextStateIn);
			nativeInputEventChannel.addChannelMemberAsOutput(textIn);
			model.addInputChannel(nativeInputEventChannel);
			
			DataTransferChannel nativeOutputEventChannel = new DataTransferChannel("NativeOutput");
			nativeOutputEventChannel.setNative(true);
			ChannelMember textOut = new ChannelMember(sampleText);
			stream.addLine("curText:Str");
			Expression curStateOut = parser.parseTerm(stream, model);
			stream.addLine("output(nextText)");
			Expression messageOut = parser.parseTerm(stream, model);
			stream.addLine("nextText");
			Expression nextStateOut = parser.parseTerm(stream, model);
			textOut.getStateTransition().setCurStateExpression(curStateOut);
			textOut.getStateTransition().setMessageExpression(messageOut);
			textOut.getStateTransition().setNextStateExpression(nextStateOut);
			nativeOutputEventChannel.addChannelMemberAsInput(textOut);
			model.addChannel(nativeOutputEventChannel);
			TypeInference.infer(model);
			
			// Create simulator
			Simulator simulator = new Simulator(model);
			
			// Initial state
			SystemState initialState = simulator.init();
			Resource ioResource = initialState.getResource("sampleText");
			
			// Connect the simulator and native code
			NativeSender textSender = new NativeSender(simulator, nativeInputEventChannel, sampleText, ioResource) {	// Native code => model
			};
			INativeReceiver textReceiver = new INativeReceiver() {		// Model to native code
				@Override
				public void onReceiveFromModel(Event event, SystemState nextSystemState) {	// Receive a message from the model
					Expression message = event.getMessage();
					System.out.println(message);
				}
			};
			simulator.addNativeReceiver(textReceiver, nativeOutputEventChannel);
			
			stream.addLine("input(\"HelloWorld\")");
			Expression sendMessage = parser.parseTerm(stream, model);
			textSender.sendToModel(sendMessage);		// Send a message to the model
			
		} catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) {
			e.printStackTrace();
		}

	}

}
