
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkOSWindow_SDL.h"
#include "SkCanvas.h"
#include "SkColorPriv.h"
#include "SkGLCanvas.h"
#include "SkOSMenu.h"
#include "SkTime.h"

static void post_SkEvent_event() {
    SDL_Event evt;
    evt.type = SDL_USEREVENT;
    evt.user.type = SDL_USEREVENT;
    evt.user.code = 0;
    evt.user.data1 = nullptr;
    evt.user.data2 = nullptr;
    SDL_PushEvent(&evt);
}

static bool skia_setBitmapFromSurface(SkBitmap* dst, SDL_Surface* src) {
    SkColorType ct;
    SkAlphaType at;

    switch (src->format->BytesPerPixel) {
        case 2:
            ct = kRGB_565_SkColorType;
            at = kOpaque_SkAlphaType;
            break;
        case 4:
            ct = kN32_SkColorType;
            at = kPremul_SkAlphaType;
            break;
        default:
            return false;
    }

    return dst->installPixels(SkImageInfo::Make(src->w, src->h, ct, at), src->pixels, src->pitch);
}

SkOSWindow::SkOSWindow(void* screen) {
    fScreen = reinterpret_cast<SDL_Surface*>(screen);
    this->resize(fScreen->w, fScreen->h);

    uint32_t rmask = SK_R32_MASK << SK_R32_SHIFT;
    uint32_t gmask = SK_G32_MASK << SK_G32_SHIFT;
    uint32_t bmask = SK_B32_MASK << SK_B32_SHIFT;
    uint32_t amask = SK_A32_MASK << SK_A32_SHIFT;

    if (fScreen->flags & SDL_OPENGL) {
        fSurface = nullptr;
        fGLCanvas = new SkGLCanvas;
        fGLCanvas->setViewport(fScreen->w, fScreen->h);
    } else {
        fGLCanvas = nullptr;
        fSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, fScreen->w, fScreen->h,
                                        32, rmask, gmask, bmask, amask);
    }
}

SkOSWindow::~SkOSWindow() {
    delete fGLCanvas;
    if (fSurface) {
        SDL_FreeSurface(fSurface);
    }
}

#include <OpenGL/gl.h>

void SkOSWindow::doDraw() {
    if (fGLCanvas) {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
        glEnable(GL_TEXTURE_2D);
        glClearColor(0, 0, 0, 0);
        glClear(GL_COLOR_BUFFER_BIT);

        int count = fGLCanvas->save();
        this->draw(fGLCanvas);
        fGLCanvas->restoreToCount(count);
        SDL_GL_SwapBuffers( );
    } else {
        if ( SDL_MUSTLOCK(fSurface) ) {
            if ( SDL_LockSurface(fSurface) < 0 ) {
                return;
            }
        }

        SkBitmap bitmap;

        if (skia_setBitmapFromSurface(&bitmap, fSurface)) {
            SkCanvas canvas(bitmap);
            this->draw(&canvas);
        }

        if ( SDL_MUSTLOCK(fSurface) ) {
            SDL_UnlockSurface(fSurface);
        }

        int result = SDL_BlitSurface(fSurface, nullptr, fScreen, nullptr);
        if (result) {
            SkDebugf("------- SDL_BlitSurface returned %d\n", result);
        }
        SDL_UpdateRect(fScreen, 0, 0, fScreen->w, fScreen->h);
    }
}

static SkKey find_skkey(SDLKey src) {
    // this array must match the enum order in SkKey.h
    static const SDLKey gKeys[] = {
        SDLK_UNKNOWN,
        SDLK_UNKNOWN,   // left softkey
        SDLK_UNKNOWN,   // right softkey
        SDLK_UNKNOWN,   // home
        SDLK_UNKNOWN,   // back
        SDLK_UNKNOWN,   // send
        SDLK_UNKNOWN,   // end
        SDLK_0,
        SDLK_1,
        SDLK_2,
        SDLK_3,
        SDLK_4,
        SDLK_5,
        SDLK_6,
        SDLK_7,
        SDLK_8,
        SDLK_9,
        SDLK_ASTERISK,
        SDLK_HASH,
        SDLK_UP,
        SDLK_DOWN,
        SDLK_LEFT,
        SDLK_RIGHT,
        SDLK_RETURN,    // OK
        SDLK_UNKNOWN,   // volume up
        SDLK_UNKNOWN,   // volume down
        SDLK_UNKNOWN,   // power
        SDLK_UNKNOWN,   // camera
    };

    const SDLKey* array = gKeys;
    for (size_t i = 0; i < SK_ARRAY_COUNT(gKeys); i++) {
        if (array[i] == src) {
            return static_cast<SkKey>(i);
        }
    }
    return kNONE_SkKey;
}

void SkOSWindow::handleSDLEvent(const SDL_Event& event) {
    switch (event.type) {
        case SDL_VIDEORESIZE:
            this->resize(event.resize.w, event.resize.h);
            break;
        case SDL_VIDEOEXPOSE:
            this->doDraw();
            break;
        case SDL_MOUSEMOTION:
            if (event.motion.state == SDL_PRESSED) {
                this->handleClick(event.motion.x, event.motion.y,
                                   SkView::Click::kMoved_State);
            }
            break;
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONUP:
            this->handleClick(event.button.x, event.button.y,
                               event.button.state == SDL_PRESSED ?
                               SkView::Click::kDown_State :
                               SkView::Click::kUp_State);
            break;
        case SDL_KEYDOWN: {
            SkKey sk = find_skkey(event.key.keysym.sym);
            if (kNONE_SkKey != sk) {
                if (event.key.state == SDL_PRESSED) {
                    this->handleKey(sk);
                } else {
                    this->handleKeyUp(sk);
                }
            }
            break;
        }
        case SDL_USEREVENT:
            if (SkEvent::ProcessEvent()) {
                post_SkEvent_event();
            }
            break;
    }
}

void SkOSWindow::onHandleInval(const SkIRect& r) {
    SDL_Event evt;
    evt.type = SDL_VIDEOEXPOSE;
    evt.expose.type = SDL_VIDEOEXPOSE;
    SDL_PushEvent(&evt);
}

void SkOSWindow::onSetTitle(const char title[]) {
    SDL_WM_SetCaption(title, nullptr);
}

void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) {}

///////////////////////////////////////////////////////////////////////////////////////

void SkEvent::SignalNonEmptyQueue() {
    SkDebugf("-------- signal nonempty\n");
    post_SkEvent_event();
}

static Uint32 timer_callback(Uint32 interval) {
//    SkDebugf("-------- timercallback %d\n", interval);
    SkEvent::ServiceQueueTimer();
    return 0;
}

void SkEvent::SignalQueueTimer(SkMSec delay)
{
    SDL_SetTimer(0, nullptr);
    if (delay) {
        SDL_SetTimer(delay, timer_callback);
    }
}
