/**
Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package bftsmart.tom;

import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import bftsmart.communication.client.CommunicationSystemClientSide;
import bftsmart.communication.client.CommunicationSystemClientSideFactory;
import bftsmart.communication.client.ReplyReceiver;
import bftsmart.reconfiguration.ClientViewController;
import bftsmart.tom.core.messages.TOMMessage;
import bftsmart.tom.core.messages.TOMMessageType;
import java.io.Closeable;

/**
 * This class is used to multicast messages to replicas and receive replies.
 */
public abstract class TOMSender implements ReplyReceiver, Closeable, AutoCloseable {

	private int me; // process id

	private ClientViewController viewController;
	private String configHome = "config";
	private int session = 0; // session id
	private int sequence = 0; // sequence number
	private int unorderedMessageSequence = 0; // sequence number for readonly messages
	private CommunicationSystemClientSide cs; // Client side comunication system
	private Lock lock = new ReentrantLock(); // lock to manage concurrent access to this object by other threads
	private boolean useSignatures = false;
	private AtomicInteger opCounter = new AtomicInteger(0);

	/**
	 * Creates a new instance of TOMulticastSender
	 *
	 * TODO: This may really be empty?
	 */
	public TOMSender() {
	}

	public void close(){
		cs.close();
	}

	public CommunicationSystemClientSide getCommunicationSystem() {
		return this.cs;
	}


	//******* EDUARDO BEGIN **************//
	public ClientViewController getViewManager(){
		return this.viewController;
	}

	/**
	 * This method initializes the object
	 * TODO: Ask if this method cannot be protected (compiles, but....)
	 *
	 * @param processId ID of the process
	 */
	public void init(int processId) {
		this.viewController = new ClientViewController(processId, "config");
		startsCS(processId);
	}

	public void init(int processId, String configHome) {
		this.viewController = new ClientViewController(processId,configHome);
		startsCS(processId);
	}

	private void startsCS(int clientId) {
		this.cs = CommunicationSystemClientSideFactory.getCommunicationSystemClientSide(clientId, this.viewController);
		this.cs.setReplyReceiver(this); // This object itself shall be a reply receiver
		this.me = this.viewController.getStaticConf().getProcessId();
		this.useSignatures = this.viewController.getStaticConf().getUseSignatures()==1?true:false;
		this.session = new Random().nextInt();
	}
	//******* EDUARDO END **************//


	public int getProcessId() {
		return me;
	}

	public int generateRequestId(TOMMessageType type) {
		lock.lock();
		int id;
		if(type == TOMMessageType.ORDERED_REQUEST)
			id = sequence++;
		else
			id = unorderedMessageSequence++; 
		lock.unlock();

		return id;
	}

	public int generateOperationId() {
		return opCounter.getAndIncrement();
	}

	public void TOMulticast(TOMMessage sm) {
		cs.send(useSignatures, this.viewController.getCurrentViewProcesses(), sm);
	}


	public void TOMulticast(byte[] m, int reqId, int operationId, TOMMessageType reqType) {
		cs.send(useSignatures, viewController.getCurrentViewProcesses(),
				new TOMMessage(me, session, reqId, operationId, m, viewController.getCurrentViewId(),
						reqType));
	}


	public void sendMessageToTargets(byte[] m, int reqId, int operationId, int[] targets, TOMMessageType type) {
		if(this.getViewManager().getStaticConf().isTheTTP()) {
			type = TOMMessageType.ASK_STATUS;
		}
		cs.send(useSignatures, targets,
				new TOMMessage(me, session, reqId, operationId, m, viewController.getCurrentViewId(), type));
	}

	public int getSession(){
		return session;
	}
}
