package page.tools.management;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;

import org.wikiwebserver.core.Privilege;
import org.wikiwebserver.core.WareHouse;
import org.wikiwebserver.handler.http.FormData;
import org.wikiwebserver.handler.http.HTTPHandler;
import org.wikiwebserver.handler.http.interfaces.HTTPResponder;
import org.wikiwebserver.html.TemplatedPage;
import org.wikiwebserver.util.comparator.FileNameComparator;

import page.config.SiteTemplatedPage;
import page.tools.entity.User;
import static org.wikiwebserver.html.HTMLHelper.*;


public class LogViewer implements HTTPResponder {
    
    private String resourcePath = "/templates/default/tools/";
    public static final String LF = "\r\n";    
	
    public Object respond(HTTPHandler conn) throws IOException {
        
        User u = User.getUser(conn.getRequest());
        if (u == null || u.getPrivilege().isBelow(Privilege.ADMIN)) {
            throw new SecurityException("Only administrators can view logs");
        }        
        
        int limit = 10000;
        
        String action = null;
        String logName = "requests";
        String startDate = null;
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
        String endDate = formatLogFileDate(System.currentTimeMillis());        
        String[] searchStrings = null;
        int[] searchTypes = null;   
        boolean[] showCols = null;        
        
        FormData formData = conn.getRequest().getFormData();
        if (formData == null) {
            formData = new FormData();
            formData.set("logName", logName);
        }
            
        action = formData.getFirst("action");
        logName = sanitize(formData.getFirst("logName"));
        startDate = sanitize(formData.getFirst("startDate"));
        
        // Column search criteria
        try {
            String numColumnsString = formData.getFirst("numColumns");
            if (numColumnsString != null) {
                int numColumns = Integer.parseInt(numColumnsString);
                searchStrings = new String[numColumns];
                searchTypes = new int[numColumns];
                showCols = new boolean[numColumns];                
                
                for (int i=0; i<numColumns; i++) {
                    searchStrings[i] = formData.getFirst("String_" + i);
                    
                    searchTypes[i] = WareHouse.SEARCH_NULL;
                    if ((formData.getFirst("Type_" + i + "_Exact") != null)) {
                        searchTypes[i] = WareHouse.SEARCH_EQUALS;
                    } else if (searchStrings[i] != null && searchStrings[i].length() > 0) {
                        searchTypes[i] = WareHouse.SEARCH_CONTAINS;
                    }
                    
                    showCols[i] = formData.getFirst("Show_" + i) != null;                    
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        String css = resourcePath + "logview.css";

        
        if (action != null && action.equals("Search")) {
            
            conn.getOutputStream().write("<html><head><title>" + logName + "</title>");
            conn.getOutputStream().write("<link rel='stylesheet' href='" + css + "' type='text/css'>");
            conn.getOutputStream().write("</head><body><div>"); 
            
            try {      
                
                Calendar cal = new GregorianCalendar();
                if (startDate != null) {
                   
                    cal.setTimeInMillis(parseLogFileDate(startDate));
                    while (!startDate.equals(endDate)) {
                        File logFile = WareHouse.getLogFile(logName, cal.getTimeInMillis());
                        WareHouse.searchLogAndOutputTable(logName, logFile, searchStrings, searchTypes, showCols, limit, conn.getOutputStream());            
                        
                        cal.add(Calendar.DATE, 1);
                        startDate = formatLogFileDate(cal.getTime().getTime());    
                                        
                    }
                }
                         
                File logFile = WareHouse.getLogFile(logName, cal.getTimeInMillis());
                // Disable gzipping, it prevents streaming in many browsers
                conn.getResponse().getHeaders().set("Content-Encoding", "none");
                WareHouse.searchLogAndOutputTable(logName, logFile, searchStrings, searchTypes, showCols, limit, conn.getOutputStream());               

                
            } catch (Exception ex) {
                ex.printStackTrace(); 
            }
            conn.getOutputStream().write("</div></body></html>");   
            return null;            
        }
        

        StringBuilder criteria = new StringBuilder();
        criteria.append(h(1, "Search Log Files"));
        
        if (action == null) {
            criteria.append("<form name='criteria' method='post'>");
            
            criteria.append(h(2, "Log type"));
            String[] values = { "requests", "exceptions", "security" };
            criteria.append(select("logName", values, logName, "onchange='document.criteria.submit()'"));

            
            
            if (logName.length() > 0) {
                File logRoot = new File(WareHouse.LOG_ROOT);
                File[] logFiles = logRoot.listFiles();
                Arrays.sort(logFiles, new FileNameComparator());
                Arrays.sort(logFiles, Collections.reverseOrder());
                
                
                for (int i=logFiles.length-1; i>=0; i--) {
                    
                    String firstLogName = logFiles[i].getName();
                    if (firstLogName.contains(logName)) {
                        try {
                            startDate = firstLogName.substring(0, firstLogName.indexOf('.'));
    
                            long time = parseLogFileDate(endDate);
                            Calendar cal = new GregorianCalendar();
                            cal.setTimeInMillis(time);
                            
                            List<String> dates = new LinkedList<String>();

                            while (!startDate.equals(endDate)) {
                                dates.add(endDate);
                                cal.add(Calendar.DATE, -1);
                                endDate = formatLogFileDate(cal.getTime().getTime());
                            }
                            
                            criteria.append(h(2, "Start date"));                            
                            criteria.append(select("startDate", dates, startDate));
                            
                            
                            criteria.append(h(2, "Search within log"));
                            List<String> headers = WareHouse.getLogHeaders(logName);
                            criteria.append(hiddenfield("numColumns", String.valueOf(headers.size())));
                            int colIdx = 0;
                            for (String header : headers) {
                                String searchString = formData.getFirst(header);
                                criteria.append(columnSearchOptions(colIdx, header, searchString));
                                colIdx++;
                            }
                        } 
                        catch (Exception ex) {
                            ex.printStackTrace();
                        }
                        
                        break;
                    }
                }        
    
                criteria.append(br() + submitbutton("action", "Search"));
            }
            criteria.append("</form>");            
        }
                
        TemplatedPage page = new SiteTemplatedPage();
        page.init(conn);
        page.setTitle("Search Logs - WikiWebServer.org");
        page.addResourceRoot(resourcePath);
        page.addCSSLink(css);
        page.append(criteria.toString());

        return page;
    }
    
    private String columnSearchOptions(int columnIndex, String header, String search) {
        String exact = "";
        
        if (search == null) search = "";
        else exact = " checked";
        
        return "<div class='headerOption'>" +
		       "<div class='headerName'>" + header + "</div>" +
		       "<div class='headerSearch'>" +
		       "<input type='text' name='String_" + columnIndex + "' value='" + search + "'>" +
		       "</div><div class='headerExact'>" +
		       "<input type='checkbox' name='Type_" + columnIndex + "_Exact'" + exact + ">" +
		       "&nbsp;&nbsp;Exact match</div><div class='headerShow'>" +
		       "<input type='checkbox' name='Show_" + columnIndex + "' checked>" +
		       "&nbsp;&nbsp;Show column</div></div>";
    }
    
    
    private String formatLogFileDate(long time) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
        return formatter.format(time);            
    }
    
    private long parseLogFileDate(String s) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
        try {
            return formatter.parse(s).getTime();            
        } catch (ParseException ex) {
            return -1;
        }
    }    
}
