/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

/* $Rev$ $Date$ */

#ifndef tuscany_element_hpp
#define tuscany_element_hpp

/**
 * Functions to help represent data as lists of elements and attributes.
 */

#include "list.hpp"
#include "value.hpp"

namespace tuscany
{

/**
 * Tags used to tag lists of elements and attributes.
 */
const value attribute("attribute");
const value element("element");
const string atsign("@");

/**
 * Returns true if a value is an element.
 */
bool isElement(const value& v) {
    if (!isList(v) || isNil(v) || element != car<value>(v))
        return false;
    return true;
}

/**
 * Returns true if a value is an attribute.
 */
bool isAttribute(const value& v) {
    if (!isList(v) || isNil(v) || attribute != car<value>(v))
        return false;
    return true;
}

/**
 * Returns the name of an attribute.
 */
const value attributeName(const list<value>& l) {
    return cadr(l);
}

/**
 * Returns the value of an attribute.
 */
const value attributeValue(const list<value>& l) {
    return caddr(l);
}

/**
 * Returns the name of an element.
 */
const value elementName(const list<value>& l) {
    return cadr(l);
}

/**
 * Returns true if an element has children.
 */
const bool elementHasChildren(const list<value>& l) {
    return !isNil(cddr(l));
}

/**
 * Returns the children of an element.
 */
const list<value> elementChildren(const list<value>& l) {
    return cddr(l);
}

/**
 * Returns true if an element has a value.
 */
const value elementHasValue(const list<value>& l) {
    const list<value> r = reverse(l);
    if (isSymbol(car(r)))
        return false;
    if(isList(car(r)) && !isNil((list<value>)car(r)) && isSymbol(car<value>(car(r))))
            return false;
    return true;
}

/**
 * Returns the value of an element.
 */
const value elementValue(const list<value>& l) {
    return car(reverse(l));
}

/**
 * Convert an element to a value.
 */
const bool elementToValueIsList(const value& v) {
    if (!isList(v))
        return false;
    const list<value> l = v;
    return (isNil(l) || !isSymbol(car(l)));
}

const value elementToValue(const value& t) {
    const list<value> elementsToValues(const list<value>& e);

    // Convert an attribute
    if (isTaggedList(t, attribute))
        return mklist<value>(c_str(atsign + attributeName(t)), attributeValue(t));

    // Convert an element
    if (isTaggedList(t, element)) {

        // Convert an element's value
        if (elementHasValue(t)) {

            // Convert a single value
            if (!elementToValueIsList(elementValue(t)))
                return mklist(elementName(t), elementValue(t));

            // Convert a list value
            return cons(elementName(t), mklist<value>(elementsToValues(elementValue(t))));
        }

        // Convert an element's children
        return cons(elementName(t), elementsToValues(elementChildren(t)));
    }

    // Convert a value
    if (!isList(t))
        return t;
    return elementsToValues(t);
}

/**
 * Convert a list of elements to a list of values.
 */
const bool elementToValueIsSymbol(const value& v) {
    if (!isList(v))
        return false;
    const list<value> l = v;
    if (isNil(l))
        return false;
    if (!isSymbol(car(l)))
        return false;
    return true;
}

const list<value> elementToValueGroupValues(const value& v, const list<value>& l) {
    if (isNil(l) || !elementToValueIsSymbol(v) || !elementToValueIsSymbol(car(l)))
        return cons(v, l);
    if (car<value>(car(l)) != car<value>(v))
        return cons(v, l);
    if (!elementToValueIsList(cadr<value>(car(l)))) {
        const value g = mklist<value>(car<value>(v), mklist<value>(cdr<value>(v), cdr<value>(car(l))));
        return elementToValueGroupValues(g, cdr(l));
    }
    const value g = mklist<value>(car<value>(v), cons<value>(cdr<value>(v), (list<value>)cadr<value>(car(l))));
    return elementToValueGroupValues(g, cdr(l));

}

const list<value> elementsToValues(const list<value>& e) {
    if (isNil(e))
        return e;
    return elementToValueGroupValues(elementToValue(car(e)), elementsToValues(cdr(e)));
}

/**
 * Convert a value to an element.
 */
const value valueToElement(const value& t) {
    const list<value> valuesToElements(const list<value>& l);

    // Convert a name value pair
    if (isList(t) && !isNil((list<value>)t) && isSymbol(car<value>(t))) {
        const value n = car<value>(t);
        const value v = cadr<value>(t);

        // Convert a single value to an attribute or an element
        if (!isList(v)) {
            if (substr(n, 0, 1) == atsign)
                return mklist<value>(attribute, substr(n, 1), v);
            return mklist(element, n, v);
        }

        // Convert a list value
        if (isNil((list<value>)v) || !isSymbol(car<value>(v)))
            return cons(element, cons(n, mklist<value>(valuesToElements(v))));

        // Convert a nested name value pair value
        return cons(element, cons(n, valuesToElements(cdr<value>(t))));
    }

    // Convert a value
    if (!isList(t))
        return t;
    return valuesToElements(t);
}

/**
 * Convert a list of values to a list of elements.
 */
const list<value> valuesToElements(const list<value>& l) {
    if (isNil(l))
        return l;
    return cons<value>(valueToElement(car(l)), valuesToElements(cdr(l)));
}

/**
 * Returns a selector lambda function which can be used to filter
 * elements against the given element pattern.
 */
struct selectorLambda {
    const list<value> select;
    selectorLambda(const list<value>& s) : select(s) {
    }
    const bool evalSelect(const list<value>& s, const list<value> v) const {
        if (isNil(s))
            return true;
        if (isNil(v))
            return false;
        if (car(s) != car(v))
            return false;
        return evalSelect(cdr(s), cdr(v));
    }
    const bool operator()(const value& v) const {
        if (!isList(v))
            return false;
        return evalSelect(select, v);
    }
};

const lambda<bool(const value&)> selector(const list<value> s) {
    return selectorLambda(s);
}

/**
 * Returns the value of the attribute with the given name.
 */
struct filterAttribute {
    const value name;
    filterAttribute(const value& n) : name(n) {
    }
    const bool operator()(const value& v) const {
        return isAttribute(v) && attributeName((list<value>)v) == name;
    }
};

const value attributeValue(const value& name, const value& l) {
    const list<value> f = filter<value>(filterAttribute(name), list<value>(l));
    if (isNil(f))
        return value();
    return caddr<value>(car(f));
}

/**
 * Returns child elements with the given name.
 */
struct filterElement {
    const value name;
    filterElement(const value& n) : name(n) {
    }
    const bool operator()(const value& v) const {
        return isElement(v) && elementName((list<value>)v) == name;
    }
};

const value elementChildren(const value& name, const value& l) {
    return filter<value>(filterElement(name), list<value>(l));
}

/**
 * Return the child element with the given name.
 */
const value elementChild(const value& name, const value& l) {
    const list<value> f = elementChildren(name, l);
    if (isNil(f))
        return value();
    return car(f);
}

}
#endif /* tuscany_element_hpp */