/*!
  \file
  \brief メニュー

  \author Satofumi KAMIMURA

  $Id: Menu.cpp 1956 2011-07-19 01:04:56Z satofumi $

  \todo clickedId() の情報を、イベントで監視できるようにすべき
*/

#include "Menu.h"
#include "Button.h"
#include "Label.h"
#include "LayerManager.h"
#include "CallbackEvent.h"
#include "AlignUtils.h"
#include "log_printf.h"
#include <set>

using namespace qrk;
using namespace std;


namespace
{
    typedef map<size_t, Button*> Items;
    typedef vector<size_t> ButtonOrder;
}


struct Menu::pImpl
{
    LayerManager layer_manager_;
    Items items_;
    Rect<long> rect_;
    Rect<long> draw_rect_;
    int align_;
    float alpha_;
    ButtonOrder button_order_;
    set<size_t> disabled_buttons_;
    int default_index_;
    int clicked_index_;
    int previous_index_;
    int index_;
    int decided_press_index_;
    bool step_specified_;
    bool key_decided_;
    Point<long> step_;
    Point<long> base_point_;
    Point<long> prev_cursor_;
    Point<long> cursor_;

    bool enable_no_select_;
    bool enable_rotate_;
    bool clicked_and_focus_;

    CallbackEvent decide_press_event_;
    CallbackEvent decide_release_event_;
    CallbackEvent up_event_;
    CallbackEvent down_event_;
    bool event_acceptable_;

    Label* icon_label_;
    bool icon_draw_front_;
    Point<long> icon_offset_;


    pImpl(const Rect<long>& rect, int align, Axis axis)
        : rect_(rect), align_(align), alpha_(1.0), default_index_(InvalidIndex),
          clicked_index_(default_index_), previous_index_(default_index_),
          index_(default_index_), decided_press_index_(InvalidIndex),
          step_specified_(false), key_decided_(false),
          prev_cursor_(layer_manager_.cursor()), cursor_(prev_cursor_),
          enable_no_select_(false), enable_rotate_(false),
          clicked_and_focus_(false),
          event_acceptable_(true), icon_label_(NULL), icon_draw_front_(true)
    {
        setAlignBase(align);

        // リターンキーで決定
        decide_press_event_.setEventAcceptable(false);
        decide_press_event_.key(SDLK_RETURN, SDL_KEYDOWN, KMOD_NONE);
        decide_release_event_.key(SDLK_RETURN, SDL_KEYUP, KMOD_NONE);

        // カーソルキーで選択項目の移動
        if (axis == Verbatim) {
            up_event_.key(SDLK_UP, SDL_KEYDOWN, KMOD_NONE);
            down_event_.key(SDLK_DOWN, SDL_KEYDOWN, KMOD_NONE);
        } else {
            up_event_.key(SDLK_LEFT, SDL_KEYDOWN, KMOD_NONE);
            down_event_.key(SDLK_RIGHT, SDL_KEYDOWN, KMOD_NONE);
        }

        layer_manager_.insertEvent(&decide_press_event_);
        layer_manager_.insertEvent(&decide_release_event_);
        layer_manager_.insertEvent(&up_event_);
        layer_manager_.insertEvent(&down_event_);

        setEnableNoSelect(enable_no_select_);
    }


    ~pImpl(void)
    {
        layer_manager_.removeEvent(&decide_press_event_);
        layer_manager_.removeEvent(&decide_release_event_);
        layer_manager_.removeEvent(&up_event_);
        layer_manager_.removeEvent(&down_event_);
    }


    void setAlignBase(int align)
    {
        base_point_.x = 0;
        base_point_.y = 0;

        if ((align & Left) || (align & Top)) {
            // base_point_ は (0, 0) のまま

        }
        if (align & Center) {
            base_point_ += Point<long>(rect_.w / 2, 0);

        }
        if (align & Right) {
            base_point_ += Point<long>(rect_.w - 1, 0);

        }
        if (align & Middle) {
            base_point_ += Point<long>(0, rect_.h / 2);

        }
        if (align & Bottom) {
            base_point_ += Point<long>(0, rect_.h - 1);
        }
    }


    void setAlpha(float alpha)
    {
        for (Items::iterator it = items_.begin(); it != items_.end(); ++it) {
            it->second->setAlpha(alpha);
        }
        alpha_ = alpha;
    }


