package org.kne.cloud.network.klalb;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.net.InetSocketAddress;
import java.util.HashSet;

import org.kne.cloud.network.*;
import org.kne.cloud.network.klalb.ui.KLALBStateGUI3;
import org.kne.cloud.network.klalb.ui.Language;
import org.kne.cloud.network.klalb.ui.UIEnv;

import java.util.Set;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;

public class KLALBProxySystem {
	private Set<Proxy> proxys=new HashSet<>();
	private KLALBController klalbController;
	private KLALBRemoteManagement krm;
	private KLALBConfig config;
	private Gson gson;
	private File jsonFile;
	{
		GsonBuilder gb=new GsonBuilder().setPrettyPrinting();
		MultiProtocolSocketAddress.registerToGsonBuilder(gb);
		KLALBConfigItem.registerToGsonBuilder(gb);
		gson=gb.create();
	}
	public Set<Proxy> getProxys() {
		return proxys;
	}
	public KLALBController getKlalbController() {
		return klalbController;
	}
	public KLALBProxySystem(Reader json) {
		loadConfigJson(json);
	}
	public KLALBProxySystem(String json) {
		loadConfigJson(json);
	}

	public KLALBProxySystem(JsonElement json) {
		loadConfigJson(json);
	}
	public KLALBProxySystem(File jsonFile) throws IOException {
		loadConfigJson(jsonFile);
	}
	public KLALBProxySystem() {
	}
	
	public void enableRemoteManagement() throws IOException {
		if(krm==null) {
		krm=new KLALBRemoteManagement(this);
		}else {
			throw new IllegalStateException("Remote Management already enabled!");
		}
	}
	
	public void enableRemoteManagement(MultiProtocolSocketAddress bind) throws IOException {
		if(krm==null) {
		krm=new KLALBRemoteManagement(this,bind);
		}else {
			throw new IllegalStateException("Remote Management already enabled!");
		}
	}
	
	public boolean isRemoteManagementEnabled() {
		return krm!=null;
	}
	
	public void disableRemoteManagement() {
		if(krm!=null) {
			krm.close();
			krm=null;
		}
	}
	
	public void loadConfigJson(File jsonFile) throws IOException {
		this.jsonFile=jsonFile;
		if(jsonFile.exists()) {
			FileReader fr = null;
			try {
				fr = new FileReader(jsonFile);
				loadConfigJson(fr);
			} finally {
				if (fr != null)
					fr.close();
			}
		}else{
			loadConfig(KLALBConfig.getDefault());
		}
		
	}
	public void loadConfigJson(String json) {
		loadConfigJson(new JsonParser().parse(json));
	}
	public void loadConfigJson(Reader json) {
		loadConfigJson(new JsonParser().parse(json));
	}
	public void loadConfigJson(JsonElement json) {
		KLALBConfig config= gson.fromJson(json, KLALBConfig.class);
		loadConfig(config);
	}
	public void loadConfig(KLALBConfig config) {
		this.config=config;
		for(KLALBConfigItem item:config) {
			if(item instanceof KLALBControllerConfigItem) {
				KLALBControllerConfigItem kcci=(KLALBControllerConfigItem) item;
				String lstr=kcci.getLanguage();
				if(lstr!=null) {
					Language lang=Language.valueOf(lstr);
					if(lang!=null) {
						try {
							UIEnv.setRsb(lang.getFileName());
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
				
				klalbController=new KLALBController(kcci);
				
				
				
				
				String vsne=kcci.getVirtualSocketName();
				if(vsne!=null) {
					MultiProtocolSocketAddress.getSocketTypeRegister().put(vsne, klalbController.getStreamSocketType());
				}else{
					MultiProtocolSocketAddress.getSocketTypeRegister().put(CONST.KLTP_VIRTUAL_SOCKET_NAME, klalbController.getStreamSocketType());
				}
				MultiProtocolSocketAddress tcple=kcci.getTCPListen();
				if(tcple!=null) {
					
					SocketChannelListener tcpl = null;
					try {
						tcpl = new SocketChannelListener(tcple);
					
					tcpl.setCon((soc)->{
					
						KLALBRemoteLink krs=null; 
						try {
							krs = new KLALBRemoteLink(klalbController,new StreamChannelKLALBPacketLink(soc));
							klalbController.addRemoteLine(krs);
						} catch (IOException e) {
							e.printStackTrace();
						}
						
					});
					MultiProtocolSocketAddress mpsa2=new MultiProtocolSocketAddress(tcple.getProtocol(), tcple.getHost(),((InetSocketAddress)tcpl.getServerSocketChannel().getLocalAddress()).getPort());
					klalbController.getListenSocketAddress().add(mpsa2);
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
				MultiProtocolSocketAddress udple=kcci.getUDPListen();
				if(udple!=null) {
					DatagramSocketListener udpl = null;
					try {
						udpl = new DatagramSocketListener(udple);
					
						udpl.setCon((soc)->{
					
						KLALBRemoteLink krs=null;
						try {
							krs = new KLALBRemoteLink(klalbController,new SplitedDatagramKLALBPacketLink(soc));
							klalbController.addRemoteLine(krs);
						} catch (IOException e) {
							e.printStackTrace();
						}
						
					});
						MultiProtocolSocketAddress mpsau2=new MultiProtocolSocketAddress(udple.getProtocol(), udple.getHost(),((InetSocketAddress)(udpl.getDatagramServerSocket().getLocalSocketAddress())).getPort());
						//klalbController.getListenSocketAddress().add(mpsau2);
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
				
				
			}else if(item instanceof SocketBridgeConfigItem) {
				SocketBridgeConfigItem scci=(SocketBridgeConfigItem) item;
				try {
					proxys.add(scci.createProxy(klalbController));
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	
	private KLALBStateGUI3 kgui;
	public KLALBStateGUI3 getKLALBGUI() {
		if(kgui==null) {
		kgui=new KLALBStateGUI3(klalbController);
		kgui.loadConfig(config);
		kgui.setSaveComsumer((cfg)->{
			String json=gson.toJson(cfg);
			if(jsonFile!=null) {
				FileWriter fw = null;
				try {
					fw=new FileWriter(jsonFile);
					fw.write(json);
				}catch(IOException e) {
					e.printStackTrace();
				}finally {
					if(fw!=null)
						try {
							fw.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
				}
			}
				
		});
		}
		return kgui;
	}
	public KLALBConfig getConfig() {
		return config;
	}
	public KLALBControllerConfigItem getControllerConfig() {
		for(KLALBConfigItem item:config) {
			if(item instanceof KLALBControllerConfigItem)
				return (KLALBControllerConfigItem) item;
		}
		return null;
	}
}
