/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.layoutopt.uix;

import org.w3c.dom.Node;

import java.util.List;
import java.util.ArrayList;

import com.android.layoutopt.uix.groovy.LayoutAnalysisCategory;

/**
 * Contains the results of a layout analysis. Instances of this class are
 * generated by {@link com.android.layoutopt.uix.LayoutAnalyzer}.
 *
 * @see com.android.layoutopt.uix.LayoutAnalyzer
 */
public class LayoutAnalysis {
    /**
     * Default layout analysis used to describe a problem with the
     * analysis process.
     */
    static final LayoutAnalysis ERROR = new LayoutAnalysis("");
    static {
        ERROR.mAnalyzed = false;
        ERROR.addIssue("The layout could not be analyzed. Check if you specified a valid "
                + "XML layout, if the specified file exists, etc.");
    }

    private final List<Issue> mIssues = new ArrayList<Issue>();
    private String mName;
    private boolean mAnalyzed;
    private Node mNode;

    /**
     * Creates a new analysis. An analysis is always considered invalid by default.
     *
     * @see #validate()
     * @see #isValid() 
     */
    LayoutAnalysis(String name) {
        mName = name;
    }

    /**
     * Returns the name of this analysis.
     */
    public String getName() {
        return mName;
    }

    void setName(String name) {
        mName = name;
    }
    
    /**
     * Adds an issue to the layout analysis.
     *
     * @param issue The issue to add.
     */
    public void addIssue(Issue issue) {
        mIssues.add(issue);
    }

    /**
     * Adds an issue to the layout analysis.
     *
     * @param description Description of the issue.
     */
    public void addIssue(String description) {
        mIssues.add(new Issue(mNode, description));
    }

    /**
     * Adds an issue to the layout analysis.
     *
     * @param node The node containing the issue.
     * @param description Description of the issue.
     */
    public void addIssue(Node node, String description) {
        mIssues.add(new Issue(node, description));
    }

    /**
     * Returns the list of issues found during the analysis.
     *
     * @return A non-null array of {@link com.android.layoutopt.uix.LayoutAnalysis.Issue}.
     */
    public Issue[] getIssues() {
        return mIssues.toArray(new Issue[mIssues.size()]);
    }

    /**
     * Indicates whether the layout was analyzed. If this method returns false,
     * a probleme occured during the analysis (missing file, invalid document, etc.)
     *
     * @return True if the layout was analyzed, false otherwise.
     */
    public boolean isValid() {
        return mAnalyzed;
    }

    /**
     * Validates the analysis. This must be call before this analysis can
     * be considered valid. Calling this method resets the current node to null.
     * 
     * @see #setCurrentNode(org.w3c.dom.Node) 
     */
    void validate() {
        mAnalyzed = true;
        mNode = null;
    }

    /**
     * Sets the current node to be automatically added to created issues.
     * 
     * @param node An XML node.
     */
    void setCurrentNode(Node node) {
        mNode = node;
    }

    /**
     * Represents an issue discovered during the analysis process.
     * An issue provides a human-readable description as well as optional solutions.
     */
    public static class Issue {
        private final String mDescription;
        private final Node mNode;

        /**
         * Creates a new issue with the specified description.
         * 
         * @param description The description of the issue.
         */
        public Issue(String description) {
            mNode = null;
            if (description == null) {
                throw new IllegalArgumentException("The description must be non-null");
            }
            mDescription = description;
        }

        /**
         * Creates a new issue with the specified description.
         * 
         * @param node The node in which the issue was found.
         * @param description The description of the issue.
         */
        public Issue(Node node, String description) {
            mNode = node;
            if (description == null) {
                throw new IllegalArgumentException("The description must be non-null");
            }
            mDescription = description;
        }

        /**
         * Describes this issue to the user.
         *
         * @return A String describing the issue, always non-null.
         */
        public String getDescription() {
            return mDescription;
        }


        /**
         * Returns the start line of this node.
         *
         * @return The start line or -1 if the line is unknown.
         */
        public int getStartLine() {
            return LayoutAnalysisCategory.getStartLine(mNode);
        }

        /**
         * Returns the end line of this node.
         *
         * @return The end line or -1 if the line is unknown.
         */
        public int getEndLine() {
            return LayoutAnalysisCategory.getEndLine(mNode);
        }
    }
}