    void draw(const Rect<long>& area, const Point<long>& own_position)
    {
        // キー操作の反映
        updateKeySelect();

        if (icon_label_ && !icon_draw_front_) {
            // アイコンを背面に描画する場合
            drawIcon(area, own_position);
        }

        // メニュー項目の描画
        Point<long> draw_point = base_point_ + own_position;
        for (Items::iterator it = items_.begin(); it != items_.end(); ++it) {
            Button* button = it->second;
            Point<long> aligned_point = draw_point;

            aligned_point += alignedPosition(button);
            button->setPosition(aligned_point);
            button->draw(area);

            draw_point += step_;
        }

        if (icon_label_ && !icon_draw_front_) {
            // アイコンを前面に描画する場合
            drawIcon(area, own_position);
        }

        // マウス操作の反映
        prev_cursor_ = cursor_;
        cursor_ = layer_manager_.cursor();
        updateCursorSelect();

        detectClicked();
    }


    void drawIcon(const Rect<long>& area, const Point<long>& own_position)
    {
        if (index_ < 0) {
            // 位置が無効な場合は戻る
            return;
        }

        Point<long> draw_point = base_point_ + own_position;
        for (int i = 0; i < index_; ++i) {
            draw_point += step_;
        }

        draw_point += alignedPosition(icon_label_);
        icon_label_->setPosition(draw_point + icon_offset_);
        icon_label_->draw(area);
    }


    Point<long> alignedPosition(Component* component)
    {
        Point<long> offset;

        if (align_ & Left) {
            // そのままの位置を用いる

        } else if (align_ & Center) {
            offset.x = center(*component, 0);

        } else if (align_ & Right) {
            offset.x = right(*component, 0);
        }

        if (align_ & Top) {
            // そのままの位置を用いる

        } else if (align_ & Middle) {
            offset.y = middle(*component, 0);

        } else if (align_ & Bottom) {
            offset.y = bottom(*component, 0);
        }
        return offset;
    }


    void addButton(Button* button, size_t id)
    {
        // step が指定されたことがなければ align に応じてステップを更新する
        if (!step_specified_) {
            if ((align_ & Left) || (align_ & Center) || (align_ & Right)) {
                step_.y = max(button->rect().h, step_.y);

            } else if ((align_ & Top) || (align_ & Middle) ||
                       (align_ & Bottom)) {
                step_.x = max(button->rect().w, step_.x);
            }
        }
        items_[id] = button;
        button->setAlpha(alpha_);
    }


    void detectClicked(void)
    {
        if ((clicked_index_ >= 0) && !clicked_and_focus_) {
            // 既に項目が決定されていれば戻る
            return;
        }

        size_t n = button_order_.size();
        for (size_t i = 0; i < n; ++i) {
            Button* button = items_[button_order_[i]];
            if (button->isClicked()) {
                if (clicked_index_ != static_cast<int>(i)) {
                    // 直前まで選択されていたボタンの状態を Normal に戻す
                    Button* pre_button =
                        buttonResource(button_order_[clicked_index_]);
                    if (pre_button) {
                        pre_button->release();
                    }
                    clicked_index_ = i;
                    index_ = i;
                    button->click();
                }
            }
        }

        if (!clicked_and_focus_ && (clicked_index_ >= 0)) {
            // 今回のチェックで決定が検出された場合、ボタンへの操作を無効にする
            setEventAcceptable(false);
            setButtonsEnabled(false);

            if (!key_decided_) {
                Button* button = buttonResource(button_order_[clicked_index_]);
                button->click();

                // マウス操作によって決定された場合は index_ を無効にする
                index_ = InvalidIndex;
                key_decided_ = false;
            }
        }
    }


    void setButtonsEnabled(bool enable)
    {
        for (Items::iterator it = items_.begin(); it != items_.end(); ++it) {
            Button* button = it->second;
            button->setEventAcceptable(enable);
            if (enable) {
                button->release();
            }
        }
    }


    void updateCursorSelect(void)
    {
        if ((clickedId() != InvalidId) && !clicked_and_focus_) {
            // 決定されていれば、操作を反映させない
            return;
        }

        if ((prev_cursor_.x != cursor_.x) || (prev_cursor_.y != cursor_.y)) {
            // マウスが移動し、別ボタンにフォーカスが発生したときに
            // 選択を無効にする
            int focused_index = focusedIndex(index_);
            if (focused_index != InvalidIndex) {
                size_t id = button_order_[index_];
                if ((index_ >= 0) && !isClicked(id)) {
                    release(button_order_[index_]);
                }
                index_ = default_index_;

            } else {
                if (enable_no_select_) {
                    if (index_ >= 0) {
                        size_t id = button_order_[index_];
                        Button* focused_button = items_[id];
                        if (!focused_button->isCursorInner()) {
                            if (!focused_button->isClicked()) {
                                focused_button->release();
                            }
                        }
                    }
                }
            }
        }
    }


