/**
 * JOnAS
 * Copyright (C) 2010 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.audit.internal.logger;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Date;
import java.util.TreeMap;

import javax.xml.namespace.QName;

import org.ow2.util.auditreport.impl.GenericAuditReport;
import org.ow2.util.auditreport.impl.InvocationAuditReport;
import org.ow2.util.auditreport.impl.JaxwsAuditReport;
import org.ow2.util.auditreport.impl.JNDIAuditReport;
import org.ow2.util.auditreport.impl.WebInvocationAuditReport;

/**
 * Report Node used to represent the graph of the request call.
 * @author Florent Benoit
 */
public class NodeReport implements Serializable {

    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = -4591802637765669254L;

    /**
     * Third parameter.
     */
    private static final int THIRD = 3;

    /**
     * Node's ID.
     */
    private String id;

    /**
     * Node's description.
     */
    private String desc;

    /**
     * Node is a leaf ?
     */
    private boolean leaf = true;

    /**
     * Node is missing its parent.
     */
    private boolean missingParent = false;

    /**
     * Date.
     */
    private Date date = null;

    /**
     * Type.
     */
    private NodeReportType type = NodeReportType.UNKNOWN;

    /**
     * Name.
     */
    private String name = null;

    /**
     * Application.
     */
    private String application = null;

    /**
     * Details.
     */
    private String details = null;

    /**
     * Execution time.
     */
    private double execTime = 0L;

    /**
     * Tree that represents every childs.
     */
    private TreeMap<Integer, NodeReport> treeMap = null;

    /**
     * Constructor with the given id.
     * @param id the id of this node
     * @param leaf true if the node is a leaf, else false
     */
    public NodeReport(final String id, final boolean leaf) {
        this.id = id;
        this.leaf = leaf;
        this.treeMap = new TreeMap<Integer, NodeReport>();

    }

    /**
     * @return the childs.
     */
    public TreeMap<Integer, NodeReport> getTreeMap() {
        return treeMap;
    }

    /**
     * @return true if the given node is a leaf
     */
    public boolean isLeaf() {
        return leaf;
    }


    /**
     * @return node's name
     */
    public String getID() {
        return id;
    }

    /**
     * @return true if the parent node is missing and then this node was added at the root level.
     */
    public boolean isMissingParent() {
        return missingParent;
    }

    /**
     * Sets the flag for this node.
     * @param missingParent true/false
     */
    public void setMissingParent(final boolean missingParent) {
        this.missingParent = missingParent;
    }

    /**
     * @return description for this node
     */
    public String getDesc() {
        return desc;
    }

    /**
     * @return type for this node.
     */
    public NodeReportType getType() {
        return type;
    }

    /**
     * Sets the type for this node.
     * @param type the given type
     */
    public void setType(final NodeReportType type) {
        this.type = type;
    }

    /**
     * Sets the given report.
     * @param report the report on which data will be extracted
     */
    public void setReport(final GenericAuditReport report) {

        // Store date of the report
        this.date = new Date(report.getRequestTimeStamp());
        String txt = "";
        // build text depending of the type of the report
        if (report instanceof InvocationAuditReport) {
            InvocationAuditReport invocationAuditReport = (InvocationAuditReport) report;
            String target = invocationAuditReport.getTarget();

            if (target.startsWith("/easybeans/")) {
                target = target.substring("/easybeans/".length());
            }
            // Keep Bean Name and method name
            String[] parts = target.split("/");
            if (parts.length >= THIRD) {
                this.name = parts[2] + "::" + parts[THIRD];
                this.application = parts[0] + "::" + parts[1];
                txt = parts[2] + "::" + parts[THIRD] + "\n" + parts[0] + "::" + parts[1] + "\n" + txt;
            }
            this.execTime = invocationAuditReport.getRequestDuration();
            if (invocationAuditReport.getBusinessMethod().contains("@Local")) {
                setType(NodeReportType.EJB_LOCAL);
            } else if (invocationAuditReport.getBusinessMethod().contains("@Remote")) {
                setType(NodeReportType.EJB_REMOTE);
            } else {
                setType(NodeReportType.EJB);
            }
        } else if (report instanceof WebInvocationAuditReport) {

            // HTTP report
            WebInvocationAuditReport webInvocationAuditReport = (WebInvocationAuditReport) report;
            String target = webInvocationAuditReport.getTarget();
            if (target.startsWith("/webcontainer/")) {
                target = target.substring("/webcontainer/".length());
            }

            String[] parts = target.split("/");
            if (parts.length >= THIRD) {
                this.name = report.getBeanName();
                if ("root".equals(parts[1])) {
                    this.application = "standalone::" + parts[0];
                } else {
                    this.application = parts[0] + "::" + parts[1];
                }
                txt = parts[0] + "::" + parts[1] + "\n" + txt;
            }

            txt = report.getBeanName() + "\n" + txt;
            setType(NodeReportType.HTTP_GET);
            this.execTime = webInvocationAuditReport.getRequestDuration();
        } else if (report instanceof JNDIAuditReport) {
            // JNDI Report
            JNDIAuditReport jndiAuditReport = (JNDIAuditReport) report;
            txt = jndiAuditReport.getBusinessMethod();
            if (jndiAuditReport.getContextParameters() == null) {
                txt += "()";
            } else {
                txt += "(" + Arrays.asList(jndiAuditReport.getContextParameters()) + ")";
            }
            this.name = txt;
            this.execTime = jndiAuditReport.getRequestDuration();
            setType(NodeReportType.JNDI);

        } else if (report instanceof JaxwsAuditReport) {

            // ---------------------------------------
            // Jaxws Report
            // ---------------------------------------

            JaxwsAuditReport auditReport = (JaxwsAuditReport) report;
            QName service = QName.valueOf(auditReport.getServiceQName());
            QName port = QName.valueOf(auditReport.getPortQName());
            QName operation = QName.valueOf(auditReport.getOperationQName());

            StringBuilder sb = new StringBuilder();
            sb.append("Service QName: ");
            sb.append(service);
            sb.append('\n');
            sb.append("Port QName: ");
            sb.append(port);
            sb.append('\n');
            sb.append("Operation QName: ");
            sb.append(operation);

            txt = sb.toString();
            this.name = service.getLocalPart() + "/"
                    + port.getLocalPart() + ":"
                    + operation.getLocalPart();

            this.execTime = auditReport.getEndProcessingTimestamp() - auditReport.getStartProcessingTimestamp();

            setType(NodeReportType.JAXWS_ENDPOINT);
        } else {
            txt = report.getBeanName();
        }

        this.desc = txt;

    }

    /**
     * @return execution time
     */
    public double getExecTime() {
        return execTime;
    }

    /**
     * Sets the details.
     * @param details the given details
     */
    public void setDetails(final String details) {
        this.details = details;
    }

    /**
     * @return details of this node
     */
    public String getDetails() {
        return details;
    }

    /**
     * @return name of this node
     */
    public String getName() {
        return name;
    }

    /**
     * @return application name
     */
    public String getApplication() {
        return application;
    }

    /**
     * @return date of this node
     */
    public Date getDate() {
        return date;
    }

    /**
     * @return string representation
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("[");
        sb.append(id);
        if (treeMap != null && treeMap.size() > 0) {
            sb.append(treeMap.toString());
        }
        if (desc != null) {
            sb.append(", desc=");
            sb.append(desc);
        }
        sb.append("]");
        return sb.toString();
    }
}
