import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.List;

import Six.Sixp.Client.*;
import Six.Sixp.Client.Error;

public class StreamingClientDemo implements Six.Sixp.Client.ISessionEventListener {
	
    private Client client;
    private ISession session;
    private MyLogger ml;
    private Map<Integer, DataListener> dataListeners;
    private Vector<SixpServerSettings> servers;
	private ProxySettings proxy;
    private ReconnectStrategy reconnectStrategy;
    private int reconnectIntervalInSec;
    private List<String> prls;
    private String userId;
    private String password;
    private Boolean isAsync;
    
    public StreamingClientDemo(Vector<SixpServerSettings> servers,
			ProxySettings proxy, ReconnectStrategy reconnectStrategy,
			int reconnectIntervalInSec, List<String> prls, String workDir,
			String userId, String password) {
    	
    	ml = new MyLogger(workDir);
    	dataListeners = new HashMap<Integer, DataListener>(10);	
		this.servers = servers;
		this.proxy = proxy;
		this.reconnectStrategy = reconnectStrategy;
		this.reconnectIntervalInSec = reconnectIntervalInSec;
		this.userId = userId;
		this.password = password;
		this.prls = prls;
		this.isAsync = true;
	}
    
    public String TicketQueryCallback()
    {
        //This callback i only used to deliver a ticket from our passport service.
        //This scenario is not covered in this demo

        return "uniqueticket";

    }
    
    public void RunAsync()
    {
        //Connect to list of servers with reconnnect interval.
        //Observe! If you are using this reconnect feature, keep in mind that when reconnected the fieldset (OnFieldsetReceived)
        //might be changed. If you have specified several servers in the list above, the version of these servers might differ and
        //the fieldset might also differ.
        isAsync = true;

        client= new Client();

        client.SetLogging(ml);

        try {
			session = client.CreateSession(this.servers, reconnectStrategy, reconnectIntervalInSec, proxy, this);
		} catch (SixpExceptionInvalidParameter e) {
			e.printStackTrace();
			System.out.println("Invalid parameter to Create Session!");
		} catch (SixpExceptionGeneral e) {
			e.printStackTrace();
		}
        try {
			session.ConnectAsync();
		} catch (SixpExceptionConnect e) {
			e.printStackTrace();
			System.out.println("Exception on connect to the SIXP server!");
		}
        System.out.println("Press q return to quit, press l return to turn off logging and count updates.");

        
        Boolean quit = false;
        while (!quit)
        {
                int c = -1;
				try {
					c = System.in.read();
				} catch (IOException e) {
					e.printStackTrace();
				}
                				
                if (c == 113) { // q key
                    quit = true;
                    break;
                } else if (c == 108) { // l key
                    Iterator<Map.Entry<Integer, DataListener>> it = dataListeners.entrySet().iterator();
                    
                    while (it.hasNext()) {
            			Map.Entry<Integer, DataListener> pairs = it.next();
            			if (pairs.getValue().getLoggaData() == true) {
                        	pairs.getValue().setLoggaData(false);
            			} else {
                        	pairs.getValue().setLoggaData(true);
            			}

                    }
                }
        }

        ClearDataListeners();
        session.Dispose();
        client.Dispose();
        ml.Dispose();
    }

	@Override
	public void OnConnected(ISession session, SixpServerSettings server, boolean reconnected) {
        ml.OnLogMessage(LogLevel.Info, "StreamingClientDemo.OnConnected");
        //Note! If you have been connected and this is fired due to a reconnect, all referenses to fieldsets must be thrown away.
        //The server that is connected now might be of a different version and might have another schema.

        if (isAsync)
        {
            //Demo in async mode. Do Login async
            ClassicLogin cl = new ClassicLogin(userId, password);
            try {
				session.LoginAsync(cl);
			} catch (SixpExceptionGeneral e) {
				System.out.println("Failed to login!");
				e.printStackTrace();
			}
        }
	}

	@Override
	public void OnConnecting(ISession session, SixpServerSettings server,
			boolean reconnecting) {
        ml.OnLogMessage(LogLevel.Info, "StreamingClientDemo.OnConnecting");
		
	}

	@Override
	public void OnDisconnected(ISession session, boolean unexpected) {
        System.out.println("OnDisconnected Unexpected=" + unexpected);

        ClearDataListeners();
	}

	@Override
	public void OnError(ISession session, SixpServerSettings server, Error errorCode,
			String errorMessage) {
        ml.OnLogMessage(LogLevel.Info, "StreamingClientDemo.OnError ec:" + errorCode + " em:" + errorMessage);
        System.out.println("OnError ec:" + errorCode + " em:" + errorMessage );		
	}

	@Override
	public void OnLoggedIn(ISession session, boolean reconnected) {
        ml.OnLogMessage(LogLevel.Info, "StreamingClientDemo.OnLoggedIn");
        if (!reconnected)
        {
            for (String prl : prls)
            {
                DataListener dl = new DataListener(session);
                //dl.LoggaData = false;
                try {
					System.out.println("Setup prl: " + prl);
					dl.setRequest(session.CreateRequest(prl, AcceptFormat.Table, true, dl));
				} catch (SixpExceptionNotLoggedIn e) {
					e.printStackTrace();
					System.out.println("Not logged into SIXP!");
				} catch (SixpExceptionInvalidParameter e) {
					e.printStackTrace();
					System.out.println("Invalid parameter!");
				}
                dataListeners.put(dl.getRequest().getId(), dl);
                dl.getRequest().ExecuteAsync();
            }
        }
	}

	@Override
	public void OnLoggedOut(ISession session, SixpServerSettings server, Error errorCode,
			String errorMessage) {
        ml.OnLogMessage(LogLevel.Info, "StreamingClientDemo.OnLoggeOut ec:" + errorCode + " em:" + errorMessage);
	}

	@Override
	public void OnTimingInfo(ISession session, long pingTurnaroundTime, long estimatedDelayTime, long queuesize) {
		System.out.println("ping: "+ pingTurnaroundTime + " delay: "+ estimatedDelayTime + "queuesize: " + queuesize);
	}
	
	private void ClearDataListeners() {
        Iterator<Map.Entry<Integer, DataListener>> it = dataListeners.entrySet().iterator();
        
        while (it.hasNext()) {
			Map.Entry<Integer, DataListener> pairs = it.next();
        	pairs.getValue().Reset();
        }
        
        dataListeners.clear();
	}
}
