package page.tools.stats;

import java.util.Map;
import java.util.TreeSet;

import org.wikiwebserver.core.Privilege;
import org.wikiwebserver.core.WareHouse;
import org.wikiwebserver.core.WikiMap;
import org.wikiwebserver.handler.http.HTTPException;
import org.wikiwebserver.handler.http.interfaces.HTTPResponder;
import org.wikiwebserver.html.HTMLHelper;
import org.wikiwebserver.html.HTMLHelper.ContainerType;

import page.config.SiteTemplatedPage;
import org.wikiwebserver.util.IPToCountry;
import org.wikiwebserver.util.comparator.DescendingEntryValueComparator;
import org.wikiwebserver.util.comparator.EntryKeyComparator;

import page.tools.entity.Browser;
import page.tools.entity.NodeData;
import page.tools.entity.ProtectedStorable;
import page.tools.entity.User;
import static org.wikiwebserver.html.HTMLHelper.*;

public class NodeInfo extends SiteTemplatedPage implements HTTPResponder {
    
    private String resourcePath = "/templates/default/browserinfo/";      
    
    private long getLongStat(NodeData b, String key) {
        long value = 0;
        Number valueObj = (Number) b.get(key);
        if (valueObj != null) value = valueObj.longValue(); 
        return value;
    }
    
    private String getStringStat(NodeData b, String key) {
        String value = "Unknown";
        Object valueObj = b.get(key);
        if (valueObj != null) value = valueObj.toString();
        return value;
    }    
	
    public void generate() throws HTTPException {    
        
        addResourceRoot(resourcePath);
        addCSSLink("browserinfo.css");        
        
        String nodeID = null;
        User operator = getUser();    
             

        if (getFormData() != null) {
            nodeID = getFormData().getFirst("nodeID");
            String command = getFormData().getFirst("cmd");
            if (command != null) {
                if (operator != null && operator.getPrivilege().isAbove(Privilege.USER)) {
                    NodeData nodeData = NodeData.getNodeDataById(nodeID);                    
                    if (command.equals("delete")) {
                        nodeData.clear();
                        String url = WareHouse.getUrlPathForClass(page.tools.stats.NodeList.class);
                        throw new HTTPException(302, "Node deleted", url);
                    }
                    else if (command.equals("block")) {
                        nodeData.setBlocked(true);
                    }
                    else if (command.equals("unblock")) {
                        nodeData.setBlocked(false);
                    }                    
                }
            }
        }
        
        NodeData nodeData = null;
        if (nodeID != null) {
            nodeData = NodeData.getNodeDataById(nodeID);
            setTitle("Node Information - WikiWebServer.org");
            append(h(1, "Node Information") +
                   nodeImage(NodeData.getNodeDataById(nodeID)) +
                   p("A node is a Java application that communicates with WikiWebServer")); 
        }
        
        if (nodeData == null) {
            throw new HTTPException(404, "Entity not found");
        }
        
        append(getDetails(nodeData));
        
        if (operator != null && operator.getPrivilege().isAbove(Privilege.USER)) {
            String blockAction = nodeData.isBlocked() ? "unblock" : "block";
            append(form(hiddenfield("nodeID", nodeID) +
                                     submitbutton("cmd", "delete") +
                                     submitbutton("cmd", blockAction)));
            
        }        
    }
    
    private String divInfoItem(String label, String content) {
        return div(HTMLHelper.ContainerType.CLASS, "label", null, label) +
               div(HTMLHelper.ContainerType.CLASS, "value", null, content);
    }
    
