/*
 * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
 * Copyright (C) 2007 Holger Hans Peter Freyther
 * Copyright (C) 2008 Kenneth Rohde Christiansen
 * Copyright (C) 2009-2010 ProFUSION embedded systems
 * Copyright (C) 2009-2010 Samsung Electronics
 * All rights reserved.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
 */

#include "config.h"
#include "Widget.h"

#include "ChromeClient.h"
#include "Cursor.h"
#include "Frame.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "IntRect.h"
#include "NotImplemented.h"
#include "Page.h"

#include <Ecore.h>
#include <Ecore_Evas.h>
#include <Edje.h>
#include <Evas.h>

#ifdef HAVE_ECORE_X
#include <Ecore_X.h>
#include <Ecore_X_Cursor.h>
#endif

#include <wtf/HashMap.h>
#include <wtf/text/CString.h>

namespace WebCore {

#ifdef HAVE_ECORE_X
class CursorMap {
private:
    HashMap<String, unsigned short> m_cursorStringMap;

public:
    CursorMap();
    unsigned int cursor(String);
};

unsigned int CursorMap::cursor(String cursorGroup)
{
    int ret = m_cursorStringMap.get(cursorGroup);

    if (ret < ECORE_X_CURSOR_X || ret > ECORE_X_CURSOR_XTERM)
        ret = ECORE_X_CURSOR_LEFT_PTR;

    return ret;
}

CursorMap::CursorMap()
{
    m_cursorStringMap.set("cursor/pointer", ECORE_X_CURSOR_LEFT_PTR);
    m_cursorStringMap.set("cursor/move", ECORE_X_CURSOR_FLEUR);
    m_cursorStringMap.set("cursor/cross", ECORE_X_CURSOR_CROSS);
    m_cursorStringMap.set("cursor/hand", ECORE_X_CURSOR_HAND2);
    m_cursorStringMap.set("cursor/i_beam", ECORE_X_CURSOR_XTERM);
    m_cursorStringMap.set("cursor/wait", ECORE_X_CURSOR_WATCH);
    m_cursorStringMap.set("cursor/help", ECORE_X_CURSOR_QUESTION_ARROW);
    m_cursorStringMap.set("cursor/east_resize", ECORE_X_CURSOR_RIGHT_SIDE);
    m_cursorStringMap.set("cursor/north_resize", ECORE_X_CURSOR_TOP_SIDE);
    m_cursorStringMap.set("cursor/north_east_resize", ECORE_X_CURSOR_TOP_RIGHT_CORNER);
    m_cursorStringMap.set("cursor/north_west_resize", ECORE_X_CURSOR_TOP_LEFT_CORNER);
    m_cursorStringMap.set("cursor/south_resize", ECORE_X_CURSOR_BOTTOM_SIDE);
    m_cursorStringMap.set("cursor/south_east_resize", ECORE_X_CURSOR_BOTTOM_RIGHT_CORNER);
    m_cursorStringMap.set("cursor/south_west_resize", ECORE_X_CURSOR_BOTTOM_LEFT_CORNER);
    m_cursorStringMap.set("cursor/west_resize", ECORE_X_CURSOR_LEFT_SIDE);
    m_cursorStringMap.set("cursor/north_south_resize", ECORE_X_CURSOR_SB_H_DOUBLE_ARROW);
    m_cursorStringMap.set("cursor/east_west_resize", ECORE_X_CURSOR_SB_V_DOUBLE_ARROW);
    m_cursorStringMap.set("cursor/north_east_south_west_resize", ECORE_X_CURSOR_SIZING);
    m_cursorStringMap.set("cursor/north_west_south_east_resize", ECORE_X_CURSOR_SIZING);
    m_cursorStringMap.set("cursor/column_resize", ECORE_X_CURSOR_SB_V_DOUBLE_ARROW);
    m_cursorStringMap.set("cursor/row_resize", ECORE_X_CURSOR_SB_H_DOUBLE_ARROW);
    m_cursorStringMap.set("cursor/middle_panning",  ECORE_X_CURSOR_CROSS_REVERSE);
    m_cursorStringMap.set("cursor/east_panning", ECORE_X_CURSOR_CROSS_REVERSE);
    m_cursorStringMap.set("cursor/north_panning", ECORE_X_CURSOR_CROSS_REVERSE);
    m_cursorStringMap.set("cursor/north_east_panning", ECORE_X_CURSOR_CROSS_REVERSE);
    m_cursorStringMap.set("cursor/north_west_panning", ECORE_X_CURSOR_CROSS_REVERSE);
    m_cursorStringMap.set("cursor/south_panning", ECORE_X_CURSOR_CROSS_REVERSE);
    m_cursorStringMap.set("cursor/south_east_panning", ECORE_X_CURSOR_CROSS_REVERSE);
    m_cursorStringMap.set("cursor/south_west_panning", ECORE_X_CURSOR_CROSS_REVERSE);
    m_cursorStringMap.set("cursor/west_panning", ECORE_X_CURSOR_CROSS_REVERSE);
    m_cursorStringMap.set("cursor/vertical_text", ECORE_X_CURSOR_SB_DOWN_ARROW);
    m_cursorStringMap.set("cursor/cell", ECORE_X_CURSOR_ICON);
    m_cursorStringMap.set("cursor/context_menu", ECORE_X_CURSOR_HAND2);
    m_cursorStringMap.set("cursor/no_drop", ECORE_X_CURSOR_DOT_BOX_MASK);
    m_cursorStringMap.set("cursor/copy", ECORE_X_CURSOR_ICON);
    m_cursorStringMap.set("cursor/progress", ECORE_X_CURSOR_WATCH);
    m_cursorStringMap.set("cursor/alias", ECORE_X_CURSOR_MAN);
    m_cursorStringMap.set("cursor/none", ECORE_X_CURSOR_X);
    m_cursorStringMap.set("cursor/not_allowed", ECORE_X_CURSOR_X);
    m_cursorStringMap.set("cursor/zoom_in", ECORE_X_CURSOR_DIAMOND_CROSS);
    m_cursorStringMap.set("cursor/zoom_out", ECORE_X_CURSOR_DIAMOND_CROSS);
    m_cursorStringMap.set("cursor/grab", ECORE_X_CURSOR_HAND2);
    m_cursorStringMap.set("cursor/grabbing", ECORE_X_CURSOR_HAND2);
}

static CursorMap cursorStringMap = CursorMap();
#endif

class WidgetPrivate {
public:
    Evas* m_evas;
    Evas_Object* m_evasObject;
    String m_theme;

