package com.eactive.eai.adapter.socket2.protocol.admin;

import com.eactive.eai.adapter.socket2.common.CommonLib;
import com.eactive.eai.adapter.socket2.config.SocketAdapterContext;
import com.eactive.eai.adapter.socket2.config.SocketAdapterManager;
import com.eactive.eai.adapter.socket2.protocol.ISocketAdapter;
import com.eactive.eai.adapter.socket2.protocol.ProtocolHandler;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.Options;
import org.apache.mina.common.IoSession;

import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;

public class AdminHandler extends ProtocolHandler {
//	private static final String HANDLER_STATE_KEY = AdminHandler.class.getName() + ".STATE";
	SocketAdapterManager manager = SocketAdapterManager.getInstance();
	
	private enum State {
		STATE_HANDSHAKE, 
		STATE_MESSAGE;
	}

	private static class HandlerState {
		State state = State.STATE_HANDSHAKE;
	}
	
	AdminParser parser;

	
	public AdminHandler(ISocketAdapter adapter) {
		super(adapter);	
		
		this.parser = new AdminParser();
	}	
	
//	private void initState(IoSession session) {
//		HandlerState handlerState = (HandlerState) session.getAttribute(HANDLER_STATE_KEY);
//
//		if (handlerState == null) {
//			handlerState = new HandlerState();
//			// NO_ACK Protocol
//			if ("N".equals(context.getAckSupport())) {
//				handlerState.state = State.STATE_MESSAGE;
//				setSessionState(session, handlerState.state.toString());
//
//				if (context.isPermanent()) {
//					sessionPool.release(session);
//				} else {
//					SimpleFuture handshakeFuture = (SimpleFuture) session.getAttribute(HANDSHAKE_FUTURE);
//					handshakeFuture.set(Boolean.TRUE);
//				}
//				adapter.setAdapterStatus(true);
//			}
//			session.setAttribute(HANDLER_STATE_KEY, handlerState);
//		}
//	}

	
	@Override
	protected byte[] processRequest(IoSession session, byte[] request) {
		String result = processCommand(new String(request));
		if(result == null) {
			return "".getBytes();
		}
		else {
			return result.getBytes();
		}
	}

	private enum Command {
		HELP,				// help
		CC, CD,         	// change context
		LIST, LS,   		// list status
		START, 				// start
		STOP,				// stop
		//GET,				// get
		SET,				// set
		DISCONNECT,			// distconnect
		QUIT, EXIT, BYE,	// quit
		UNKNOWN;

        public static Command getValue(String value) {
            try {
                return valueOf(value.toUpperCase());
            } catch (Exception e) {
                return UNKNOWN;
            }
        }
	}
	
	private enum Option {
		CONNECTION,
		SESSION,
		TRACE,
		CONFIG,
		UNKNOWN;
		
        public static Option getValue(String value) {
            try {
                return valueOf(value.toUpperCase());
            } catch (Exception e) {
                return UNKNOWN;
            }
        }
	}
	
	public String processCommand(String command) {
		if (context.getTraceLevel() >= context.TRACE_LEVEL_DEBUG) {
			logger.debug(context.getAdapterName() + " | execute command : " + command);
		}		
	
		//String commands = "stop,start,get,set,disconnect";
		//String options = "connection,session,trace,config";
		//String inCmd = command;

		if (command == null || command.equals("")) {
			return "";
		}
		
	    // Parse the program arguments
		CommandLine line = null;
		try {
			line = parser.parse(command);
		} catch(Exception e) {
        	logger.error(context.getAdapterName() + " | " + e.getMessage(), e);
			adapter.addRecentError(context.getAdapterName() + " | " + e.getMessage(), e);
			return "[AdminHandler] " + e.getMessage();
		}
	
	    // Set the appropriate variables based on supplied options
	    boolean verbose = false;
	    boolean help = false;
	
	    if (line.hasOption('h')) {
	        help = true;
	    }
	
	    if (line.hasOption('v')) {
	        verbose = true;
	    }

		String[] args = line.getArgs();		
		String arg0 = args[0].toLowerCase();

		switch (Command.getValue(arg0)) {
		case HELP:
			return help();
		
		case CD:
		case CC:
			return cc(args);
			
		case LS:
		case LIST:
			return ls(args, verbose);
			
		case START:
			return start(args);
			
		case STOP:
			return stop(args);
			
		case DISCONNECT:
			return disconnect(args, command);

//		case GET:
//			if (args.length < 3) {
//				return "args.length < 3";
//			}
//			
//			switch (Option.getValue(args[1].toLowerCase())) {
//			case CONNECTION:
//				if (args[2].equalsIgnoreCase("all")) {
//					return manager.getConnectionInfo(null);
//				} else if (args[2].equalsIgnoreCase("summary")) {
//					return manager.getConnectionSummary();
//				} else {
//					return manager.getConnectionInfo(args[2]);
//				}
//			case CONFIG:
//				SocketAdapterContext ctx = manager.getContext(args[2]);
//				if (ctx == null) {
//					return CommonLib.getMessage("cannot get context");
//				} else {
//					return manager.getContext(args[2]).toString();
//				}
//			case SESSION:
//				if (args[2].equalsIgnoreCase("all")) {
//					return manager.getSessionInfo(null);
//				} else {
//					return manager.getSessionInfo(args[2]);
//				}
//			case UNKNOWN:
//			default:
//				return "Invalid Option : " + command;
//			}
//			
		case SET:
			if (args.length < 4) {
				return CommonLib.getMessage("args.length < 4");
			}

			// Connections
			int newCount = 0;
			try {
				newCount = Integer.parseInt(args[3]);
			} catch (Exception e) {
				return e.getMessage();
			}

			switch (Option.getValue(args[1].toLowerCase())) {
//			case CONNECTION:
//				return manager.adjustConnection(args[2], newCount);
//			case SESSION:
//				try {
//					return manager.adjustThread(args[2], newCount);
//				} catch (Exception e) {
//					return e.getMessage();
//				}
			case TRACE:
				SocketAdapterContext ctx = manager.getContext(args[2]);
				if (newCount < 0 || newCount > 2) {
					return "count is invalid";
				}
				if (ctx == null) {
					return "context is null";
				}
				ctx.setTraceLevel(newCount);
				String newLevel = "";

				switch(newCount) {
				case 0:
					newLevel = "0(ERROR)";
					break;
				case 1:
					newLevel = "1(INFO)";
					break;
				default:
					newLevel = "2(DEBUG)";
				}

				return "change trace level to " + newLevel;
			case UNKNOWN:
			default:
				return "Invalid Option : " + command;
			}
			
		case QUIT:
		case EXIT:
		case BYE:
			return "EXIT";
		case UNKNOWN:
		default:
			return "    Unknown Command : " + command + ". Type 'help'";
		}
	}
	
