//=============================================================================================================================
//
// Copyright (c) 2015-2021 VisionStar Information Technology (Shanghai) Co., Ltd. All Rights Reserved.
// EasyAR is the registered trademark or trademark of VisionStar Information Technology (Shanghai) Co., Ltd in China
// and other countries for the augmented reality technology developed by VisionStar Information Technology (Shanghai) Co., Ltd.
//
//=============================================================================================================================

#import "helloar.h"

#import "BoxRenderer.h"
#import "BGRenderer.h"

#import <easyar/types.oc.h>
#import <easyar/buffer.oc.h>
#import <easyar/camera.oc.h>
#import <easyar/frame.oc.h>
#import <easyar/objecttracker.oc.h>
#import <easyar/objecttarget.oc.h>
#import <easyar/dataflow.oc.h>
#import <easyar/cameraparameters.oc.h>

#include <OpenGLES/ES2/gl.h>

easyar_DelayedCallbackScheduler * scheduler;
easyar_CameraDevice * camera;
NSMutableArray<easyar_ObjectTracker *> * trackers;
BGRenderer * bgRenderer;
BoxRenderer * boxRenderer;
easyar_InputFrameThrottler * throttler;
easyar_FeedbackFrameFork * feedBackFrameFork;
easyar_InputFrameToOutputFrameAdapter * i2OAdapter;
easyar_InputFrameFork * inputFrameFork;
easyar_OutputFrameJoin * join;
easyar_OutputFrameBuffer * outputFrameBuffer;
easyar_InputFrameToFeedbackFrameAdapter * i2FAdapter;
easyar_OutputFrameFork * outputFrameFork;
int previousInputFrameIndex = -1;

void createScheduler()
{
    scheduler = [easyar_DelayedCallbackScheduler create];
}
easyar_DelayedCallbackScheduler * getScheduler()
{
    return scheduler;
}

void recreate_context()
{
    bgRenderer = nil;
    boxRenderer = nil;
    previousInputFrameIndex = -1;
    bgRenderer = [BGRenderer create];
    boxRenderer = [BoxRenderer create];
}

void initialize()
{
    recreate_context();

    throttler = [easyar_InputFrameThrottler create];
    inputFrameFork = [easyar_InputFrameFork create:2];
    outputFrameBuffer = [easyar_OutputFrameBuffer create];
    i2OAdapter = [easyar_InputFrameToOutputFrameAdapter create];
    i2FAdapter = [easyar_InputFrameToFeedbackFrameAdapter create];
    outputFrameFork = [easyar_OutputFrameFork create:2];
    
    camera = [easyar_CameraDeviceSelector createCameraDevice: easyar_CameraDevicePreference_PreferObjectSensing];
    if (![camera openWithPreferredType:easyar_CameraDeviceType_Back]) { return; }
    [camera setFocusMode:easyar_CameraDeviceFocusMode_Continousauto];
    [camera setSize:[easyar_Vec2I create:@[@1280, @960]]];

    easyar_ObjectTracker * tracker = [easyar_ObjectTracker create];
    easyar_ObjectTarget * target = [easyar_ObjectTarget createFromObjectFile:@"hexagon.obj" storageType:easyar_StorageType_Assets name:@"" uid:@"" meta:@"" scale:1.0f];
    [tracker loadTarget:target callbackScheduler:scheduler callback:^(easyar_Target * target, bool status) {
        NSLog(@"load target (%d): %@ (%d)", status, [target name], [target runtimeID]);
    }];
    trackers = [[NSMutableArray<easyar_ObjectTracker *> alloc] init];
    [trackers addObject:tracker];
    feedBackFrameFork = [easyar_FeedbackFrameFork create:(int)[trackers count]];
    join = [easyar_OutputFrameJoin create:(int)(trackers.count + 1)];
    [[camera inputFrameSource] connect:[throttler input]];
    [[throttler output] connect:[inputFrameFork input]];
    [[inputFrameFork output:0] connect:[i2OAdapter input]];
    [[i2OAdapter output] connect:[join input:0]];
    
    [[inputFrameFork output:1] connect:[i2FAdapter input]];
    [[i2FAdapter output] connect:[feedBackFrameFork input]];
    int trackerBufferRequirement = 0;
    for (int i = 0; i < trackers.count; i++) {
        [[feedBackFrameFork output:i] connect:trackers[i].feedbackFrameSink];
        [[trackers[i] outputFrameSource] connect:[join input:i+1]];
        trackerBufferRequirement += [trackers[i] bufferRequirement];
    }
    
    [[join output] connect:[outputFrameFork input]];
    [[outputFrameFork output:0] connect:[outputFrameBuffer input]];
    [[outputFrameFork output:1] connect:[i2FAdapter sideInput]];
    [[outputFrameBuffer signalOutput] connect:[throttler signalInput]];

    //CameraDevice and rendering each require an additional buffer
    [camera setBufferCapacity: [throttler bufferRequirement] + [i2FAdapter bufferRequirement] + [outputFrameBuffer bufferRequirement] + trackerBufferRequirement + 2];
}

