// Copyright (C) 2014 Patryk Nadrowski
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in Irrlicht.h

#include "CNSOGLManager.h"

#ifdef _IRR_COMPILE_WITH_NSOGL_MANAGER_

#include "os.h"

namespace irr
{
namespace video
{

CNSOGLManager::CNSOGLManager()
	: PrimaryContext(SExposedVideoData(0)), PixelFormat(nil)
{
#ifdef _DEBUG
	setDebugName("CNSOGLManager");
#endif
}

CNSOGLManager::~CNSOGLManager()
{
}

bool CNSOGLManager::initialize(const SIrrlichtCreationParameters& params, const SExposedVideoData& videodata)
{
	Params = params;

    return true;
}

void CNSOGLManager::terminate()
{
}

bool CNSOGLManager::generateSurface()
{    
	if (Params.DriverType == video::EDT_OPENGL)
	{
        int alphaSize = Params.WithAlphaChannel ? 4 : 0;
        int depthSize = Params.ZBufferBits;
        
        if (Params.WithAlphaChannel && Params.Bits == 32)
            alphaSize = 8;
        
        NSOpenGLPixelFormatAttribute Attribs[] =
        {
            NSOpenGLPFANoRecovery,
            NSOpenGLPFAAccelerated,
            NSOpenGLPFADoubleBuffer,
            NSOpenGLPFADepthSize, depthSize,
            NSOpenGLPFAColorSize, Params.Bits,
            NSOpenGLPFAAlphaSize, alphaSize,
            NSOpenGLPFASampleBuffers, 1,
            NSOpenGLPFASamples, Params.AntiAlias,
            NSOpenGLPFAStencilSize, Params.Stencilbuffer ? 1 : 0,
            //NSOpenGLPFAFullScreen,
            0
        };

        u32 Steps = 6;
        
        // Choose the best pixel format.
        do
        {
            switch (Steps)
            {
            case 6: // decrease step.
                --Steps;
                break;
            case 5: // samples
                if (Attribs[12] > 2)
                    --Attribs[12];
                else
                {
                    Attribs[10] = 0;
                    Attribs[12] = 0;
                    --Steps;
                }
                break;
            case 4: // alpha
                if (Attribs[8])
                {
                    Attribs[8] = 0;
                        
                    if (Params.AntiAlias)
                    {
                        Attribs[10] = 1;
                        Attribs[12] = Params.AntiAlias;
                        Steps = 5;
                    }
                }
                else
                    --Steps;
                break;
            case 3: // stencil
                if (Attribs[14])
                {
                    Attribs[14] = 0;
                        
                    if (Params.AntiAlias)
                    {
                        Attribs[10] = 1;
                        Attribs[12] = Params.AntiAlias;
                        Steps = 5;
                    }
                }
                else
                    --Steps;
                break;
            case 2: // depth size
                if (Attribs[4] > 16)
                {
                    Attribs[4] = Attribs[4] - 8;
                }
                else
                    --Steps;
                break;
            case 1: // buffer size
                if (Attribs[6] > 16)
                {
                    Attribs[6] = Attribs[6] - 8;
                }
                else
                    --Steps;
                break;
            default:
                os::Printer::log("Could not get pixel format.");
                return false;
            }
            
            PixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:Attribs];
        }
        while (PixelFormat == nil);
        
        if (Params.AntiAlias && !Attribs[10])
            os::Printer::log("No multisampling.");
        
        if (Params.WithAlphaChannel && !Attribs[8])
            os::Printer::log("No alpha.");
        
        if (Params.Stencilbuffer && !Attribs[14])
            os::Printer::log("No stencil buffer.");
        
        if (Params.ZBufferBits > Attribs[4])
            os::Printer::log("No full depth buffer.");
        
        if (Params.Bits > Attribs[6])
            os::Printer::log("No full color buffer.");
	}

    return true;
}

void CNSOGLManager::destroySurface()
{
    [PixelFormat release];
    PixelFormat = nil;
}

bool CNSOGLManager::generateContext()
{
    NSOpenGLContext* Context = [[NSOpenGLContext alloc] initWithFormat:PixelFormat shareContext:nil];
    
    GLint Vsync = Params.Vsync ? 1 : 0;
    [Context setValues:&Vsync forParameter:NSOpenGLCPSwapInterval];

	if (Context == nil)
	{
		os::Printer::log("Could not create OpenGL context.", ELL_ERROR);
		return false;
	}

	// set exposed data
	CurrentContext.OpenGLOSX.Context = Context;
    
	if (!PrimaryContext.OpenGLOSX.Context)
		PrimaryContext.OpenGLOSX.Context = CurrentContext.OpenGLOSX.Context;

	return true;
}

const SExposedVideoData& CNSOGLManager::getContext() const
{
	return CurrentContext;
}

bool CNSOGLManager::activateContext(const SExposedVideoData& videoData)
{
    if (videoData.OpenGLOSX.Context)
    {
        if ((NSOpenGLContext*)videoData.OpenGLOSX.Context != [NSOpenGLContext currentContext])
        {
            [(NSOpenGLContext*)videoData.OpenGLOSX.Context makeCurrentContext];
            
            CurrentContext = videoData;
        }
    }
    // set back to main context
    else
    {
        if ((NSOpenGLContext*)PrimaryContext.OpenGLOSX.Context != [NSOpenGLContext currentContext])
        {
            [(NSOpenGLContext*)PrimaryContext.OpenGLOSX.Context makeCurrentContext];
            
            CurrentContext = PrimaryContext;
        }
    }
    
	return true;
}

void CNSOGLManager::destroyContext()
{
	if (CurrentContext.OpenGLOSX.Context)
	{
        if (PrimaryContext.OpenGLOSX.Context == CurrentContext.OpenGLOSX.Context)
            PrimaryContext.OpenGLOSX.Context = nil;

		[(NSOpenGLContext*)CurrentContext.OpenGLOSX.Context makeCurrentContext];
        [(NSOpenGLContext *)CurrentContext.OpenGLOSX.Context clearDrawable];
        [(NSOpenGLContext *)CurrentContext.OpenGLOSX.Context release];
		[NSOpenGLContext clearCurrentContext];
        
		CurrentContext.OpenGLOSX.Context = nil;
	}
}

bool CNSOGLManager::swapBuffers()
{
    [(NSOpenGLContext*)CurrentContext.OpenGLOSX.Context flushBuffer];

	return true;
}

}
}

#endif
