package network;

import backupAndRecovery.*;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

/**
 * Request for fragments used during recovery process. There are two basic types
 * of request. Full request contains exact metadata of file to be recovered.
 * Filename request contains only filename.
 *
 * All messages are expected to be in XML format.
 * 
 * @author Marcel Duris
 */
public class ProtocolRecovery {

    public static int UNKNOWN = 0;
    public static int FULL_REQUEST = 1;
    public static int FILENAME_REQUEST = 2;
    public static final String POLL = "fragmentPoll";

    /**
     * Builds partial request containing only filename. Hosts storing matching
     * backups should respond with list of files related to that backup they
     * are storing.
     * 
     * @param filename of file to be found
     * @return byte array to be placed directly into UDP packet
     */
    public static byte[] buildRequest(String filename) {
        Document request = DocumentHelper.createDocument();
        request.addElement(POLL).addElement("filename").addText(filename);

        return request.asXML().getBytes();
    }

    /**
     * Builds full request for exact backup identified by full backup metadata.
     * Hosts storing matching backups should respond with list of files related
     * to that backup they are storing.
     *
     * @param meta Backup metadata container of requested backup
     * @return byte array to be placed directly into UDP packet
     */
    public static byte[] buildRequest(BackupMetaContainer meta) {
        Document request = DocumentHelper.createDocument();
        Element reqRoot = request.addElement(POLL);
        reqRoot.add(meta.getXmlDocument().getRootElement());

        return request.asXML().getBytes();
    }

    /**
     * Seeks request for name of file.
     *
     * @param msg Xml request received over network
     * @return name of file
     */
    public static String getFilename(Document msg) {
        Element filename = msg.getRootElement().element("filename");
        if (filename != null) {
            return filename.getTextTrim();
        }

        BackupMetaContainer meta = new BackupMetaContainer(msg);
        return meta.getFilename();
    }

    /**
     * Attempts to convert request into backup metadata container.
     * 
     * @param msg xml message received over network.
     * @return BackupMetaContainer corresponding to passed Xml document
     */
    public static BackupMetaContainer getMeta(Document msg) {
        try {
            return new BackupMetaContainer((Element) msg.getRootElement().elements().get(0));
        } catch (IllegalArgumentException ex){
            return null;
        } 
    }

    /**
     * Method used to distinguis different types of request. Request can be
     * either full request indicating exact information abou backup or partial
     * which is request only for filename.
     * 
     * @param msg Xml message received over network
     * @return type of request
     */
    public static int getRequestType(Document msg) {
        if (msg.getRootElement().element("filename") != null) {
            return FILENAME_REQUEST;
        }

        try {
            new BackupMetaContainer(msg.getRootElement().element(BackupMetaContainer.ROOT));
            return FULL_REQUEST;
        } catch (IllegalArgumentException ex) {
            return UNKNOWN;
        }
    }

    /**
     * This method build reponse to any request, either full or partial.
     * Response contains metadata of backup and list of files hosts stores.
     * Both of this information must be passed as arguments.
     *
     * @param meta metadata of backup
     * @param files array of stored files belonging to backup
     * @return byte array to be placed directly into UDP packet
     */
    public static byte[] buildResponse(BackupMetaContainer meta, String[] files) {
        Document response = DocumentHelper.createDocument();
        Element root = response.addElement("fragmentResponse");
        root.add(meta.getXmlDocument().getRootElement());
        for (String file : files) {
            root.addElement("file").setText(file);
        }

        return response.asXML().getBytes();
    }

    /**
     * Parses response. Returns hashtable which facilitates searching for files
     * belonging to known backup. Any required information can be loaded from
     * this hashtable. This HashMap represents state of single network host.
     *
     * @param response to request in xml format received over network
     * @return HashMap storing backup metadata and list of files
     */
    public static HashMap<BackupMetaContainer, String[]> parseResponse(String response) {
        Document docResp = null;
        try {
            docResp = DocumentHelper.parseText(response);
        } catch (DocumentException ex) {
            Logger.getLogger(ProtocolRecovery.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }

        BackupMetaContainer meta = new BackupMetaContainer(docResp.getRootElement().element(BackupMetaContainer.ROOT));

        List<Element> list = docResp.getRootElement().elements("file");
        String[] files = new String[list.size()];
        for (int i = 0; i < list.size(); i++) {
            files[i] = list.get(i).getTextTrim();
        }

        HashMap<BackupMetaContainer, String[]> result = new HashMap<BackupMetaContainer, String[]>();
        result.put(meta, files);

        return result;
    }
}
