// -*-c++-*- osgWidget - Code by: Jeremy Moles (cubicool) 2007-2008

#include <sstream>
#include <osg/io_utils>
#include <osgWidget/StyleManager>

namespace osgWidget {

Style::Style(const std::string& name, const std::string& style):
_style(style) {
    setName(name);
}

Style::Style(const Style& style, const osg::CopyOp& co):
osg::Object (style, co),
_style      (style._style) {
}

bool Style::applyStyle(Widget* widget, Reader r) {
    std::string str;
    osg::Vec2   vec2;
    osg::Vec3   vec3;
    osg::Vec4   vec4;
    float       f;

    if(_match("pos %i %i", r) || _match("pos %f %f", r)) {
        r.readSequence(vec2);

        widget->setOrigin(vec2);
    }

    else if(_match("pos-x %i", r) || _match("pos-x %f", r)) {
        r.readSequence(f);

        widget->setX(f);
    }

    else if(_match("pos-y %i", r) || _match("pos-y %f", r)) {
        r.readSequence(f);

        widget->setY(f);
    }

    else if(_match("size %i %i", r) || _match("size %f %f", r)) {
        r.readSequence(vec2);

        widget->setSize(vec2);
    }

    else if(_match("width %i", r) || _match("width %f", r)) {
        r.readSequence(f);

        widget->setWidth(f);
    }

    else if(_match("height %i", r) || _match("height %f", r)) {
        r.readSequence(f);

        widget->setHeight(f);
    }

    // Color using 4x 0-255 integers.
    else if(_match("color %i %i %i %i", r)) {
        r.readSequence(vec4);

        widget->setColor(vec4 / 255.0f);
    }

    // Color using 3x 0-255 integers with a default alpha of 255.
    else if(_match("color %i %i %i", r)) {
        r.readSequence(vec3);

        widget->setColor(osg::Vec4(vec3[0], vec3[1], vec3[2], 255.0f) / 255.0f);
    }

    // Color using 4x 0.0f-1.0f floats.
    else if(_match("color %f %f %f %f", r)) {
        r.readSequence(vec4);

        widget->setColor(vec4);
    }

    // Color using 3x 0.0f-1.0f floats with a default alpha of 1.0f.
    else if(_match("color %f %f %f", r)) {
        r.readSequence(vec3);

        widget->setColor(osg::Vec4(vec3[0], vec3[1], vec3[2], 1.0f));
    }

    // Set padding uniformly.
    else if(_match("padding %i", r)) {
        r.readSequence(f);

        widget->setPadding(f);
    }

    // Set left padding.
    else if(_match("padding-left %i", r)) {
        r.readSequence(f);

        widget->setPadLeft(f);
    }

    // Set right padding.
    else if(_match("padding-right %i", r)) {
        r.readSequence(f);

        widget->setPadRight(f);
    }

    // Set top padding.
    else if(_match("padding-top %i", r)) {
        r.readSequence(f);

        widget->setPadTop(f);
    }

    // Set bottom padding.
    else if(_match("padding-bottom %i", r)) {
        r.readSequence(f);

        widget->setPadBottom(f);
    }

    else if(_match("layer %w", r)) {
        r.readSequence(str);

        widget->setLayer(strToLayer(str));
    }

    else if(_match("valign %w", r)) {
        r.readSequence(str);

        widget->setAlignVertical(strToVAlign(str));
    }

    else if(_match("halign %w", r)) {
        r.readSequence(str);

        widget->setAlignHorizontal(strToHAlign(str));
    }

    else if(_match("coordmode %w", r)) {
        r.readSequence(str);

        widget->setCoordinateMode(strToCoordMode(str));
    }

    else if(_match("fill %w", r)) {
        r.readSequence(str);

        widget->setCanFill(strToFill(str));
    }

    else if(_match("image %s", r)) {
        r.readSequence(str);

        widget->setImage(str, true);
    }

    // Otherwise, increment the stream pointer.
    else return false;

    return true;
}

bool Style::applyStyle(Label* /*label*/, Reader /*r*/) {
    return false;
}

bool Style::applyStyle(Input* /*input*/, Reader /*r*/) {
    return false;
}

bool Style::applyStyle(Window* window, Reader r) {
    osg::Vec2 vec2;
    float     f;

    if(_match("pos %i %i", r) || _match("pos %f %f", r)) {
        r.readSequence(vec2);

        window->setOrigin(vec2.x(), vec2.y());
    }

    else if(_match("pos-x %i", r) || _match("pos-x %f", r)) {
        r.readSequence(f);

        window->setX(f);
    }

    else if(_match("pos-y %i", r) || _match("pos-y %f", r)) {
        r.readSequence(f);

        window->setY(f);
    }

    else if(_match("size %i %i", r) || _match("size %f %f", r)) {
        r.readSequence(vec2);

        window->resize(vec2.x(), vec2.y());
    }

    else if(_match("width %i", r) || _match("width %f", r)) {
        r.readSequence(f);

        window->resize(f);
    }

    else if(_match("height %i", r) || _match("height %f", r)) {
        r.readSequence(f);

        window->resize(0.0f, f);
    }

    else return false;

    return true;
}

bool Style::applyStyle(Canvas* /*label*/, Reader /*r*/) {
    return false;
}


bool Style::applyStyle(Window::EmbeddedWindow*, Reader /*r*/) {
    return false;
}

bool Style::applyStyle(Box* box, Reader r) {
    if(applyStyle(static_cast<Window*>(box), r)) return true;

    return false;
}

bool Style::applyStyle(Frame::Corner*, Reader /*r*/) {
    return false;
}

bool Style::applyStyle(Frame::Border*, Reader /*r*/) {
    return false;
}

Widget::Layer Style::strToLayer(const std::string& layer)
{
    std::string l = lowerCase(layer);

    if(l == "top") return Widget::LAYER_TOP;

    else if(l == "high") return Widget::LAYER_HIGH;

    else if(l == "middle") return Widget::LAYER_MIDDLE;

    else if(l == "low") return Widget::LAYER_LOW;

    else if(l == "bg") return Widget::LAYER_BG;

    else {
        warn() << "Unknown Layer name [" << layer << "]; using LAYER_MIDDLE." << std::endl;

        return Widget::LAYER_MIDDLE;
    }
}

Widget::VerticalAlignment Style::strToVAlign(const std::string& valign) {
    std::string va = lowerCase(valign);

    if(va == "center") return Widget::VA_CENTER;

    else if(va == "top") return Widget::VA_TOP;

    else if(va == "bottom") return Widget::VA_BOTTOM;

    else {
        warn() << "Unknown VAlign name [" << valign << "]; using VA_CENTER." << std::endl;

        return Widget::VA_CENTER;
    }
}

Widget::HorizontalAlignment Style::strToHAlign(const std::string& halign) {
    std::string ha = lowerCase(halign);

    if(ha == "center") return Widget::HA_CENTER;

    else if(ha == "left") return Widget::HA_LEFT;

    else if(ha == "right") return Widget::HA_RIGHT;

    else {
        warn() << "Unknown HAlign name [" << halign << "]; using HA_CENTER." << std::endl;

        return Widget::HA_CENTER;
    }
}

Widget::CoordinateMode Style::strToCoordMode(const std::string& coordmode) {
    std::string cm = lowerCase(coordmode);

    if(cm == "absolute") return Widget::CM_ABSOLUTE;

    else if(cm == "relative") return Widget::CM_RELATIVE;

    else {
        warn()
            << "Unknown CoordMode name [" << coordmode
            << "]; using CM_ABSOLUTE." << std::endl
        ;

        return Widget::CM_ABSOLUTE;
    }
}

bool Style::strToFill(const std::string& fill) {
    std::string cm = lowerCase(fill);

    if(cm == "true") return true;

    else if(cm == "false") return false;

    else {
        warn()
            << "Unknown Fill name [" << fill
            << "]; using false." << std::endl
        ;

        return false;
    }
}

StyleManager::StyleManager() {
}

StyleManager::StyleManager(const StyleManager& manager, const osg::CopyOp& co):
osg::Object(manager, co) {
    for(ConstIterator i = _styles.begin(); i != _styles.end(); i++) if(i->second.valid()) {
        _styles[i->first] = new Style(*i->second.get(), osg::CopyOp::DEEP_COPY_ALL);
    }
}

bool StyleManager::_applyStyleToObject(osg::Object* obj, const std::string& style) {
    std::string c = obj->className();

    if(!std::string("Widget").compare(c)) return _coerceAndApply<Widget>(
        obj,
        style,
        c
    );

    else if(!std::string("Label").compare(c)) return _coerceAndApply<Label>(
        obj,
        style,
        c
    );

    else if(!std::string("Box").compare(c)) return _coerceAndApply<Box>(
        obj,
        style,
        c
    );
    else if(!std::string("Canvas").compare(c)) return _coerceAndApply<Canvas>(
        obj,
        style,
        c
    );
    else if(!std::string("Window").compare(c)) return _coerceAndApply<Window>(
        obj,
        style,
        c
    );
    else if(!std::string("Input").compare(c)) return _coerceAndApply<Input>(
        obj,
        style,
        c
    );
    else if(!std::string("Corner").compare(c)) return _coerceAndApply<Frame::Corner>(
        obj,
        style,
        c
    );
    else if(!std::string("Border").compare(c)) return _coerceAndApply<Frame::Border>(
        obj,
        style,
        c
    );
    else if(!std::string("EmbeddedWindow").compare(c)) return _coerceAndApply<Window::EmbeddedWindow>(
        obj,
        style,
        c
    );


    else warn()
        << "StyleManager does not support coercion of objects of type "
        << c << "." << std::endl
    ;

    return false;
}

bool StyleManager::addStyle(Style* style) {
    if(!style || style->getName().empty()) {
        warn() << "Cannot add a NULL or nameless Style object." << std::endl;

        return false;
    }

    _styles[style->getName()] = style;

    return true;
}

}
