/*
 * DemoViewController.m
 *
 * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#import "DemoViewController.h"
#import <QuartzCore/CAMetalLayer.h>

#include <MoltenVK/mvk_vulkan.h>

#include "cube.cpp"

#pragma mark -
#pragma mark DemoViewController

@implementation DemoViewController {
    CVDisplayLinkRef _displayLink;
    struct Demo demo;
    NSTimer* _timer;
}

- (void)dealloc {
    demo.cleanup();
    CVDisplayLinkRelease(_displayLink);
    [super dealloc];
}

/** Since this is a single-view app, initialize Vulkan during view loading. */
- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.wantsLayer = YES;  // Back the view with a layer created by the makeBackingLayer method.

    // Convert incoming args to "C" argc/argv strings
    NSArray *args = [[NSProcessInfo processInfo] arguments];
    const char** argv = (const char**) alloca(sizeof(char*) * args.count);
    for(unsigned int i = 0; i < args.count; i++) {
        NSString *s = args[i];
        argv[i] = s.UTF8String;
    }

    demo_main(demo, self.view.layer, args.count, argv);

    // Monitor the rendering loop for a quit condition
    _timer = [NSTimer scheduledTimerWithTimeInterval: 0.2
                                              target: self
                                            selector: @selector(onTick:)
                                            userInfo: self
                                             repeats: YES];

    // Start the rendering loop
    CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
    CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, &demo);
    CVDisplayLinkStart(_displayLink);
}

// Close the window if the demo is in a Quit state
-(void)onTick:(NSTimer*)timer {
    if (demo.quit) {
        [[[self view] window] close];
    }
}

#pragma mark Display loop callback function

/** Rendering loop callback function for use with a CVDisplayLink. */
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now,
                                    const CVTimeStamp* outputTime,
                                    CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* target) {
    struct Demo* demo = (struct Demo*)target;
    demo->run();
    if (demo->quit) {
        CVDisplayLinkStop(displayLink);
        CVDisplayLinkRelease(displayLink);
    }
    return kCVReturnSuccess;
}

@end

#pragma mark -
#pragma mark DemoView

@implementation DemoView

/** Indicates that the view wants to draw using the backing layer instead of using drawRect:.  */
- (BOOL)wantsUpdateLayer {
    return YES;
}

/** Returns a Metal-compatible layer. */
+ (Class)layerClass {
    return [CAMetalLayer class];
}

/** If the wantsLayer property is set to YES, this method will be invoked to return a layer instance. */
- (CALayer*)makeBackingLayer {
    CALayer* layer = [self.class.layerClass layer];
    CGSize viewScale = [self convertSizeToBacking:CGSizeMake(1.0, 1.0)];
    layer.contentsScale = MIN(viewScale.width, viewScale.height);
    return layer;
}

@end