    void updateKeySelect(void)
    {
        if ((clickedId() != InvalidId) && !clicked_and_focus_) {
            // 決定されていれば、操作を反映させない
            return;
        }

        // キー操作の描画への反映
        bool decide_pressed = decide_press_event_.isActive();
        if (decide_pressed) {
            decided_press_index_ = index_;
        }
        // 配置直後の押下に反応しないための処理
        // これがないと、ウィンドウ配置用の押下に反応する
        decide_press_event_.setEventAcceptable(event_acceptable_);

        size_t n = button_order_.size();
        bool decided = decide_release_event_.isActive();
        if ((index_ >= 0) && (index_ < static_cast<int>(n)) &&
            decided && (decided_press_index_ == index_)) {
            // 決定扱いにする
            press(button_order_[index_]);
            key_decided_ = true;

            // フォーカス表示のために index と previous_index を違う値にする
            //previous_index_ = index_ - 1;
            previous_index_ = InvalidIndex;
            return;
        }

        bool up = up_event_.isActive();
        bool down = down_event_.isActive();

        // フォーカス中のボタン情報を index_ に反映させる
        int focused_index = focusedIndex();
        if (focused_index >= 0) {
            index_ = focused_index;
        }

        // 選択項目の移動
        if (down) {
            for (size_t i = 0; i < n; ++i) {
                ++index_;
                if (index_ >= static_cast<int>(n)) {
                    index_ = (enable_rotate_) ? 0 : previous_index_;
                }
                size_t id = button_order_[index_];
                if (disabled_buttons_.find(id) == disabled_buttons_.end()) {
                    break;
                }
            }
        }
        if (up) {
            for (size_t i = 0; i < n; ++i) {
                if (index_ <= 0) {
                    if (enable_rotate_) {
                        index_ = static_cast<int>(n - 1);
                    }
                } else {
                    --index_;
                }
                size_t id = button_order_[index_];
                if (disabled_buttons_.find(id) == disabled_buttons_.end()) {
                    break;
                }
            }
        }

        if ((index_ != previous_index_) && !clicked_and_focus_) {
            // 見た目の更新
            if (previous_index_ >= 0) {
                size_t id = button_order_[previous_index_];
                if (!isClicked(id)) {
                    release(id);
                }
            }
            if (index_ >= 0) {
                setFocus(button_order_[index_]);
            }
            previous_index_ = index_;
        }
    }


    int focusedIndex(int ignore_index = InvalidIndex)
    {
        size_t n = button_order_.size();
        for (size_t i = 0; i < n; ++i) {
            if (static_cast<int>(i) == ignore_index) {
                continue;
            }
            size_t id = button_order_[i];
            Button* button = items_[id];
            if ((button->isFocused()) &&
                (static_cast<int>(i) != clicked_index_)) {
                return static_cast<int>(i);
            }
        }
        return InvalidIndex;
    }


    Button* buttonResource(size_t id)
    {
        Items::iterator it = items_.find(id);
        if (it == items_.end()) {
            return NULL;
        } else {
            return it->second;
        }
    }


    void setFocus(size_t id)
    {
        Button* button = buttonResource(id);
        if (button) {
            button->setFocus();
        }
    }


    void press(size_t id)
    {
        Button* button = buttonResource(id);
        if (button) {
            button->click();
        }
    }


    void release(size_t id)
    {
        Button* button = buttonResource(id);
        if (button) {
            button->release();
        }
    }


    void release(void)
    {
        //setEventAcceptable(true);
        setButtonsEnabled(true);
        clicked_index_ = InvalidIndex;
    }


    bool isClicked(size_t id)
    {
        Button* button = buttonResource(id);
        return (button) ? button->isClicked() : false;
    }


    size_t clickedId(void)
    {
        vector<size_t> clicked_id;

        for (Items::iterator it = items_.begin(); it != items_.end(); ++it) {
            if (it->second->isClicked()) {
                return it->first;
            }
        }
        return InvalidId;
    }


    void updateButtonOrder(void)
    {
        button_order_.clear();
        for (Items::iterator it = items_.begin(); it != items_.end(); ++it) {
            button_order_.push_back(it->first);
        }
    }


