/*
 * Copyright 2013 Google Inc.
 *
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 */
#include <v8.h>
#include <include/libplatform/libplatform.h>

#include "SkV8Example.h"
#include "Global.h"
#include "JsContext.h"
#include "Path2D.h"
#include "Path2DBuilder.h"

#include "gl/GrGLUtil.h"
#include "gl/GrGLDefines.h"
#include "gl/GrGLInterface.h"
#include "GrRenderTarget.h"
#include "GrContext.h"
#include "SkApplication.h"
#include "SkCommandLineFlags.h"
#include "SkData.h"
#include "SkDraw.h"
#include "SkGpuDevice.h"
#include "SkGraphics.h"
#include "SkScalar.h"
#include "SkSurface.h"


DEFINE_string2(infile, i, NULL, "Name of file to load JS from.\n");
DEFINE_bool(gpu, true, "Use the GPU for rendering.");

void application_init() {
    SkGraphics::Init();
    SkEvent::Init();
}

void application_term() {
    SkEvent::Term();
    SkGraphics::Term();
}

SkV8ExampleWindow::SkV8ExampleWindow(void* hwnd, JsContext* context)
    : INHERITED(hwnd)
    , fJsContext(context)
#if SK_SUPPORT_GPU
    , fCurContext(NULL)
    , fCurIntf(NULL)
    , fCurRenderTarget(NULL)
    , fCurSurface(NULL)
#endif
{
    this->setColorType(kBGRA_8888_SkColorType);
    this->setVisibleP(true);
    this->setClipToBounds(false);

#if SK_SUPPORT_GPU
    this->windowSizeChanged();
#endif
}

SkV8ExampleWindow::~SkV8ExampleWindow() {
#if SK_SUPPORT_GPU
    SkSafeUnref(fCurContext);
    SkSafeUnref(fCurIntf);
    SkSafeUnref(fCurRenderTarget);
    SkSafeUnref(fCurSurface);
#endif
}

#if SK_SUPPORT_GPU
void SkV8ExampleWindow::windowSizeChanged() {
    if (FLAGS_gpu) {
        SkOSWindow::AttachmentInfo attachmentInfo;
        bool result = this->attach(
                SkOSWindow::kNativeGL_BackEndType, 0, &attachmentInfo);
        if (!result) {
            printf("Failed to attach.");
            exit(1);
        }

        fCurIntf = GrGLCreateNativeInterface();
        fCurContext = GrContext::Create(
                kOpenGL_GrBackend, (GrBackendContext) fCurIntf);
        if (NULL == fCurIntf || NULL == fCurContext) {
            printf("Failed to initialize GL.");
            exit(1);
        }

        GrBackendRenderTargetDesc desc;
        desc.fWidth = SkScalarRoundToInt(this->width());
        desc.fHeight = SkScalarRoundToInt(this->height());
        desc.fConfig = kSkia8888_GrPixelConfig;
        desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
        desc.fSampleCnt = attachmentInfo.fSampleCount;
        desc.fStencilBits = attachmentInfo.fStencilBits;
        GrGLint buffer;
        GR_GL_GetIntegerv(fCurIntf, GR_GL_FRAMEBUFFER_BINDING, &buffer);
        desc.fRenderTargetHandle = buffer;

        SkSafeUnref(fCurRenderTarget);
        fCurRenderTarget = fCurContext->wrapBackendRenderTarget(desc);
        SkSafeUnref(fCurSurface);
        fCurSurface = SkSurface::NewRenderTargetDirect(fCurRenderTarget);
    }
}
#endif

#if SK_SUPPORT_GPU
SkSurface* SkV8ExampleWindow::createSurface() {
    if (FLAGS_gpu) {
        // Increase the ref count since callers of createSurface put the
        // results in a SkAutoTUnref.
        fCurSurface->ref();
        return fCurSurface;
    } else {
        return this->INHERITED::createSurface();
    }
}
#endif

void SkV8ExampleWindow::onSizeChange() {
    this->INHERITED::onSizeChange();

#if SK_SUPPORT_GPU
    this->windowSizeChanged();
#endif
}

Global* global = NULL;

void SkV8ExampleWindow::onDraw(SkCanvas* canvas) {

    canvas->save();
    canvas->drawColor(SK_ColorWHITE);

    // Now jump into JS and call the onDraw(canvas) method defined there.
    fJsContext->onDraw(canvas);

    canvas->restore();

    this->INHERITED::onDraw(canvas);

#if SK_SUPPORT_GPU
    if (FLAGS_gpu) {
        fCurContext->flush();
        this->present();
    }
#endif
}

#ifdef SK_BUILD_FOR_WIN
void SkV8ExampleWindow::onHandleInval(const SkIRect& rect) {
    RECT winRect;
    winRect.top = rect.top();
    winRect.bottom = rect.bottom();
    winRect.right = rect.right();
    winRect.left = rect.left();
    InvalidateRect((HWND)this->getHWND(), &winRect, false);
}
#endif


SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) {
    printf("Started\n");

    v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
    SkCommandLineFlags::Parse(argc, argv);

    v8::V8::InitializeICU();
    v8::Platform* platform = v8::platform::CreateDefaultPlatform();
    v8::V8::InitializePlatform(platform);
    v8::V8::Initialize();

    v8::Isolate* isolate = v8::Isolate::New();
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);
    isolate->Enter();

    global = new Global(isolate);


    // Set up things to look like a browser by creating
    // a console object that invokes our print function.
    const char* startupScript =
            "function Console() {};                   \n"
            "Console.prototype.log = function() {     \n"
            "  var args = Array.prototype.slice.call(arguments).join(' '); \n"
            "  print(args);                      \n"
            "};                                       \n"
            "console = new Console();                 \n";

    if (!global->parseScript(startupScript)) {
        printf("Failed to parse startup script: %s.\n", FLAGS_infile[0]);
        exit(1);
    }

    const char* script =
            "function onDraw(canvas) {              \n"
            "    canvas.fillStyle = '#00FF00';      \n"
            "    canvas.fillRect(20, 20, 100, 100); \n"
            "    canvas.inval();                    \n"
            "}                                      \n";

    SkAutoTUnref<SkData> data;
    if (FLAGS_infile.count()) {
        data.reset(SkData::NewFromFileName(FLAGS_infile[0]));
        script = static_cast<const char*>(data->data());
    }
    if (NULL == script) {
        printf("Could not load file: %s.\n", FLAGS_infile[0]);
        exit(1);
    }
    Path2DBuilder::AddToGlobal(global);
    Path2D::AddToGlobal(global);

    if (!global->parseScript(script)) {
        printf("Failed to parse file: %s.\n", FLAGS_infile[0]);
        exit(1);
    }


    JsContext* jsContext = new JsContext(global);

    if (!jsContext->initialize()) {
        printf("Failed to initialize.\n");
        exit(1);
    }
    SkV8ExampleWindow* win = new SkV8ExampleWindow(hwnd, jsContext);
    global->setWindow(win);

    return win;
}