package wa.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.FileSystem;
import org.hyperic.sigar.FileSystemUsage;
import org.hyperic.sigar.Mem;
import org.hyperic.sigar.NetFlags;
import org.hyperic.sigar.NetInterfaceConfig;
import org.hyperic.sigar.NetInterfaceStat;
import org.hyperic.sigar.NetRoute;
import org.hyperic.sigar.ProcCpu;
import org.hyperic.sigar.ProcUtil;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import org.hyperic.sigar.CpuInfo;
import org.hyperic.sigar.Swap;
import org.hyperic.sigar.cmd.Shell;

/**
 * Generates an XML with the status of the server, extended 
 * information can be obtained by adding a parameter extended 
 * in the GET request.
 * 
 * @author Ziver
 */
public class ServerStatus extends HttpServlet{
	private static final long serialVersionUID = 1L;

	public  void doGet(HttpServletRequest request, HttpServletResponse  response) throws IOException{
		PrintWriter out = response.getWriter();
		boolean extended = request.getParameter("extended") != null;
		
		try {
			Shell shell = new Shell();
			Sigar sigar = shell.getSigar();
			StringBuilder output = new StringBuilder();
			output.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
			output.append("<serverstatus>");
			
			//****************** CPU **********************
			CpuInfo cpu_info = sigar.getCpuInfoList()[0];
			CpuPerc[] cpus = sigar.getCpuPercList();
			
			output.append("<cpu>");
			//output.append(cpu_info.getTotalCores());
			for(int i=0; i<cpu_info.getTotalCores() ;i++){
				output.append("<core id='");
				output.append( i );
				output.append("' load='");
				output.append((int)(cpus[i].getCombined()*100));
				output.append("' />");
			}
			output.append("</cpu>");
			
			//***************** Memory *******************
			Mem mem   = sigar.getMem();
	        Swap swap = sigar.getSwap();
			
			output.append("<memory total='");
			output.append(mem.getRam());
			output.append("' used='");
			output.append( (int)format(mem.getUsed()) );
			output.append("' />");
			
			output.append("<swap total='");
			output.append( (int)format(swap.getTotal()) );
			output.append("' used='");
			output.append( (int)format(swap.getUsed()) );
			output.append("' />");
			
			//**************** NETWORK ******************
			output.append("<network>");
			String[] net_intfs = sigar.getNetInterfaceList();
			for( String intf : net_intfs ){
				NetInterfaceStat net_stat = sigar.getNetInterfaceStat( intf );
				NetInterfaceConfig net_conf = sigar.getNetInterfaceConfig( intf );
				output.append("<interface name='");
				output.append( intf );
				output.append("' up='");
				output.append( (net_conf.getFlags() & NetFlags.IFF_UP) > 0 );
				output.append("' tx='");
				output.append( format(net_stat.getTxBytes()) );
				output.append("' rx='");
				output.append( format(net_stat.getRxBytes()) );
				output.append("' dropped='");
				output.append( net_stat.getRxDropped()+net_stat.getTxDropped() );
				output.append("' error='");
				output.append( net_stat.getRxErrors()+net_stat.getTxErrors() );
				if(extended){
					output.append("' ip='");
					output.append( net_conf.getAddress() );
					output.append("' netmask='");
					output.append( net_conf.getNetmask() );
					output.append("' mac='");
					output.append( net_conf.getHwaddr() );
					output.append("' type='");
					output.append( net_conf.getType() );
					output.append("' flags='");
					output.append( NetFlags.getIfFlagsString(net_conf.getFlags()) );
					output.append("' description='");
					output.append( net_conf.getDescription() );
				}
				output.append("' />");
			}			
			output.append("</network>");
			
			//***************** HDD ********************
			FileSystem[] hdds = sigar.getFileSystemList();
			output.append("<hdd>");
			for(FileSystem hdd : hdds){
				if( hdd.getType() != FileSystem.TYPE_LOCAL_DISK )
					continue;
				//System.out.println("HDD: "+hdd);
				FileSystemUsage hdd_use = sigar.getFileSystemUsage(hdd.getDirName());
				output.append("<disk name='");
				output.append(new String(hdd.getDevName().getBytes(),"UTF-8"));
				output.append("' total='");
				output.append( format(hdd_use.getTotal()) );
				output.append("' used='");
				output.append( format(hdd_use.getTotal() - hdd_use.getFree()) );
				output.append("' free='");
				output.append( format(hdd_use.getFree()) );				
				//if(extended){
				output.append("' type='");
				output.append(hdd.getSysTypeName());
				output.append("' mount='");
				output.append(hdd.getDirName());
				//}
				output.append("' />");
			}
			output.append("</hdd>");

			//******************************************************************
			//***************************   EXTENDED    ************************
			if(extended){
				//****************** ROUTING TABLE *************************
				NetRoute[] routes = sigar.getNetRouteList();
				output.append("<routing_table>");
				for( NetRoute route : routes ) {
					output.append("<route interface='");
					output.append( route.getIfname() );
					output.append("' destination='");
					output.append( route.getDestination() );
					output.append("' gateway='");
					output.append( route.getGateway() );
					output.append("' netmask='");
					output.append( route.getMask() );
					output.append("' flag='");
					output.append( routing_flags(route.getFlags()) );
					output.append("' metric='");
					output.append( route.getMetric() );
					output.append("' />");
		        }
				output.append("</routing_table>");
				
				//****************** APPLICATIONS *************************
				long[] pids = Shell.getPids(sigar, new String[0]);
				output.append("<applications>");
				for( long pid : pids ) {
					String app_user = "UNKNOWN";
					long app_time = 0;
					String app_load = "-";
					try{
						ProcCpu cpu = sigar.getProcCpu(pid);
						app_load = CpuPerc.format(cpu.getPercent());
					}catch(SigarException e){}
					try{
						app_user = sigar.getProcCredName(pid).getUser();
					}catch(SigarException e){}
					try{
						app_time = sigar.getProcTime(pid).getTotal() / 1000;
					}catch(SigarException e){}
	                output.append("<app pid='");
					output.append( pid );
					output.append("' user='");
					output.append( app_user );
					output.append("' size='");
					output.append( format(sigar.getProcMem(pid).getSize()) );
					output.append("' cputime='");
					output.append( app_time/60 + ":" + app_time%60 );
					output.append("' cpu='");
					output.append( app_load );
					output.append("' command='");
					output.append( ProcUtil.getDescription(sigar, pid) );
					output.append("' />");	
		        }
				output.append("</applications>");
				
				//****************** UPTIME *************************
				output.append("<uptime>");
				output.append( formatUptime(sigar.getUptime().getUptime()) );
				output.append("</uptime>");
				
				//****************** HARDWARE INFO *************************
				
			}			
			
			output.append("</serverstatus>");
			out.println(output.toString());
		} catch (SigarException e) {
			e.printStackTrace();
		}
	}
	
    private static String routing_flags(long flags) {
        StringBuffer sb = new StringBuffer();
        if ((flags & NetFlags.RTF_UP) != 0) {
            sb.append('U');
        }
        if ((flags & NetFlags.RTF_GATEWAY) != 0) {
            sb.append('G');
        }
        return sb.toString();
    }
    
    /**
     * Formats a time to "Xdays Xh Xmin"
     */
    private static String formatUptime(double uptime) {
        StringBuilder ret = new StringBuilder();
        
        int days = (int)uptime / (60*60*24);
        int minutes, hours;

        if (days != 0) {
            ret.append(days);
            if( days > 1 )
            	ret.append( " days " );
            else
            	ret.append( " day " );
        }

        minutes = (int)uptime / 60;
        hours = minutes / 60;
        hours %= 24;
        minutes %= 60;

        if (hours != 0) {
            ret.append( hours );
            ret.append( "h " );
        }
        ret.append( minutes );
        ret.append( "min " );
        return ret.toString();
    }
    
    /**
     * Takes in a size of B and returns MB
     */
    private static double format(long size){
    	double tmp = ((double)size/(1024*1024));
    	tmp = (int)(tmp*10)/10.0;
    	return tmp;
    }
}
