/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package backupAndRecovery;

import java.net.InetAddress;
import org.dom4j.*;
import org.dom4j.Element;

/**
 * This class represents single computer capable of storing backup files.
 *
 * Information about usage of host resources is stored within this class
 * together with IP address of host and its type.
 *
 * Any information about usage of disk space(e.g repository size,
 * used repository) is in MB.
 *
 * Type of host is used to distiguish desktops and notebooks. Notebooks can
 * be further divided into two categories - ordinary notebooks that are
 * often disconnected from network and carried around and static notebooks
 * which are used more like desktop computers, however, employees can use
 * them to work from home.
 * 
 * @author Marcel Duris
 */
public class RepoHost implements Comparable<RepoHost> {

    public InetAddress hostAddress;
    public int hostMask;
    public int hostPort;
    public int repoSize;
    public int repoUsed;
    public int hostType;
    public int backupsStored;
    public static final int UNKNOWN = 0;
    public static final int NOTEBOOK = 1;
    public static final int DESKTOP = 2;

    /**
     * Creates empty container with default data.
     */
    public RepoHost() {
        hostAddress = null;
        repoSize = 0;
        repoUsed = 0;
        backupsStored = 0;
        hostType = UNKNOWN;
    }

    /**
     * Loads information about host from xml file.
     * 
     * @param xmlRepoHostData String representation of xml file.
     * @throws IllegalArgumentException if xml data is malformed or invalid
     */
    public RepoHost(String xmlRepoHostData) throws IllegalArgumentException {
        try {
            Document repoHostData = DocumentHelper.parseText(xmlRepoHostData.trim());
            Element root = repoHostData.getRootElement();

            hostAddress = InetAddress.getByName(root.element("hostAddress").getText());
            hostMask = Integer.parseInt(root.element("hostMask").getTextTrim());
            hostPort = Integer.parseInt(root.element("hostPort").getTextTrim());
            repoSize = Integer.parseInt(root.element("repoSize").getTextTrim());
            repoUsed = Integer.parseInt(root.element("repoUsed").getTextTrim());
            backupsStored = Integer.parseInt(root.element("backupsStored").getTextTrim());
            hostType = getHostType(root.element("hostType").getTextTrim());

        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("Unable to parse repository host XML data.");
        }
    }

    /**
     * Builds container using passed arguments.
     *
     * @param address   IP address of host
     * @param maskLength Length of subnet mask
     * @param size  Size of repository in MB.
     * @param used  Used portion of repository storage in MB.
     * @param stored    Number of stored backups.
     * @param type  Type of host.
     */
    public RepoHost(InetAddress address, int maskLength, int port, int size, int used, int stored, int type) {
        hostAddress = address;
        hostMask = maskLength;
        hostPort = port;
        repoSize = size;
        repoUsed = used;
        backupsStored = stored;
        hostType = type;
    }

    /**
     * Returns IP address of host.
     *
     * @return InetAddress of host.
     */
    public InetAddress getAddress() {
        return hostAddress;
    }

    /**
     * Returns subnet mask of host as array of bytes. Byte 0 is for first octet
     * of address byte 1 for second and so on.
     *
     * @return array of bytes representing subnet mask.
     */
    public byte[] getSubnetMask() {
        //if (hostAddress.)
        //TODO: distinguish ipv4 and ipv6
        byte[] mask = new byte[4];
        int octet = 0;
        int maskl = hostMask;
        //TODO: distinguish ipv4 and ipv6
        maskl = Math.min(32, maskl);
        while (maskl > 8) {
            mask[octet] = (byte) 255;
            maskl -= 8;
            octet++;
        }
        byte lastOctet = 0;
        int bit = 128;
        while (maskl > 0) {
            lastOctet += bit;
            bit = bit >> 1;
            maskl--;
        }
        mask[octet] = lastOctet;
        return mask;
    }

    /**
     * Size of repository.
     *
     * @return Size of repository
     */
    public int getRepoSize() {
        return repoSize;
    }

    /**
     * Returns size of used repository
     * 
     * @return Size of used repository
     */
    public int getRepoUsed() {
        return repoUsed;
    }

    /**
     * Number of backups stored.
     * 
     * @return Number of backups stored.
     */
    public int getBackupsStored() {
        return backupsStored;
    }

    /**
     * Returns type of host. Used to determine probality of host going down.
     * 
     * @return int representing type of host
     */
    public int getHostType() {
        return hostType;
    }

    /**
     * Convers inner data of container into xml representation.
     *
     * @return String representing container data.
     */
    public String getXml() {
        Document document = DocumentHelper.createDocument();
        Element root = document.addElement("repoHost");
        
        root.addElement("hostAddress").addText(hostAddress.getHostAddress());
        root.addElement("hostMask").addText(Integer.toString(hostMask));
        root.addElement("hostPort").addText(Integer.toString(hostPort));
        root.addElement("repoSize").addText(Integer.toString(repoSize));
        root.addElement("repoUsed").addText(Integer.toString(repoUsed));
        root.addElement("backupsStored").addText(Integer.toString(backupsStored));
        root.addElement("hostType").addText(getHostType(hostType));
        
        return document.asXML();
    }

    /**
     * Function convers between string representation of host
     * type and its decimal variant.
     *
     * @return type of host
     */
    private int getHostType(String s) {
        if (s.compareToIgnoreCase("DESKTOP") == 0) {
            return DESKTOP;
        }
        if (s.compareToIgnoreCase("NOTEBOOK") == 0) {
            return NOTEBOOK;
        }
        return UNKNOWN;
    }

    /**
     * Returns description of type of host to given decimal representation.
     * @param t type of host
     * @return description
     */
    private String getHostType(int t) {
        switch (t) {
            case 0:
                return "UNKNOWN";
            case 1:
                return "DESKTOP";
            case 2:
                return "NOTEBOOK";
        }
        return "UNKNOWN";
    }

    /**
     * Returns weight or desirability of host to store backup. This method is
     * used to compare hosts and to find the best nodes for our backup.
     *
     * Higher number means better host.
     * 
     * @return weight of host
     */
    public double getWeight(){
        double weight = 1000000 * getRepoSize() * (hostType + 1) / (getRepoUsed() * getBackupsStored() + 1);
        return weight;
    }

    /**
     * Returns XML representation of host as string.
     *
     * @return xml string
     */
    @Override
    public String toString() {
        return getXml();
    }

    public int compareTo(RepoHost o) {
        if (getWeight() > o.getWeight()) {
            return 1;
        }
        if (getWeight() < o.getWeight()) {
            return -1;
        }
        return 0;
    }
}
