/*
 * Copyright (c) 2009 Srikalyan Swayampakula.. All rights reserved.
 * 
 *   Author : Srikalyan Swayampakula. .
 *   Name of the File : WADLParser.java .
 *   Created on : Nov 22, 2009 at 5:30:47 PM .
 * 
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 * 
 *  1. Redistributions of source code must retain the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer.
 *  2. Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *  3. Neither the name of the University of Georgia nor the names
 *     of its contributors may be used to endorse or promote
 *     products derived from this software without specific prior
 *     written permission.
 * 
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package lsdis;

import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.xml.sax.Attributes;

/**
 *
 * @author Srikalyan Swayampakula.
 */
public class WADLParser
{

    static Document currentDocument;
    static Element rootElement;
    static Namespace wadlNamespace;
    public Application application;

    public WADLParser(URL fileURL) throws Exception
    {
        currentDocument = generateDocumentation(fileURL);
        rootElement = currentDocument.getRootElement();
        wadlNamespace = rootElement.getNamespace();
        application = getApplication();
    }
    
    public Application getApplicationOfWADL()
    {
        return application;
    }

    private Document generateDocumentation(URL fileName) throws Exception
    {
        Document doc = null;
        SAXBuilder builder = new SAXBuilder();
        //doc = builder.build(new File(fileName.toURI()));
        doc = builder.build(fileName);
        return doc;
    }

    private Application getApplication() throws Exception
    {
        List<Doc> docs = getDocs(rootElement);
        List<Resources> resources = getResources(rootElement);
        Grammar grammar = getGrammar(rootElement);
        return new Application(resources, docs, grammar, null);
    }