void finalize()
{
    [trackers removeAllObjects];
    boxRenderer = nil;
    bgRenderer = nil;
    camera = nil;
    throttler = nil;
    inputFrameFork = nil;
    outputFrameBuffer = nil;
    i2OAdapter = nil;
    i2FAdapter = nil;
    outputFrameFork = nil;
    scheduler = nil;
}

BOOL start()
{
    BOOL status = YES;
    if (camera != nil) {
        status &= [camera start];
    } else {
        status = NO;
    }
    for (easyar_ObjectTracker * tracker in trackers) {
        status &= [tracker start];
    }
    return status;
}

void stop()
{
    for (easyar_ObjectTracker * tracker in trackers) {
        [tracker stop];
    }
    if (camera != nil) {
        [camera stop];
    }
}

void render(int width, int height, int screenRotation)
{
    while ([scheduler runOne])
    {
    }

    glViewport(0, 0, width, height);
    glClearColor(0.f, 0.f, 0.f, 1.f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    easyar_OutputFrame * oFrame = [outputFrameBuffer peek];
    if (oFrame == nil){ return; }
    easyar_InputFrame * iFrame = [oFrame inputFrame];
    if (iFrame == nil) { return; }
    if (![iFrame hasCameraParameters]) { return; }
    easyar_CameraParameters * cameraParameters = [iFrame cameraParameters];
    float viewport_aspect_ratio = (float)width / (float)height;
    easyar_Matrix44F * projection = [cameraParameters projection:0.01f farPlane:1000.f viewportAspectRatio:viewport_aspect_ratio screenRotation:screenRotation combiningFlip:true manualHorizontalFlip:false];
    easyar_Matrix44F * imageProjection = [cameraParameters imageProjection:viewport_aspect_ratio screenRotation:screenRotation combiningFlip:true manualHorizontalFlip:false];
    easyar_Image * image = [iFrame image];

    if ([iFrame index] != previousInputFrameIndex) {
        [bgRenderer upload:[image format] width:[image width] height:[image height] bufferData:[[image buffer] data]];
        previousInputFrameIndex = [iFrame index];
    }
    [bgRenderer render:imageProjection];

    NSArray<easyar_FrameFilterResult *> * results = [oFrame results];
    
    for (int i = 0; i < [results count]; i++) {
        easyar_ObjectTrackerResult * result;
        easyar_FrameFilterResult * _result = results[i];
        result = ([_result isEqual:[NSNull null]])? nil: (easyar_ObjectTrackerResult * )_result;
        if (result != nil) {
            for (easyar_TargetInstance * targetInstance in [result targetInstances]) {
                easyar_TargetStatus status = [targetInstance status];
                if (status == easyar_TargetStatus_Tracked) {
                    easyar_Target * target = [targetInstance target];
                    easyar_ObjectTarget * objecttarget = [target isKindOfClass:[easyar_ObjectTarget class]] ? (easyar_ObjectTarget *)target : nil;
                    if (objecttarget == nil) {
                        continue;
                    }
                    [boxRenderer renderBox:projection cameraview:[targetInstance pose] box:[objecttarget boundingBox]];
                }
            }
        }
    }
}
