#include "../../baygui.h"

namespace baygui {
    namespace awt {
        /** 垂直ボタン（パレット）*/
        static unsigned int button_palette[] = {
                                            0xff040204,
                                            0xff8486dc,
                                            0xffdcdafc,
                                            0xff5456b4,
                                            0xffb4b2fc,
                                            0xfff4f2f4,
                                        };

        /** 垂直ボタン（データ）*/
        static unsigned char button_data[] = {
                                                 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
                                                 0x0,0x5,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x4,0x0,
                                                 0x0,0x2,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x5,0x2,0x2,0x2,0x2,0x2,0x2,0x4,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x4,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x5,0x2,0x2,0x2,0x2,0x2,0x2,0x4,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x4,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x5,0x2,0x2,0x2,0x2,0x2,0x2,0x4,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x4,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x5,0x2,0x2,0x2,0x2,0x2,0x2,0x4,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x4,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x1,0x0,
                                                 0x0,0x2,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x1,0x0,
                                                 0x0,0x4,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,
                                                 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
                                             };

        /** 上向き矢印（パレット）*/
        static unsigned int arrow_palette[] = {
                                           0xff040204,
                                           0xffcccecc,
                                           0xfffcfefc,
                                           0xffe4e6e4,
                                       };

        /** 上向き矢印（データ）*/
        static unsigned char arrow1_data[] = {
                                                 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
                                                 0x0,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x3,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x0,0x0,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x0,0x0,0x0,0x0,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x3,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,
                                                 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
                                             };

        /** 下向き矢印（データ）*/
        static unsigned char arrow2_data[] = {
                                                 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
                                                 0x0,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x3,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x0,0x0,0x0,0x0,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x0,0x0,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x0,
                                                 0x0,0x3,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,
                                                 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
                                             };

        /** 新しい垂直スクロールバーを構築します。*/
        Scrollbar::Scrollbar() {
            addClassName(baygui_awt_Scrollbar);
            this->orientation = VERTICAL;
            this->minimum   = 0;
            this->maximum   = 100;
            this->blocksize = 10;
            this->preValue  = 0;
            this->value     = 0;
        }

        /** 指定された方向の新しいスクロールバーを構築します。*/
        Scrollbar::Scrollbar(int orientation) {
            addClassName(baygui_awt_Scrollbar);
            this->orientation = orientation;
            this->minimum   = 0;
            this->maximum   = 100;
            this->blocksize = 10;
            this->preValue  = 0;
            this->value     = 0;
        }

        /** デストラクタです。*/
        Scrollbar::~Scrollbar() {
        }

        /** このスクロールバーの「ブロック」増分値を返します。*/
        int Scrollbar::getBlocksize() {
            return this->blocksize;
        }

        /** このスクロールバーの最大値を返します。*/
        int Scrollbar::getMaximum() {
            return this->maximum;
        }

        /** このスクロールバーの最小値を返します。*/
        int Scrollbar::getMinimum() {
            return this->minimum;
        }

        /** このスクロールバーの現在値を返します。*/
        int Scrollbar::getValue() {
            return this->value;
        }

        /** このコンポーネントをペイントします。*/
        void Scrollbar::paint(Graphics* g) {
            int w = getWidth();
            int h = getHeight();

            /* 外枠を描画 */
            g->setColor(Color::gray);
            g->fillRect(0, 0, w, h);
            g->setColor(Color::black);
            g->drawRect(0, 0, w, h);

            /* 非活性のときはボタンを描画しない */
            if (isEnabled() == false) return;

            /* 最大値と最小値が等しいときはボタンを描画しない */
            if ((this->maximum - this->minimum) == 0) return;

            /* 垂直スクロールバーを描画 */
            if (this->orientation == VERTICAL) {
                /* 上向き矢印を描画 */
                for (int i = 0; i < 16; i++) {
                    for (int j = 0; j < 16; j++) {
                        g->setColor(arrow_palette[arrow1_data[i * 16 + j] & 0xFF]);
                        g->drawPixel(j, i);
                    }
                }
                /* 下向き矢印を描画 */
                for (int i = 0; i < 16; i++) {
                    for (int j = 0; j < 16; j++) {
                        g->setColor(arrow_palette[arrow2_data[i * 16 + j] & 0xFF]);
                        g->drawPixel(j, i + h - 16);
                    }
                }
                /* ボタンを描画 */
                int offset = 15 + (h - 47) * (this->value - this->minimum) / (this->maximum - this->minimum);
                for (int i = 0; i < 17; i++) {
                    for (int j = 0; j < 16; j++) {
                        g->setColor(button_palette[button_data[i * 16 + j] & 0xFF]);
                        g->drawPixel(j, i + offset);
                    }
                }
            /* 水平スクロールバーを描画 */
            } else {
                /* 左向き矢印を描画 */
                for (int i = 0; i < 16; i++) {
                    for (int j = 0; j < 16; j++) {
                        g->setColor(arrow_palette[arrow1_data[i * 16 + j] & 0xFF]);
                        g->drawPixel(i, j);
                    }
                }
                /* 右向き矢印を描画 */
                for (int i = 0; i < 16; i++) {
                    for (int j = 0; j < 16; j++) {
                        g->setColor(arrow_palette[arrow2_data[i * 16 + j] & 0xFF]);
                        g->drawPixel(i + w - 16, j);
                    }
                }
                /* ボタンを描画 */
                int offset = 15 + (w - 47) * (this->value - this->minimum) / (this->maximum - this->minimum);
                for (int i = 0; i < 17; i++) {
                    for (int j = 0; j < 16; j++) {
                        g->setColor(button_palette[button_data[i * 16 + j] & 0xFF]);
                        g->drawPixel(i + offset, j);
                    }
                }
            }
        }