	private String help() {
		StringBuilder sb = new StringBuilder();
		sb.append("\n    help\n");
		sb.append("    -------------------------------------------------------------------\n");
		sb.append("    help                            - 도움말\n");
		sb.append("    list|ls|stat|st|                - List properties, Status\n");
		sb.append("    cd|cc [AdapterGroup].[Adapter]  - Change Context\n");
		sb.append("    start                           - Start socket adapter\n");
		sb.append("    stop                            - Stop socket adapter\n");
		sb.append("    disconnect                      - Disconnect session\n");
		sb.append("    set                             - Modify socket adapter property\n");
		return sb.toString();
	}
	
	private String cc(String[] args) {
		if (args.length < 2) {
			return "    usage : cc|cd [adapter group].[adapter]";
		}
		
		ISocketAdapter adapter = manager.getSocketAdapter(args[1]);
		if (adapter != null)
			return args[1];
		else
			return "    ERROR - context (adapter group, adapter) does not exist : " + args[1];
	}
	
	private String ls(String[] args, boolean verbose) {
		StringBuilder sb = new StringBuilder();
		sb.append("\n");
    	sb.append(String.format("    %-30s  %-10s %-10s %10s %10s %10s %10s\n", 
    			  "adapter",
    			  "protocol",
    			  "state",
    			  "pool",
    			  "recv",
    			  "send",
    			  "error"));
    	sb.append("    -----------------------------------------------------------------------------------------------------------\n");
    	String prev = "";

        ConcurrentHashMap<String, ConcurrentHashMap<String, ISocketAdapter>> adapterGroups = (ConcurrentHashMap)manager.getSocketAdapterGroups();
	    for (Enumeration e = adapterGroups.keys(); e.hasMoreElements();) {
	    	String adapterGroupName = (String) e.nextElement();
	    	ConcurrentHashMap<String, ISocketAdapter> adapters = adapterGroups.get(adapterGroupName);
	    	//sb.append(adapterGroupName + "\n");
		    for (Enumeration<ISocketAdapter> ee = adapters.elements() ; ee.hasMoreElements() ;) {
		    	ISocketAdapter adapter = ee.nextElement();
		    	if (!prev.equals(adapterGroupName)) {
		    		sb.append(String.format("    %-31s %s\n", 
		    				                adapterGroupName, 
		    				                "---------------------------------------------------------------------------"));
		    	}
		    	sb.append(String.format("    .%-30s %-10s %-10s %10d %10d %10d %10d\n",
		    			  adapter.getContext().getAdapterName(),
		    			  adapter.getContext().getProtocol().length() > 5 ? adapter.getContext().getProtocol().substring(0,5) : adapter.getContext().getProtocol(),
    					  adapter.getState().name(),
		    			  adapter.getSessionPool().peekSize(),
		    			  adapter.getMessageRecv(),
		    			  adapter.getMessageSend(),
		    			  adapter.getRecentErrorCount()));
		    	prev = adapterGroupName;
		    }
	    }
	    
		return sb.toString();
	}
	
	private String start(String[] args) {
		if (args.length < 3) {
			return "    usage : start [adapter group] [adapter]";
		}

		return manager.start(args[1], args[2]);
	}
	
	private String stop(String[] args) {
		if (args.length < 3) {
			return "    usage : stop [adapter group] [adapter]";
		}
		
		manager.stop();
		return "stopped";
	}
	
	private String disconnect(String[] args, String command) {
		if (args.length < 3) {
			return "args.length < 3";
		}
		return manager.disconnect(args[1].toLowerCase(), args[2]);
	}
}

class AdminParser {
	CommandLineParser parser;
	Options options;
	
	public AdminParser() {
	    parser = new BasicParser();
	    options = new Options();
	    options.addOption("h", "help", false, "Print this usage information");
	    options.addOption("v", "verbose", false, "Print out VERBOSE information");
	    //options.addOption("f", "file", true, "File to save program output to");
	}
	
	public CommandLine parse(String cmd) throws Exception {
		return parser.parse(options, cmd.split("\\s"));
	}
}
