#include "button.h"

#include <stdio.h>
#include <stdlib.h>

#define ERROR(...) fprintf(stderr, __VA_ARGS__)

Button *buttonNew(const char *text, float x, float y, float w, float h,
                  SDL_Texture *inactive, SDL_Texture *active,
                  SDL_Color textColor, TTF_Font *font, ButtonSoundSet sounds,
                  const Window *window) {
    Button *button = malloc(sizeof(Button));
    if (!button) {
        ERROR("error: out of memory\n");
        exit(EXIT_FAILURE);
    }
    if (text) {
        button->text = strdup(text);
        if (!text) {
            ERROR("error: out of memory\n");
            free(button);
            exit(EXIT_FAILURE);
        }
    } else {
        button->text = NULL;
    }
    button->x = x;
    button->y = y;
    button->w = w;
    button->h = h;
    button->inactive = inactive;
    button->active = active;
    button->current = inactive;
    button->font = font;
    button->textColor = textColor;
    button->sounds = sounds;
    button->action = NULL;
    button->renderedText = NULL;
    button->down = 0;
    buttonUpdate(button, window);
    return button;
}

void buttonDelete(Button *button) {
    if (button->text && strlen(button->text)) {
        free(button->text);
        SDL_DestroyTexture(button->renderedText);
    }
    free(button);
}

void buttonDraw(const Button *button, const Window *window) {
    float bw = button->w;
    float bh = button->h;
    int ww, wh;
    SDL_GetWindowSize(window->obj, &ww, &wh);
    if (button->w == 0 && button->h == 0) {
        bh = (float)button->textW / ww;
        bw = (float)button->textH / wh;
    } else if (button->w == 0) {
        bw = (((wh * button->h) * button->textH) / button->textW) / ww;
    } else if (button->h == 0) {
        bh = (((ww * button->w) * button->textH) / button->textW) / wh;
    }
    SDL_Rect backArea = {ww * button->x, wh * button->y, ww * bw, wh * bh};
    SDL_RenderCopy(window->render, button->current, NULL, &backArea);
    if (button->text && strlen(button->text)) {
        SDL_Rect textArea = {backArea.x * (1.0f + BUTTON_PADDING),
                             backArea.y * (1.0f + BUTTON_PADDING),
                             backArea.w * (1.0f - (2 * BUTTON_PADDING)),
                             backArea.h * (1.0f - (2 * BUTTON_PADDING))};
        SDL_RenderCopy(window->render, button->renderedText, NULL, &textArea);
    }
}

void buttonUpdate(Button *button, const Window *window) {
    if (button->renderedText) {
        SDL_DestroyTexture(button->renderedText);
    }
    if (button->text && strlen(button->text)) {
        SDL_Surface *surface = TTF_RenderText_Blended(
            button->font, button->text, button->textColor);
        if (!surface) {
            ERROR("error: sdl2: ttf: %s\n", TTF_GetError());
            exit(EXIT_FAILURE);
        }
        button->textW = surface->w;
        button->textH = surface->h;
        button->renderedText =
            SDL_CreateTextureFromSurface(window->render, surface);
        SDL_FreeSurface(surface);
        if (!button->renderedText) {
            ERROR("error: sdl2: %s\n", SDL_GetError());
            exit(EXIT_FAILURE);
        }
    }
}

static int intersectsButton(const Button *button, float bw, float bh, float x,
                            float y) {
    return x >= button->x && y >= button->y && x <= button->x + bw &&
           y <= button->y + bh;
}

void buttonProcessInput(Button *button, Window *window,
                        const SDL_Event *event) {
    int ww, wh;
    SDL_GetWindowSize(window->obj, &ww, &wh);
    float bw = button->w;
    float bh = button->h;
    if (button->w == 0 && button->h == 0) {
        bh = (float)button->textW / ww;
        bw = (float)button->textH / wh;
    } else if (button->w == 0) {
        bw = (((wh * button->h) * button->textH) / button->textW) / ww;
    } else if (button->h == 0) {
        bh = (((ww * button->w) * button->textH) / button->textW) / wh;
    }
    if (event->type == SDL_MOUSEMOTION) {
        float x = (float)event->motion.x / ww;
        float y = (float)event->motion.y / wh;
        if (intersectsButton(button, bw, bh, x, y)) {
            if (button->active && button->current != button->active) {
                if (button->sounds.hover) {
                    Mix_PlayChannel(-1, button->sounds.hover, 0);
                }
                button->current = button->active;
            }
        } else {
            if (button->current != button->inactive) {
                if (button->sounds.hover) {
                    Mix_PlayChannel(-1, button->sounds.hover, 0);
                }
                button->current = button->inactive;
            }
            if (button->down) {
                button->down = 0;
            }
        }
    } else if (event->type == SDL_MOUSEBUTTONDOWN &&
               event->button.button == SDL_BUTTON_LEFT) {
        float x = (float)event->button.x / ww;
        float y = (float)event->button.y / wh;
        if (intersectsButton(button, bw, bh, x, y)) {
            button->down = 1;
            if (button->sounds.down) {
                Mix_PlayChannel(-1, button->sounds.down, 0);
            }
        }
    } else if (event->type == SDL_MOUSEBUTTONUP &&
               event->button.button == SDL_BUTTON_LEFT) {
        float x = (float)event->button.x / ww;
        float y = (float)event->button.y / wh;
        if (intersectsButton(button, bw, bh, x, y) && button->down) {
            button->down = 0;
            if (button->sounds.up) {
                Mix_PlayChannel(-1, button->sounds.up, 0);
            }
            if (button->action) {
                button->action(button, window, x - button->x, y - button->y);
            }
            button->current = button->inactive;
        }
    }
}

void buttonGetCenter(const Button *button, Point *point) {
    point->x = button->x + (button->w / 2);
    point->y = button->y + (button->w / 2);
}