        /** このコンポーネントで発生するマウスイベントを処理します。*/
        void Scrollbar::processMouseEvent(MouseEvent* e) {
            Component::processMouseEvent(e);

            if (e->getID() == MouseEvent::MOUSE_PRESSED) {
                int mx = e->getX();
                int my = e->getY();

                /* 垂直スクロールバーのとき */
                if (this->orientation == VERTICAL) {
                    if (0 < my && my < 16) {
                        setValue(this->value - this->blocksize);
                    } else if (getHeight() - 16 < my && my < getHeight()) {
                        setValue(this->value + this->blocksize);
                    } else {
                        setValue((my - 16) * (this->maximum - this->minimum) / (getHeight() - 32) + this->minimum);
                    }
                /* 水平スクロールバーのとき */
                } else {
                    if (0 < mx && mx < 16) {
                        setValue(this->value - this->blocksize);
                    } else if (getWidth() - 16 < mx && mx < getWidth()) {
                        setValue(this->value + this->blocksize);
                    } else {
                        setValue((mx - 16) * (this->maximum - this->minimum) / (getWidth() - 32) + this->minimum);
                    }
                }
            }
        }

        /** このコンポーネントで発生するマウスモーションイベントを処理します。*/
        void Scrollbar::processMouseMotionEvent(MouseEvent* e) {
            Component::processMouseMotionEvent(e);

            if (e->getID() == MouseEvent::MOUSE_DRAGGED) {
                int mx = e->getX();
                int my = e->getY();

                /* 垂直スクロールバーのとき */
                if (this->orientation == VERTICAL) {
                    if (my < 16) {
                        setValue(this->minimum);
                    } else if (my > getHeight() - 16) {
                        setValue(this->maximum);
                    } else {
                        setValue((my - 16) * (this->maximum - this->minimum) / (getHeight() - 32) + this->minimum);
                    }
                /* 水平スクロールバーのとき */
                } else if (this->orientation == HORIZONTAL) {
                    if (mx < 16) {
                        setValue(this->minimum);
                    } else if (mx > getWidth() - 16) {
                        setValue(this->maximum);
                    } else {
                        setValue((mx - 16) * (this->maximum - this->minimum) / (getWidth() - 32) + this->minimum);
                    }
                }
            }
        }

        /** このスクロールバーの「ブロック」増分値を設定します。*/
        void Scrollbar::setBlocksize(int n) {
            this->blocksize = n;
        }

        /** このスクロールバーの最大値を設定します。*/
        void Scrollbar::setMaximum(int n) {
            this->maximum = n;
        }

        /** このスクロールバーの最小値を設定します。*/
        void Scrollbar::setMinimum(int n) {
            this->minimum = n;
        }

        /**
         * このコンポーネントを移動し、サイズ変更します。
         * 垂直スクロールバーの時は幅が16、水平スクロールバーのときは高さが16になります。
         * それ以外の値を設定しても16になります。
        */
        void Scrollbar::setBounds(int x, int y, int w, int h) {
            if (this->orientation == VERTICAL) {
                Component::setBounds(x, y, 16, h);
            } else {
                Component::setBounds(x, y, w, 16);
            }
        }

        /** このスクロールバーの値を、指定された値に設定します。*/
        void Scrollbar::setValue(int value) {
            if (value < this->minimum) {
                this->value = this->minimum;
            } else if (value > this->maximum) {
                this->value = this->maximum;
            } else {
                this->value = value;
            }
            repaint();
            #if 0
            if (value < this->preValue) {
                this->adjustmentEvent.setType(Event::BLOCK_DECLEMENT);
                getParent()->processEvent(&this->adjustmentEvent);
            } else {
                this->adjustmentEvent.setType(Event::BLOCK_INCLEMENT);
                getParent()->processEvent(&this->adjustmentEvent);
            }
            #endif
            this->preValue = value;
        }
    }
}
