/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#include "tools/sk_app/ios/Window_ios.h"

#include "tools/window/DisplayParams.h"
#include "tools/window/ios/WindowContextFactory_ios.h"

#if __has_feature(objc_arc)
#error "File should not be compiled with ARC."
#endif

@interface WindowViewController : UIViewController

- (WindowViewController*)initWithWindow:(sk_app::Window_ios*)initWindow;

@end

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

using sk_app::Window;
using skwindow::DisplayParams;

namespace sk_app {

Window_ios* Window_ios::gWindow = nullptr;

Window* Windows::CreateNativeWindow(void*) {
    // already have a window
    if (Window_ios::MainWindow()) {
        return nullptr;
    }

    Window_ios* window = new Window_ios();
    if (!window->initWindow()) {
        delete window;
        return nullptr;
    }

    return window;
}

bool Window_ios::initWindow() {
    // we already have a window
    if (fWindow) {
        return true;
    }

    // Create a view controller to track certain events
    WindowViewController* viewController = [[WindowViewController alloc] initWithWindow:this];
    if (nil == viewController) {
        return false;
    }

    fWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    if (nil == fWindow) {
        [viewController release];
        return false;
    }
    fWindow.backgroundColor = [UIColor whiteColor];

    viewController.view = nil;
    [fWindow setRootViewController:viewController];
    [fWindow makeKeyAndVisible];

    gWindow = this;

    return true;
}

void Window_ios::closeWindow() {
    if (nil != fWindow) {
        gWindow = nullptr;
        [fWindow release];
        fWindow = nil;
    }
}

bool Window_ios::attach(BackendType attachType) {
    this->initWindow();

    skwindow::IOSWindowInfo info;
    info.fWindow = this;
    info.fViewController = fWindow.rootViewController;
    switch (attachType) {
#ifdef SK_METAL
        case kMetal_BackendType:
            fWindowContext = skwindow::MakeMetalForIOS(info, fRequestedDisplayParams->clone());
            break;
#if defined(SK_GRAPHITE)
        case kGraphiteMetal_BackendType:
            fWindowContext = MakeGraphiteMetalForIOS(info, fRequestedDisplayParams->clone());
            break;
#endif
#endif
#ifdef SK_GL
        case kNativeGL_BackendType:
            fWindowContext = skwindow::MakeGLForIOS(info, fRequestedDisplayParams->clone());
            break;
        case kRaster_BackendType:
            fWindowContext = skwindow::MakeRasterForIOS(info, fRequestedDisplayParams->clone());
            break;
#endif
        default:
            SkASSERT_RELEASE(false);
    }
    this->onBackendCreated();

    return (SkToBool(fWindowContext));
}

void Window_ios::PaintWindow() {
    gWindow->onPaint();
}

void Window_ios::onInval() {
    // TODO: send expose event
}

}   // namespace sk_app

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

@implementation WindowViewController {
    sk_app::Window_ios* fWindow;
}

- (WindowViewController*)initWithWindow:(sk_app::Window_ios *)initWindow {
    self = [super initWithNibName:nil bundle:nil];
    if (self) {
        fWindow = initWindow;
    }
    return self;
}

- (void)viewDidLoad {
    // nothing yet
}

- (void)didReceiveMemoryWarning {
    // nothing yet
}

- (void)viewWillTransitionToSize:(CGSize)size
       withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    // handle rotations here
}
@end

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

@implementation MainView {
    sk_app::Window_ios* fWindow;
}

- (void)panGestureAction:(UIGestureRecognizer*)sender {
    CGPoint location = [sender locationInView:self];
    switch (sender.state) {
        case UIGestureRecognizerStateBegan:
            fWindow->onMouse(location.x, location.y,
                             skui::InputState::kDown, skui::ModifierKey::kNone);
            break;
        case UIGestureRecognizerStateChanged:
            fWindow->onMouse(location.x, location.y,
                             skui::InputState::kMove, skui::ModifierKey::kNone);
            break;
        case UIGestureRecognizerStateEnded:
            fWindow->onMouse(location.x, location.y,
                             skui::InputState::kUp, skui::ModifierKey::kNone);
            break;
        case UIGestureRecognizerStateCancelled:
            fWindow->onMouse(location.x, location.y,
                             skui::InputState::kUp, skui::ModifierKey::kNone);
            break;
        default:
            break;
    }
}

