#include "solvespace.h"
#include "obj/icons.h"

BYTE SPACER[1];
static const struct {
    BYTE        *image;
    int          menu;
    char        *tip;
} Toolbar[] = {
    { Icon_line,            GraphicsWindow::MNU_LINE_SEGMENT,   "Sketch line segment"                               },
    { Icon_rectangle,       GraphicsWindow::MNU_RECTANGLE,      "Sketch rectangle"                                  },
    { Icon_circle,          GraphicsWindow::MNU_CIRCLE,         "Sketch circle"                                     },
    { Icon_arc,             GraphicsWindow::MNU_ARC,            "Sketch arc, or tangent arc at selected point"      },
    { Icon_bezier,          GraphicsWindow::MNU_CUBIC,          "Sketch cubic Bezier section"                       },
    { Icon_point,           GraphicsWindow::MNU_DATUM_POINT,    "Sketch datum point"                                },
    { Icon_construction,    GraphicsWindow::MNU_CONSTRUCTION,   "Toggle construction"                               },
    { Icon_trim,            GraphicsWindow::MNU_SPLIT_CURVES,   "Split lines / curves where they intersect"         },
    { SPACER  },

    { Icon_length,          GraphicsWindow::MNU_DISTANCE_DIA,   "Constrain distance / diameter / length"            },
    { Icon_angle,           GraphicsWindow::MNU_ANGLE,          "Constrain angle"                                   },
    { Icon_horiz,           GraphicsWindow::MNU_HORIZONTAL,     "Constrain to be horizontal"                        },
    { Icon_vert,            GraphicsWindow::MNU_VERTICAL,       "Constrain to be vertical"                          },
    { Icon_parallel,        GraphicsWindow::MNU_PARALLEL,       "Constrain to be parallel or tangent"               },
    { Icon_perpendicular,   GraphicsWindow::MNU_PERPENDICULAR,  "Constrain to be perpendicular"                     },
    { Icon_pointonx,        GraphicsWindow::MNU_ON_ENTITY,      "Constrain point on line / curve / plane / point"   },
    { Icon_symmetric,       GraphicsWindow::MNU_SYMMETRIC,      "Constrain symmetric"                               },
    { Icon_equal,           GraphicsWindow::MNU_EQUAL,          "Constrain equal length / radius / angle"           },
    { Icon_same_orientation,GraphicsWindow::MNU_ORIENTED_SAME,  "Constrain normals in same orientation"             },
    { Icon_other_supp,      GraphicsWindow::MNU_OTHER_ANGLE,    "Other supplementary angle"                         },
    { Icon_ref,             GraphicsWindow::MNU_REFERENCE,      "Toggle reference dimension"                        },
    { SPACER  },

    { Icon_extrude,         GraphicsWindow::MNU_GROUP_EXTRUDE,  "New group extruding active sketch"                 },
    { Icon_sketch_in_plane, GraphicsWindow::MNU_GROUP_WRKPL,    "New group in new workplane (thru given entities)"  },
    { Icon_step_rotate,     GraphicsWindow::MNU_GROUP_ROT,      "New group step and repeat rotating"                },
    { Icon_step_translate,  GraphicsWindow::MNU_GROUP_TRANS,    "New group step and repeat translating"             },
    { Icon_sketch_in_3d,    GraphicsWindow::MNU_GROUP_3D,       "New group in 3d"                                   },
    { Icon_assemble,        GraphicsWindow::MNU_GROUP_IMPORT,   "New group importing / assembling file"             },
    { SPACER  },

    { Icon_in3d,            GraphicsWindow::MNU_FREE_IN_3D,     "Sketch / constrain in 3d"                          },
    { Icon_ontoworkplane,   GraphicsWindow::MNU_SEL_WORKPLANE,  "Sketch / constrain in workplane"                   },
    { NULL  },
};

void GraphicsWindow::ToolbarDraw(void) {
    ToolbarDrawOrHitTest(0, 0, true, NULL);
}

bool GraphicsWindow::ToolbarMouseMoved(int x, int y) {
    x += ((int)width/2);
    y += ((int)height/2);
   
    int nh;
    bool withinToolbar = ToolbarDrawOrHitTest(x, y, false, &nh);
    if(!withinToolbar) nh = 0;

    if(nh != toolbarTooltipped) {
        // Don't let the tool tip move around if the mouse moves within the
        // same item.
        toolbarMouseX = x;
        toolbarMouseY = y;
        toolbarTooltipped = 0;
    }

    if(nh != toolbarHovered) {
        toolbarHovered = nh;
        SetTimerFor(1000);
        PaintGraphics();
    }
    // So if we moved off the toolbar, then toolbarHovered is now equal to
    // zero, so it doesn't matter if the tool tip timer expires. And if
    // we moved from one item to another, we reset the timer, so also okay.
    return withinToolbar;
}

bool GraphicsWindow::ToolbarMouseDown(int x, int y) {
    x += ((int)width/2);
    y += ((int)height/2);
   
    int nh = -1;
    bool withinToolbar = ToolbarDrawOrHitTest(x, y, false, &nh);
    // They might have clicked within the toolbar, but not on a button.
    if(withinToolbar && nh >= 0) {
        for(int i = 0; SS.GW.menu[i].level >= 0; i++) {
            if(nh == SS.GW.menu[i].id) {
                (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)SS.GW.menu[i].id);
                break;
            }
        }
    }
    return withinToolbar;
}

bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
                                          bool paint, int *menu)
{
    int i;
    int x = 17, y = (int)(height - 52);

    int fudge = 8;
    int h = 32*14 + 3*16 + fudge;
    int aleft = 0, aright = 66, atop = y+16+fudge/2, abot = y+16-h;

    bool withinToolbar =
        (mx >= aleft && mx <= aright && my <= atop && my >= abot);

    if(!paint && !withinToolbar) {
        // This gets called every MouseMove event, so return quickly.
        return false;
    }
    
    if(paint) {
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glTranslated(-1, -1, 0);
        glScaled(2.0/width, 2.0/height, 0);
        glDisable(GL_LIGHTING);

        double c = 30.0/255;
        glColor4d(c, c, c, 1.0);
        glBegin(GL_QUADS);
            glVertex2d(aleft,  atop);
            glVertex2d(aleft,  abot);
            glVertex2d(aright, abot);
            glVertex2d(aright, atop);
        glEnd();
    }

    struct {
        bool show;
        char *str;
    } toolTip = { false, NULL };

    bool leftpos = true;
    for(i = 0; Toolbar[i].image; i++) {
        if(Toolbar[i].image == SPACER) {
            if(!leftpos) {
                leftpos = true;
                y -= 32;
                x -= 32;
            }
            y -= 16;

            if(paint) {
                // Draw a separator bar in a slightly different color.
                int divw = 30, divh = 2;
                glColor4d(0.17, 0.17, 0.17, 1);
                x += 16;
                y += 24;
                glBegin(GL_QUADS);
                    glVertex2d(x+divw, y+divh);
                    glVertex2d(x+divw, y-divh);
                    glVertex2d(x-divw, y-divh);
                    glVertex2d(x-divw, y+divh);
                glEnd(); 
                x -= 16;
                y -= 24;
            }

            continue;
        }

        if(paint) {
            glRasterPos2i(x - 12, y - 12);
            glDrawPixels(24, 24, GL_RGB, GL_UNSIGNED_BYTE, Toolbar[i].image);

            if(toolbarHovered == Toolbar[i].menu) {
                // Highlight the hovered or pending item.
                glColor4d(1, 1, 0, 0.3);
                int boxhw = 15;
                glBegin(GL_QUADS);
                    glVertex2d(x+boxhw, y+boxhw);
                    glVertex2d(x+boxhw, y-boxhw);
                    glVertex2d(x-boxhw, y-boxhw);
                    glVertex2d(x-boxhw, y+boxhw);
                glEnd(); 
            }

            if(toolbarTooltipped == Toolbar[i].menu) {
                // Display the tool tip for this item; postpone till later
                // so that no one draws over us. Don't need position since
                // that's just wherever the mouse is.
                toolTip.show = true;
                toolTip.str = Toolbar[i].tip;
            }
        } else {
            int boxhw = 16;
            if(mx < (x+boxhw) && mx > (x - boxhw) &&
               my < (y+boxhw) && my > (y - boxhw))
            {
                if(menu) *menu = Toolbar[i].menu;
            }
        }

        if(leftpos) {
            x += 32;
            leftpos = false;
        } else {
            x -= 32;
            y -= 32;
            leftpos = true;
        }
    }

    if(paint) {
        // Do this last so that nothing can draw over it.
        if(toolTip.show) {
            char str[1024];
            if(strlen(toolTip.str) >= 200) oops();
            strcpy(str, toolTip.str);

            for(i = 0; SS.GW.menu[i].level >= 0; i++) {
                if(toolbarTooltipped == SS.GW.menu[i].id) {
                    int accel = SS.GW.menu[i].accel;
                    int ac = accel & 0xff;
                    if(isalnum(ac) || ac == '[') {
                        char *s = str+strlen(str);
                        if(accel & 0x100) {
                            sprintf(s, " (Shift+%c)", ac);
                        } else if((accel & ~0xff) == 0) {
                            sprintf(s, " (%c)", ac);
                        }
                    }
                    break;
                }
            }

            int tw, th;
            GetBitmapFontExtent(str, &tw, &th);
            tw += 10;
            th += 2;
            
            double ox = toolbarMouseX + 3, oy = toolbarMouseY + 3;
            glLineWidth(1);
            glColor4d(1.0, 1.0, 0.6, 1.0);
            glBegin(GL_QUADS);
                glVertex2d(ox, oy);
                glVertex2d(ox+tw, oy);
                glVertex2d(ox+tw, oy+th);
                glVertex2d(ox, oy+th);
            glEnd();
            glColor4d(0.0, 0.0, 0.0, 1.0);
            glBegin(GL_LINE_LOOP);
                glVertex2d(ox, oy);
                glVertex2d(ox+tw, oy);
                glVertex2d(ox+tw, oy+th);
                glVertex2d(ox, oy+th);
            glEnd();

            glColor4d(0, 0, 0, 1);
            glPushMatrix();
                glRasterPos2d(ox+6, oy+6);
                DrawWithBitmapFont(str);
            glPopMatrix();
        }
        glxDepthRangeLockToFront(false);
    }

    return withinToolbar;
}

void GraphicsWindow::TimerCallback(void) {
    SS.GW.toolbarTooltipped = SS.GW.toolbarHovered;
    PaintGraphics();
}

