#include "xshaperectangle.hpp"

slop::XShapeRectangle::XShapeRectangle( glm::vec2 p1, glm::vec2 p2, float border, float padding, glm::vec4 color, bool highlight ) {
    this->color = convertColor( color );
    this->border = border;
    this->padding = padding;
    this->highlight = highlight;
    this->alpha = color.a;
    // Find each corner of the rectangle
    ul = glm::vec2( glm::min( p1.x, p2.x ), glm::max( p1.y, p2.y ) ) ;
    bl = glm::vec2( glm::min( p1.x, p2.x ), glm::min( p1.y, p2.y ) ) ;
    ur = glm::vec2( glm::max( p1.x, p2.x ), glm::max( p1.y, p2.y ) ) ;
    br = glm::vec2( glm::max( p1.x, p2.x ), glm::min( p1.y, p2.y ) ) ;
    // Offset the inner corners by the padding.
    ul = ul + glm::vec2(-padding,padding);
    bl = bl + glm::vec2(-padding,-padding);
    ur = ur + glm::vec2(padding,padding);
    br = br + glm::vec2(padding,-padding);
    // Create the outer corners by offsetting the inner by the bordersize
    oul = ul + glm::vec2(-border,border);
    obl = bl + glm::vec2(-border,-border);
    our = ur + glm::vec2(border,border);
    obr = br + glm::vec2(border,-border);

    XSetWindowAttributes attributes;
    // Set up the window so it's our color 
    attributes.background_pixel = this->color.pixel;
    // Disable window decorations.
    attributes.override_redirect = True;
    // Make sure we know when we've been successfully destroyed later!
    attributes.event_mask = StructureNotifyMask;
    unsigned long valueMask = CWBackPixel | CWOverrideRedirect | CWEventMask;
    // Create the window
    window = XCreateWindow( x11->display, x11->root, 0, 0, WidthOfScreen( x11->screen ), HeightOfScreen( x11->screen ),
                              0, CopyFromParent, InputOutput,
                              CopyFromParent, valueMask, &attributes );
    if ( alpha < 1 ) {
        // Change the window opacity
        unsigned int cardinal_alpha = (unsigned int) (alpha * (unsigned int)-1) ;
        XChangeProperty( x11->display, window, XInternAtom( x11->display, "_NET_WM_WINDOW_OPACITY", 0),
                         XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&cardinal_alpha, 1 );
    }
    // Set the class hint, and title to "slop"
    XClassHint classhints;
    char name[] = "slop";
    classhints.res_name = name;
    classhints.res_class = name;
    XSetClassHint( x11->display, window, &classhints );
    // Now punch a hole into it so it looks like a selection rectangle, but only if we're not highlighting.
    generateHoles();
    createdWindow = false;
}

void slop::XShapeRectangle::createWindow() {
    if ( createdWindow ) {
        return;
    }
    XMapWindow( x11->display, window );
    createdWindow = true;
}

void slop::XShapeRectangle::generateHoles() {
    if ( !highlight ) {
        XRectangle rects[4];
        // Left
        rects[0].x = oul.x;
        rects[0].y = obl.y;
        rects[0].width = border;
        rects[0].height = oul.y-obl.y;
        // Top
        rects[1].x = ul.x;
        rects[1].y = obl.y;
        rects[1].width = ur.x-ul.x;
        rects[1].height = border;
        // Right
        rects[2].x = ur.x;
        rects[2].y = obr.y;
        rects[2].width = border;
        rects[2].height = our.y - obr.y;
        // Bottom
        rects[3].x = bl.x;
        rects[3].y = ul.y;
        rects[3].width = br.x-bl.x;
        rects[3].height = border;
        XShapeCombineRectangles( x11->display, window, ShapeBounding, 0, 0, rects, 4, ShapeSet, 0);
        return;
    }
    XRectangle rect;
    rect.x = oul.x;
    rect.y = obl.y;
    rect.width = our.x-oul.x;
    rect.height = oul.y-obl.y;
    XShapeCombineRectangles( x11->display, window, ShapeBounding, 0, 0, &rect, 1, ShapeSet, 0);
}

void slop::XShapeRectangle::setPoints( glm::vec2 p1, glm::vec2 p2 ) {
    // Find each corner of the rectangle
    ul = glm::vec2( glm::min( p1.x, p2.x ), glm::max( p1.y, p2.y ) ) ;
    bl = glm::vec2( glm::min( p1.x, p2.x ), glm::min( p1.y, p2.y ) ) ;
    ur = glm::vec2( glm::max( p1.x, p2.x ), glm::max( p1.y, p2.y ) ) ;
    br = glm::vec2( glm::max( p1.x, p2.x ), glm::min( p1.y, p2.y ) ) ;
    // Offset the inner corners by the padding.
    ul = ul + glm::vec2(-padding,padding);
    bl = bl + glm::vec2(-padding,-padding);
    ur = ur + glm::vec2(padding,padding);
    br = br + glm::vec2(padding,-padding);
    // Create the outer corners by offsetting the inner by the bordersize
    oul = ul + glm::vec2(-border,border);
    obl = bl + glm::vec2(-border,-border);
    our = ur + glm::vec2(border,border);
    obr = br + glm::vec2(border,-border);
    generateHoles();
}

slop::XShapeRectangle::~XShapeRectangle() {
    XUnmapWindow( x11->display, window );
    XDestroyWindow( x11->display, window );
}

void slop::XShapeRectangle::draw( glm::mat4& matrix ) {
    // We don't want to be visible until we're asked to draw.
    createWindow();
}

glm::vec4 slop::XShapeRectangle::getRect() {
    return glm::vec4( bl.x, bl.y, ur.x-ul.x, ul.y-bl.y );
}

XColor slop::XShapeRectangle::convertColor( glm::vec4 color ) {
    // Convert float colors to shorts.
    short red   = short( floor( color.r * 65535.f ) );
    short green = short( floor( color.g * 65535.f ) );
    short blue  = short( floor( color.b * 65535.f ) );
    XColor xc;
    xc.red = red;
    xc.green = green;
    xc.blue = blue;
    int err = XAllocColor( x11->display, DefaultColormap( x11->display, XScreenNumberOfScreen( x11->screen ) ), &xc );
    if ( err == BadColor ) {
        throw std::runtime_error(std::string("Couldn't allocate a color"));
    }
    return xc;
}