- (void)tapGestureAction:(UIGestureRecognizer*)sender {
    CGPoint location = [sender locationInView:self];
    switch (sender.state) {
        case UIGestureRecognizerStateEnded:
            fWindow->onMouse(location.x, location.y,
                             skui::InputState::kDown, skui::ModifierKey::kNone);
            fWindow->onMouse(location.x, location.y,
                             skui::InputState::kUp, skui::ModifierKey::kNone);
            break;
        default:
            break;
    }
}

- (void)pinchGestureAction:(UIGestureRecognizer*)sender {
    CGPoint location = [sender locationInView:self];
    UIPinchGestureRecognizer* pinchGestureRecognizer = (UIPinchGestureRecognizer*) sender;
    float scale = pinchGestureRecognizer.scale;
    switch (sender.state) {
        case UIGestureRecognizerStateBegan:
            fWindow->onPinch(skui::InputState::kDown, scale, location.x, location.y);
            break;
        case UIGestureRecognizerStateChanged:
            fWindow->onPinch(skui::InputState::kMove, scale, location.x, location.y);
            break;
        case UIGestureRecognizerStateEnded:
            fWindow->onPinch(skui::InputState::kUp, scale, location.x, location.y);
            break;
        case UIGestureRecognizerStateCancelled:
            fWindow->onPinch(skui::InputState::kUp, scale, location.x, location.y);
            break;
        default:
            break;
    }
}

- (void)swipeRightGestureAction:(UIGestureRecognizer*)sender {
    if (UIGestureRecognizerStateEnded == sender.state) {
        fWindow->onFling(skui::InputState::kRight);
    }
}

- (void)swipeLeftGestureAction:(UIGestureRecognizer*)sender {
    if (UIGestureRecognizerStateEnded == sender.state) {
        fWindow->onFling(skui::InputState::kLeft);
    }
}

- (MainView*)initWithWindow:(sk_app::Window_ios *)initWindow {
    self = [super init];

    UIPanGestureRecognizer* panGestureRecognizer = [[UIPanGestureRecognizer alloc] init];
    panGestureRecognizer.maximumNumberOfTouches = 1;
    [panGestureRecognizer addTarget:self action:@selector(panGestureAction:)];
    [self addGestureRecognizer:panGestureRecognizer];

    UITapGestureRecognizer* tapGestureRecognizer = [[UITapGestureRecognizer alloc] init];
    [tapGestureRecognizer addTarget:self action:@selector(tapGestureAction:)];
    [self addGestureRecognizer:tapGestureRecognizer];

    UIPinchGestureRecognizer* pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] init];
    [pinchGestureRecognizer addTarget:self action:@selector(pinchGestureAction:)];
    [self addGestureRecognizer:pinchGestureRecognizer];

    UISwipeGestureRecognizer* swipeRightGestureRecognizer = [[UISwipeGestureRecognizer alloc] init];
    swipeRightGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
    [swipeRightGestureRecognizer addTarget:self action:@selector(swipeRightGestureAction:)];
    [self addGestureRecognizer:swipeRightGestureRecognizer];

    UISwipeGestureRecognizer* swipeLeftGestureRecognizer = [[UISwipeGestureRecognizer alloc] init];
    swipeLeftGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    [swipeLeftGestureRecognizer addTarget:self action:@selector(swipeLeftGestureAction:)];
    [self addGestureRecognizer:swipeLeftGestureRecognizer];

    // disable pan recognition until swipes fail
    [panGestureRecognizer requireGestureRecognizerToFail:swipeLeftGestureRecognizer];
    [panGestureRecognizer requireGestureRecognizerToFail:swipeRightGestureRecognizer];

    fWindow = initWindow;

    return self;
}

@end