    public String getDetails(NodeData nodeData) {
    
        StringBuilder details = new StringBuilder();
        
        long firstAccess = getLongStat(nodeData, "createdTime");
        long lastAccess = getLongStat(nodeData, "lastRequestTime");
        
        String lastIP = (String) getStringStat(nodeData, "lastRequestAddress");
        String lastCountry = (String) getStringStat(nodeData, "lastRequestCountry");
        if (lastCountry == null) {
            lastCountry = IPToCountry.getCountryName(lastIP);
        }
        
        String numRequests = formatNumber(getLongStat(nodeData, "numRequests"));        
        String numTasksAssigned = formatNumber(getLongStat(nodeData, "numTasksAssigned"));

        String bytesUploaded = WareHouse.formatSize(getLongStat(nodeData, "bytesUploaded"));
        String bytesDownloaded = WareHouse.formatSize(getLongStat(nodeData, "bytesDownloaded"));
        
        details.append(divInfoItem("Name:", nodeData.getName()));        
        details.append(divInfoItem("Identity:", nodeData.getId()));
        details.append(divInfoItem("Password protected:", String.valueOf(nodeData.requiresPassword())));
        
        details.append(divInfoItem("First access:", WareHouse.formatStandardDate(firstAccess)));
        details.append(divInfoItem("Last access:", WareHouse.formatStandardDate(lastAccess)));
        details.append(divInfoItem("Last IP address:", lastIP));
        details.append(divInfoItem("Last IP country:", lastCountry));
        details.append(divInfoItem("Num requests:", numRequests));        
        details.append(divInfoItem("Num tasks assigned:", numTasksAssigned));
        details.append(divInfoItem("Data from node:", bytesUploaded));
        details.append(divInfoItem("Data to node:", bytesDownloaded));  
        details.append(divInfoItem("Blocked:", String.valueOf(nodeData.isBlocked())));        
        
        User owner = nodeData.getOwner();    
        details.append(cleardiv());
        details.append(h(2, "Registered Owner"));
        if (owner != null) {
            String ownerLink = getLink(owner);
            details.append(p(a(ownerLink, avatarImage(owner)) + 
                             a(ownerLink, emailImage(owner))));
            details.append(p("Make sure you are signed in when launching MyDiskNode to update this field."));
        }
        else details.append(p("Unknown or not registered"));
        details.append(cleardiv());        
        
        String userAgent = getStringStat(nodeData, "userAgent");        
        details.append(cleardiv());
        details.append(h(2, "User Agent") + p(userAgent) + cleardiv());
        
        String osName = getStringStat(nodeData, "osName");        
        details.append(cleardiv());
        details.append(h(2, "Operating System") + p(osName) + cleardiv());        
            
        String addressList = getIPAddresses(nodeData);
        details.append(h(2, "IP Addresses") + 
               p("IP addresses used by node sorted by number of requests.") +
               div("ipAddresses", addressList) + cleardiv());
        
        String visitTimes = getVisitTimes(nodeData, 100);
        details.append(h(2, "Start times:") + 
               p("Start times and entrance urls sorted by time.") +
               div("visitTimes", visitTimes) + cleardiv());          

        String type = "Node%20ID";
        String link = WareHouse.getUrlPathForClass(page.tools.management.LogViewer.class)
                    + "?logName=requests&" + type + "=" + nodeData.getId();
        details.append(p("[ <a href='" + link + "'>Search log files</a> ]"));               
        
        return details.toString();
    }
    
    private String getIPAddresses(NodeData b) {
        StringBuilder body = new StringBuilder();
        
        TreeSet<Map.Entry<String, Object>> sortedSet 
            = new TreeSet<Map.Entry<String, Object>>(
                    new DescendingEntryValueComparator<String, Object>());

        Map<String, Object> dataStore = (WikiMap) b.get("RequestAddresses");
        if (dataStore == null) return p("No data");
        for (Map.Entry<String, Object> entry : dataStore.entrySet()) {
            sortedSet.add(entry);
        }
        
        
        for (Map.Entry<String, Object> entry: sortedSet) {
            Object value = entry.getValue();
            String url = entry.getKey();
            long count = 0;
            if (value instanceof Number) {
                count = ((Number) value).longValue();
            }        
            body.append(div(ContainerType.CLASS, "accessCount", null, formatNumber(count)));
            body.append(url);
            body.append(br());          
        }
        
        return body.toString();
    }   
    
    private String getVisitTimes(ProtectedStorable b, int limit) {
        StringBuilder body = new StringBuilder();
        
        TreeSet<Map.Entry<String, Object>> sortedSet 
            = new TreeSet<Map.Entry<String, Object>>(
                    new EntryKeyComparator<String, Object>());

        Map<String, Object> dataStore = (WikiMap) b.get("VisitTimes");
        if (dataStore == null) return p("No data");
        for (Map.Entry<String, Object> entry : dataStore.entrySet()) {
            sortedSet.add(entry);
        }
        
        int c = 0;
        for (Map.Entry<String, Object> entry: sortedSet) {
            String timeString = (String) entry.getKey();
            String url = ((String)entry.getValue()).replace("&", "&amp;");
            
            String time = WareHouse.formatStandardDate(Long.parseLong(timeString));
                       
            body.append(div(ContainerType.CLASS, "timeBlock", null, time));
            String printableUrl = url;
            if (url.length() > 70) {
                printableUrl = url.substring(0, 35) + " ... " 
                             + url.substring(url.length()-35, url.length());
            }
            body.append(a(url, WareHouse.escapeHTMLEntities(printableUrl)));
            body.append(br());
            if (limit != -1 && ++c > limit) break;            
        } 
        if (c==0) body.append("No data");
        
        return p(body.toString());
    }        
    
    private String getLink(ProtectedStorable item) {
        if (item instanceof User) {
            return WareHouse.getUrlPathForClass(BrowserInfo.class) + "?userID=" + item.getId();      
        } else if (item instanceof Browser) {
            return WareHouse.getUrlPathForClass(BrowserInfo.class) + "?browserID=" + item.getId();  
        }
        return "#";
    }    
    
    private String formatNumber(Object obj) {
        if (obj instanceof Number) {
            return WareHouse.formatNumber(((Number)obj).longValue());
        }
        return "unknown";
    }

    public String getCacheKey() {
        return null;
    }
}