    WidgetPrivate()
        : m_evas(0)
        , m_evasObject(0)
        , m_cursorObject(0)
#ifdef HAVE_ECORE_X
        , m_isUsingEcoreX(false)
#endif
    {}

    /* cursor */
    String m_cursorGroup;
    Evas_Object* m_cursorObject;

#ifdef HAVE_ECORE_X
    bool m_isUsingEcoreX;
#endif
};

Widget::Widget(PlatformWidget widget)
    : m_parent(0)
    , m_widget(0)
    , m_selfVisible(false)
    , m_parentVisible(false)
    , m_frame(0, 0, 0, 0)
    , m_data(new WidgetPrivate)
{
    init(widget);
}

Widget::~Widget()
{
    ASSERT(!parent());

    if (m_data->m_cursorObject)
        evas_object_del(m_data->m_cursorObject);

    delete m_data;
}

IntRect Widget::frameRect() const
{
    return m_frame;
}

void Widget::setFrameRect(const IntRect& rect)
{
    m_frame = rect;
    Widget::frameRectsChanged();
}

void Widget::frameRectsChanged()
{
    Evas_Object* o = evasObject();
    Evas_Coord x, y;

    if (!parent() || !o)
        return;

    IntRect rect = frameRect();
    if (parent()->isScrollViewScrollbar(this))
        rect.setLocation(parent()->convertToContainingWindow(rect.location()));
    else
        rect.setLocation(parent()->contentsToWindow(rect.location()));

    evas_object_geometry_get(root()->evasObject(), &x, &y, 0, 0);
    evas_object_move(o, x + rect.x(), y + rect.y());
    evas_object_resize(o, rect.width(), rect.height());
}

void Widget::setFocus(bool focused)
{
}

void Widget::applyFallbackCursor()
{
#if HAVE_ECORE_X
    if (m_data->m_isUsingEcoreX && !m_data->m_cursorGroup.isNull()) {
        int shape = cursorStringMap.cursor(m_data->m_cursorGroup.utf8().data());

        if (shape < ECORE_X_CURSOR_X || shape > ECORE_X_CURSOR_XTERM) {
            LOG_ERROR("cannot map an equivalent X cursor for"
                      " c ursor group %s", m_data->m_cursorGroup.utf8().data());
            shape = ECORE_X_CURSOR_LEFT_PTR;
        }

        Ecore_X_Window win = ecore_evas_software_x11_window_get(ecoreEvas());
        Ecore_X_Cursor cur = ecore_x_cursor_shape_get(shape);
        ecore_x_window_cursor_set(win, cur);
        return;
    }
#endif
    LOG("Ooops, no fallback to set cursor %s!\n",
        m_data->m_cursorGroup.utf8().data());
}

void Widget::applyCursor()
{
    CString file = edjeThemeRecursive().utf8();

    m_data->m_cursorObject = edje_object_add(evas());
    if (!file.isNull() && !edje_object_file_set(m_data->m_cursorObject, file.data(), m_data->m_cursorGroup.utf8().data())) {
        evas_object_del(m_data->m_cursorObject);
        m_data->m_cursorObject = 0;
        ecore_evas_object_cursor_set(ecoreEvas(), 0, 0, 0, 0);
        applyFallbackCursor();
    } else {
        Evas_Coord x, y, w, h;
        const char *d;

        edje_object_size_min_get(m_data->m_cursorObject, &w, &h);
        if ((w <= 0) || (h <= 0))
            edje_object_size_min_calc(m_data->m_cursorObject, &w, &h);
        if ((w <= 0) || (h <= 0))
            w = h = 16;
        evas_object_resize(m_data->m_cursorObject, w, h);

        d = edje_object_data_get(m_data->m_cursorObject, "hot.x");
        x = d ? atoi(d) : 0;

        d = edje_object_data_get(m_data->m_cursorObject, "hot.y");
        y = d ? atoi(d) : 0;

        ecore_evas_object_cursor_set(ecoreEvas(), m_data->m_cursorObject,
                                     EVAS_LAYER_MAX, x, y);
    }
}

void Widget::setCursor(const Cursor& cursor)
{
    if (!evas())
         return;

    const char *group = cursor.impl();
    if (!group || String(group) == m_data->m_cursorGroup)
        return;

    m_data->m_cursorGroup = group;

    applyCursor();
}

void Widget::show()
{
    if (!platformWidget())
         return;

    evas_object_show(platformWidget());
}

void Widget::hide()
{
    if (!platformWidget())
         return;

    evas_object_hide(platformWidget());
}

void Widget::paint(GraphicsContext* context, const IntRect&)
{
    notImplemented();
}

void Widget::setIsSelected(bool)
{
    notImplemented();
}

const String Widget::edjeTheme() const
{
    return m_data->m_theme;
}

void Widget::setEdjeTheme(const String& themePath)
{
    if (m_data->m_theme == themePath)
        return;

    m_data->m_theme = themePath;
}

const String Widget::edjeThemeRecursive() const
{
    if (!m_data->m_theme.isNull())
        return m_data->m_theme;
    if (m_parent)
        return m_parent->edjeThemeRecursive();

    return String();
}

Evas* Widget::evas() const
{
    return m_data->m_evas;
}

Ecore_Evas* Widget::ecoreEvas() const
{
    // FIXME EFL: XXX assume evas was created by ecore_evas
    return static_cast<Ecore_Evas*>(evas_data_attach_get(evas()));
}

void Widget::setEvasObject(Evas_Object *o)
{
    // FIXME: study platformWidget() and use it
    // FIXME: right now platformWidget() requires implementing too much
    if (m_data->m_evasObject == o)
        return;
    m_data->m_evasObject = o;
    if (!o) {
        m_data->m_evas = 0;
#ifdef HAVE_ECORE_X
        m_data->m_isUsingEcoreX = false;
#endif
        return;
    }

    m_data->m_evas = evas_object_evas_get(o);

#ifdef HAVE_ECORE_X
    const char *engine = ecore_evas_engine_name_get(ecoreEvas());
    m_data->m_isUsingEcoreX = (!strcmp(engine, "software_x11")
                               || !strcmp(engine, "software_xcb")
                               || !strcmp(engine, "software_16_x11")
                               || !strncmp(engine, "xrender", sizeof("xrender") - 1));
#endif

    Widget::frameRectsChanged();
}

Evas_Object* Widget::evasObject() const
{
    return m_data->m_evasObject;
}

}