    /***
     * used to get docs list for an element eg. applicaiton.
     * @param e
     * @return
     */
    private List<Doc> getDocs(Element e)
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = null;
        List<Element> xmlDoc = e.getChildren(WADLConstant.DOC, wadlNamespace);
        if (xmlDoc != null)
        {
            docs = new ArrayList<Doc>();
            for (Element e1 : xmlDoc)
            {
                Doc tempDoc = getDoc(e1);
                if (tempDoc != null)
                {
                    docs.add(tempDoc);
                }
            }
        }
        return docs;
    }

    private Doc getDoc(Element e)
    {
        if (e == null)
        {
            return null;
        }
        String title = e.getAttributeValue(WADLConstant.DOC_TITLE);

        String lang = e.getAttributeValue(WADLConstant.DOC_LANG);
        if (lang == null)
        {
            List<Attribute> temp = e.getAttributes();
            for (Attribute temp1 : temp)
            {
                if((temp1.getName()).equals(WADLConstant.DOC_LANG))
                    lang=temp1.getValue();
            }
        }
        String innerText = e.getText();
        return new Doc(title, lang, innerText);
    }

    private Grammar getGrammar(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        List<Include> includes = getIncludes(e);
        return new Grammar(docs, includes);
    }

    private List<Include> getIncludes(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Include> includes = null;
        List<Element> xmlIncludes = e.getChildren(WADLConstant.INCLUDE, wadlNamespace);
        if (xmlIncludes != null)
        {
            includes = new ArrayList<Include>();
            for (Element xmlInclude : xmlIncludes)
            {
                Include tempInclude = getInclude(xmlInclude);
                if (tempInclude != null)
                {
                    includes.add(tempInclude);
                }
            }
        }
        return includes;
    }

    private Include getInclude(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        URI href = null;
        String tempHref = e.getAttributeValue(WADLConstant.INCLUDE_HREF);
        if (tempHref != null)
        {
            href = new URI(tempHref);
        }
        return new Include(docs, href);
    }

    private List<Resources> getResources(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }

        List<Resources> resources = null;
        List<Element> xmlResources = e.getChildren(WADLConstant.RESOURCES, wadlNamespace);
        if (xmlResources != null)
        {
            resources = new ArrayList<Resources>();
            for (Element tempResources : xmlResources)
            {
                Resources tempResource = getResourcesInstance(tempResources);
                if (tempResource != null)
                {
                    resources.add(tempResource);
                }
            }
        }

        return resources;

    }

    private Resources getResourcesInstance(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        String tempBase = e.getAttributeValue(WADLConstant.RESOURCES_BASE);//, wadlNamespace);
        URI base = null;
        if (tempBase != null)
        {
            base = new URI(tempBase);
        }
        List<Doc> docs = getDocs(e);
        List<Resource> subResources = getSubResources(e);
        return new Resources(docs, subResources, base);
    }

    private List<Resource> getSubResources(Element e) throws Exception
    {
        if (e == null)
        {
            return null;

        }
        List<Resource> subResources = null;
        List<Element> xmlSubResources = e.getChildren(WADLConstant.RESOURCE, wadlNamespace);
        if (xmlSubResources != null)
        {
            subResources = new ArrayList<Resource>();
            for (Element xmlSubResource : xmlSubResources)
            {
                Resource subResource = getSubResource(xmlSubResource);
                if (subResource != null)
                {
                    subResources.add(subResource);
                }
            }
        }

        return subResources;

    }

    private Resource getSubResource(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        List<Param> params = getParams(e);
        List<Method> methods = getMethods(e);
        List<Resource> resources = getSubResources(e);
        String id = e.getAttributeValue(WADLConstant.RESOURCE_ID);//, wadlNamespace);
        String queryType = e.getAttributeValue(WADLConstant.RESOURCE_QUERY_TYPE);//, wadlNamespace);
        String path = e.getAttributeValue(WADLConstant.RESOURCE_PATH);
        //System.out.println("the path is " + path + " wadlNampeSpace is " + wadlNamespace);
        return new Resource(docs, params, methods, resources, id, queryType, path);
    }

    private List<Param> getParams(Element e) throws Exception
    {
        if (e == null)
        {
            return null;

        }
        List<Param> params = null;
        List<Element> xmlParams = e.getChildren(WADLConstant.PARAM, wadlNamespace);
        if (xmlParams != null)
        {
            params = new ArrayList<Param>();
            for (Element xmlParam : xmlParams)
            {
                Param param = getParam(xmlParam);
                if (param != null)
                {
                    params.add(param);

                }
            }
        }
        return params;
    }

    private Param getParam(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        List<Option> options = getOptions(e);
        Link link = getLink(e.getChild(WADLConstant.LINK, wadlNamespace));
        String tempHref = e.getAttributeValue(WADLConstant.PARAM_HREF);//, wadlNamespace);
        URI href = null;
        if (tempHref != null)
        {
            href = new URI(tempHref);
        }
        String name = e.getAttributeValue(WADLConstant.PARAM_NAME);//, wadlNamespace);
        String style = e.getAttributeValue(WADLConstant.PARAM_STYLE);//, wadlNamespace);
        String id = e.getAttributeValue(WADLConstant.PARAM_ID);//, wadlNamespace);
        String type = e.getAttributeValue(WADLConstant.PARAM_TYPE);//, wadlNamespace);
        String default1 = e.getAttributeValue(WADLConstant.PARAM_DEFAULT);//, wadlNamespace);
        String tempRequired = e.getAttributeValue(WADLConstant.PARAM_REQUIRED);//, wadlNamespace);
        boolean required = false;
        if (tempRequired != null)
        {
            required = Boolean.valueOf(tempRequired);
        }
        String tempRepeating = e.getAttributeValue(WADLConstant.PARAM_REPEATING);//, wadlNamespace);
        boolean repeating = false;
        if (tempRepeating != null)
        {
            repeating = Boolean.valueOf(tempRepeating);
        }
        String fixed = e.getAttributeValue(WADLConstant.PARAM_FIXED);//, wadlNamespace);
        String path = e.getAttributeValue(WADLConstant.PARAM_PATH);//, wadlNamespace);
        return new Param(docs, options, link, href, name, style, id, type, default1, fixed, path,required);
    }

    private List<Option> getOptions(Element e)
    {
        System.out.println(e.getName());
    	if (e == null)
        {
            return null;
        }
        List<Option> options = null;
        List<Element> xmlOptions = e.getChildren(WADLConstant.OPTION, wadlNamespace);
        if (xmlOptions != null)
        {
            options = new ArrayList<Option>();
            for (Element xmlOption : xmlOptions)
            {
                Option option = getOption(xmlOption);
                if (option != null)
                {
                    options.add(option);
                }
            }
        }
        return options;
    }

    private Option getOption(Element e)
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        String value = e.getAttributeValue(WADLConstant.OPTION_VALUE);//, wadlNamespace);
        String mediaType = e.getAttributeValue(WADLConstant.OPTION_MEDIA_TYPE);//, wadlNamespace);
        return new Option(docs, value, mediaType);
    }

    private Link getLink(Element e)
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        String resourceType = e.getAttributeValue(WADLConstant.LINK_RESOURCE_TYPE);//, wadlNamespace);
        String rel = e.getAttributeValue(WADLConstant.LINK_REL);//, wadlNamespace);
        String rev = e.getAttributeValue(WADLConstant.LINK_REV);//, wadlNamespace);
        return new Link(docs, resourceType, rel, rev);
    }

    private List<Method> getMethods(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Method> methods = null;
        List<Element> xmlMethods = e.getChildren(WADLConstant.METHOD, wadlNamespace);
        if (xmlMethods != null)
        {
            methods = new ArrayList<Method>();
            for (Element xmlMethod : xmlMethods)
            {
                Method method = getMethod(xmlMethod);
                if (method != null)
                {
                    methods.add(method);
                }
            }
        }
        return methods;
    }

    private Method getMethod(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        Request request = getRequest(e.getChild(WADLConstant.REQUEST, wadlNamespace));
        List<Response> responses = getResponses(e);
        String id = e.getAttributeValue(WADLConstant.METHOD_ID);//, wadlNamespace);
        String tempHref = e.getAttributeValue(WADLConstant.METHOD_HREF);//, wadlNamespace);
        URI href = null;
        if (tempHref != null)
        {
            href = new URI(tempHref);
        }
        String name = e.getAttributeValue(WADLConstant.METHOD_NAME);//, wadlNamespace);
        return new Method(docs, request, responses, id, name, href);
    }

    private Request getRequest(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        List<Param> params = getParams(e);
        List<Representation> representations = getRepresentations(e);
        return new Request(docs, params, representations);
    }

    private List<Representation> getRepresentations(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Representation> representations = null;
        List<Element> xmlRepresentations = e.getChildren(WADLConstant.REPRESENTATION, wadlNamespace);
        if (xmlRepresentations != null)
        {
            representations = new ArrayList<Representation>();
            for (Element xmlRepresentation : xmlRepresentations)
            {
                Representation representation = getRepresentation(e);
                if (representation != null)
                {
                    representations.add(representation);
                }
            }
        }
        return representations;
    }

    private Representation getRepresentation(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        List<Param> params = getParams(e);
        String id = e.getAttributeValue(WADLConstant.REPRESENTATION_ID);//, wadlNamespace);
        String element = e.getAttributeValue(WADLConstant.REPRESENTATION_ELEMENT);//, wadlNamespace);
        String mediaType = e.getAttributeValue(WADLConstant.REPRESENTATION_MEDIA_TYPE);//, wadlNamespace);
        String tempHref = e.getAttributeValue(WADLConstant.REPRESENTATION_HREF);//, wadlNamespace);
        URI href = null;
        if (tempHref != null)
        {
            href = new URI(tempHref);
        }
        String profile = e.getAttributeValue(WADLConstant.REPRESENTATION_PROFILE);//, wadlNamespace);
        return new Representation(docs, params, id, element, mediaType, href, profile);
    }

    private List<Response> getResponses(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Response> responses = null;
        List<Element> xmlResponses = e.getChildren(WADLConstant.RESPONSE, wadlNamespace);
        if (xmlResponses != null)
        {
            responses = new ArrayList<Response>();
            for (Element xmlResponse : xmlResponses)
            {
                Response response = getResponse(e);
                if (response != null)
                {
                    responses.add(response);
                }
            }
        }
        return responses;
    }

    private Response getResponse(Element e) throws Exception
    {
        if (e == null)
        {
            return null;
        }
        List<Doc> docs = getDocs(e);
        List<Param> params = getParams(e);
        List<Representation> representations = getRepresentations(e);
        String tempStatus = e.getAttributeValue(WADLConstant.RESPONSE_STATUS);//, wadlNamespace);
        int status = -1;
        if (tempStatus != null)
        {
            status = Integer.parseInt(tempStatus);
        }
        return new Response(docs, params, representations, status);

    }
}
