//=============================================================================================================================
//
// 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 "RecorderRenderer.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/texture.oc.h>
#import <easyar/recorder.oc.h>
#import <easyar/recorder_configuration.oc.h>
#import <easyar/callbackscheduler.oc.h>
#import <easyar/dataflow.oc.h>
#import <easyar/cameraparameters.oc.h>

#include <OpenGLES/ES2/gl.h>

easyar_DelayedCallbackScheduler * scheduler;
easyar_CameraDevice * camera;
BGRenderer * bgRenderer;
RecorderRenderer * recorder_renderer;
easyar_Recorder * recorder;
int view_size[] = {0, 0};
bool recording_started = false;
easyar_InputFrameThrottler * throttler;
easyar_InputFrameToOutputFrameAdapter * i2OAdapter;
easyar_OutputFrameBuffer * outputFrameBuffer;
int previousInputFrameIndex = -1;

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

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

void initialize()
{
    recreate_context();

    throttler = [easyar_InputFrameThrottler create];
    i2OAdapter = [easyar_InputFrameToOutputFrameAdapter create];
    outputFrameBuffer = [easyar_OutputFrameBuffer create];

    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]]];

    [[camera inputFrameSource] connect:[throttler input]];
    [[throttler output] connect:[i2OAdapter input]];
    [[i2OAdapter output] connect:[outputFrameBuffer input]];
    [[outputFrameBuffer signalOutput] connect:[throttler signalInput]];

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

void finalize()
{
    recorder = nil;
    recorder_renderer = nil;
    bgRenderer = nil;
    camera = nil;
    scheduler = nil;
}

BOOL start()
{
    bool status = true;
    if (camera != nil) {
        status &= [camera start];
    } else {
        status = NO;
    }
    return status;
}

void stop()
{
    if (camera != nil) {
        [camera stop];
    }
}

void render(int width, int height, int screenRotation, void (^rebindFrameBuffer)())
{
    view_size[0] = width;
    view_size[1] = height;

    while ([scheduler runOne])
    {
    }

    glViewport(0, 0, width, height);
    glClearColor(0.f, 0.f, 0.f, 1.f);

    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 * 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];
    }

    if (recording_started) {
        [recorder_renderer preRender];
        [bgRenderer render:imageProjection];
        rebindFrameBuffer();
        [recorder_renderer postRender];
        [recorder updateFrame:[easyar_TextureId fromInt:[recorder_renderer getTextureId]] width:view_size[0] height:view_size[1]];
    } else {
        [bgRenderer render:imageProjection];
    }
}

void requestPermissions(void(^ callback)(easyar_PermissionStatus status, NSString* msg))
{
    [easyar_Recorder requestPermissions:[easyar_ImmediateCallbackScheduler getDefault] permissionCallback:callback];
}

void startRecording(NSString * path, void(^ callback)(easyar_RecordStatus status, NSString *value))
{
    if (recording_started) { return; }
    easyar_RecorderConfiguration * conf = [easyar_RecorderConfiguration create];

    [conf setOutputFile:path];
    [conf setZoomMode:easyar_RecordZoomMode_ZoomInWithAllContent];
    [conf setProfile:easyar_RecordProfile_Quality_720P_Middle];
    easyar_RecordVideoOrientation mode = view_size[0] < view_size[1]? easyar_RecordVideoOrientation_Portrait :easyar_RecordVideoOrientation_Landscape;
    [conf setVideoOrientation:(mode)];

    recorder = [easyar_Recorder create:conf callbackScheduler:scheduler statusCallback:^(easyar_RecordStatus status, NSString *value) {
        if (status == easyar_RecordStatus_OnStopped) {
            recording_started = false;
        }
        callback(status, value);
    }];
    [recorder_renderer resize:view_size[0] height:view_size[1]];
    [recorder start];
    recording_started = true;
}

void stopRecording()
{
    if (!recording_started) { return; }
    [recorder stop];
    recorder = nil;
    recording_started = false;
}
