package cc;
import java.net.*;
import java.util.Vector;

public class Sender extends Streamer{

    int delay = 0;
    int size = 0;
	String label;
	long lastRequest = 0;
	
	Vector packetsToResend = new Vector();
    
    DatagramSocket socket = null;
    DatagramPacket packet = null;
    byte buf[];
    
    String host = null;
    int port = 0;
    
	Sender(String host, int port, int size, int delay) 
			throws SocketException, UnknownHostException{
		socket = new DatagramSocket();
		buf = new byte[Parameters.getMaxSize()];

		this.host = host;
		this.port = port;
		
		
		this.size = size;
		this.delay = delay;
		
		this.lastRequest = System.currentTimeMillis();
	}

    public void run(){
		trace("->");
		try{
            int i = 0;
            InetAddress iHost = InetAddress.getByName(host);
            
			int loopCounter = 0;
            
			//Used to fit delay to loop overhead 
			int correctedDelay = delay;
			
			//Used to note changes in delay requested by Client 
			int lastDelay = delay;
			
			long currentTime, lastSendTime = System.currentTimeMillis();
			
			while (!finish){
				
				//Verifies if there are packets to resend
				if (packetsToResend.size() != 0){
					Integer pNumber = new Integer(packetsToResend.remove(0).toString());
					label = setLabel(pNumber.intValue());		
				}
				else{			
	                label = setLabel(++i);
				}
				
				for (int j=0 ; j<label.length() && j<size ; j++)
                    buf[j] = label.getBytes()[j];
                packet = new DatagramPacket(buf, size, iHost, port);
	
				//PROBLEM AT GSD, time diverge form requested 
				Thread.sleep(correctedDelay); //this.wait(delayAdjusted);
                socket.send(packet);
				loopCounter ++ ;

				//Verifies if is time to check inter-package time
				currentTime = System.currentTimeMillis();					
				if (currentTime >= (lastSendTime + (Parameters.getInterval()*1000))){

					//Verifies if client is alive
					if (lastRequest < (currentTime - (Parameters.getTimeout()*1000))){
						finish = true; 
					}
					
					//Adjust delay
					if (lastDelay == delay){
						int realDelay = (int) ((currentTime - lastSendTime)/loopCounter);
						if (realDelay >= delay)
							correctedDelay -= realDelay - delay;
						else
							correctedDelay += delay - realDelay;
						if (correctedDelay < 0){
							correctedDelay = 0;
						}
					}
						
					lastSendTime = currentTime;
					lastDelay = delay;
					loopCounter = 0;		
				}
            }
            socket.close();
            completeStreaming();
        }
        catch(Exception e){
			e.printStackTrace();
        }
        finally{
            finish = true;
            socket.close();
        }
		trace("<-");
    }
    
	private String setLabel (int i) {
		//index
		String ret = i + ";";
		//timestamp
		ret += System.currentTimeMillis() + ";";
		//size
		ret += size + ";";
		//delay
		ret += delay + ";";
		
		return ret;
	}
	
    public void setSize (int size) {
		if (size > Parameters.getMaxSize())
			size = Parameters.getMaxSize();

		if (size < Parameters.getMinSize())
			size = Parameters.getMinSize();
    		
		if ((this.size != size) &&
			(Parameters.getMaxSize() >= size) &&
			(Parameters.getMinSize() <= size)){
			if (this.size >= size){
				this.size = size;
				trace("s-");
			}
			else{
				this.size = size;
				trace("s+");
			}
		}
    }
    
    public void setDelay (int delay) {
		if (delay > Parameters.getMaxDelay())
			delay = Parameters.getMaxDelay();

		if (delay < Parameters.getMinDelay())
			delay = Parameters.getMinDelay();
    		
		if ((this.delay != delay) &&
			(Parameters.getMaxDelay() >= delay) &&
			(Parameters.getMinDelay() <= delay)){
			if (this.delay >= delay){
				trace("d+");
				this.delay = delay;
			}
			else{
				trace("d-");
				this.delay = delay;
			}
		}
    }
    
    public int getSize() {
        return size;
    }

	public int getDelay() {
		return delay;
	}

	public void alive() {
		this.lastRequest = System.currentTimeMillis();
	}

	public void requestPackets(String listOfPackets) {
		System.out.println(listOfPackets);

		//Parse package label
		java.util.StringTokenizer st = new java.util.StringTokenizer(listOfPackets , ";");
		while(st.hasMoreElements()){
			packetsToResend.add(st.nextToken());
		}
	}
	
    private void trace(String context){
		System.out.println(context + " " + host +":"+ port +"\t"+ 
				this.size +"B\t"+ this.delay +"ms\t#"+ this.label);
    }
}