    void setEventAcceptable(bool acceptable)
    {
        event_acceptable_ = acceptable;
        decide_press_event_.setEventAcceptable(acceptable);
        decide_release_event_.setEventAcceptable(acceptable);
        up_event_.setEventAcceptable(acceptable);
        down_event_.setEventAcceptable(acceptable);

#if 0
        for (Items::iterator it = items_.begin(); it != items_.end(); ++it) {
            it->second->setEventAcceptable(acceptable);
        }
#endif
    }


    void setEnableNoSelect(bool enable)
    {
        default_index_ = (enable) ? InvalidIndex : 0;

        if (!enable && (index_ == InvalidIndex)) {
            index_ = default_index_;
        }
    }
};


Menu::Menu(const Rect<long>& rect, int align, Axis axis)
    : pimpl(new pImpl(rect, align, axis))
{
}


Menu::~Menu(void)
{
}


Rect<long> Menu::rect(void) const
{
    return pimpl->rect_;
}


void Menu::setEventAcceptable(bool acceptable)
{
    pimpl->setEventAcceptable(acceptable);
}


void Menu::setAlpha(float alpha)
{
    pimpl->setAlpha(alpha);
}


float Menu::alpha(void)
{
    return pimpl->alpha_;
}


void Menu::setRotateAngle(const Angle& angle)
{
    (void)angle;
    // !!!
}


void Menu::draw(const Rect<long>& area)
{
    pimpl->draw(area, position());
}


void Menu::setButtonStep(const Point<long>& step)
{
    pimpl->step_ = step;
    pimpl->step_specified_ = true;
}


void Menu::addButton(Component& button, size_t id)
{
    // !!! dynamic_cast の使い方を見直す
    Button* raw_button = dynamic_cast<Button*>(&button);
    if (raw_button == NULL) {
        log_printf("Menu::addButton(): no Button obuject.\n");
        return;
    }

    pimpl->addButton(raw_button, id);
    pimpl->updateButtonOrder();
}


void Menu::removeButton(size_t id)
{
    pimpl->items_.erase(id);
    pimpl->updateButtonOrder();
}


void Menu::setButtonEnabled(size_t id, bool enable)
{
    if (!enable) {
        pimpl->disabled_buttons_.insert(id);
    } else {
        pimpl->disabled_buttons_.erase(id);
    }

    Button* button = pimpl->buttonResource(id);
    if (button) {
        button->setEnabled(enable);
    }
}


void Menu::setEnableNoSelect(bool enable)
{
    pimpl->setEnableNoSelect(enable);
}


void Menu::setEnableRotate(bool enable)
{
    pimpl->enable_rotate_ = enable;
}


void Menu::setEnableClickedAndFocus(bool enable)
{
    pimpl->clicked_and_focus_ = enable;
}


bool Menu::empty(void)
{
    return pimpl->items_.empty();
}


size_t Menu::indexSize(void)
{
    return pimpl->items_.size();
}


void Menu::setFocus(size_t id)
{
    pimpl->setFocus(id);
}


void Menu::press(size_t id)
{
    pimpl->press(id);
}


void Menu::release(size_t id)
{
    pimpl->release(id);
}


void Menu::release(void)
{
    pimpl->release();
}


void Menu::setIndex(int index)
{
    index = max(static_cast<int>(InvalidIndex), index);
    index = min(index, static_cast<int>(pimpl->items_.size()));

    if (index != pimpl->index_) {
        release();
    }
    pimpl->index_ = index;
}


int Menu::index(void) const
{
    return pimpl->index_;
}


bool Menu::isClicked(void) const
{
    return (pimpl->clicked_index_ == InvalidIndex) ? false : true;
}


size_t Menu::clickedId(void) const
{
    if (pimpl->clicked_index_ == InvalidIndex) {
        return InvalidId;
    } else {
        return pimpl->button_order_[pimpl->clicked_index_];
    }
}


int Menu::clickedIndex(void) const
{
    return pimpl->clicked_index_;
}


void Menu::registerIconLabel(Component& icon_label, bool draw_front)
{
    // !!! dynamic_cast の使い方を見直す
    Label* raw_label = dynamic_cast<Label*>(&icon_label);
    if (raw_label == NULL) {
        log_printf("Menu::registerIconLabel(): no Label obuject.\n");
        return;
    }

    pimpl->icon_label_ = raw_label;
    pimpl->icon_draw_front_ = draw_front;
}


void Menu::unregisterIconLabel(void)
{
    pimpl->icon_label_ = NULL;
}


void Menu::setIconOffset(const Point<long>& offset)
{
    pimpl->icon_offset_ = offset;
}


void Menu::connectPressedEvent(ButtonIdCallback::slot_type& slot)
{
    static_cast<void>(slot);
    // !!!
}
