/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.
*/
#ifndef _GL_SHARED_GROUP_H_
#define _GL_SHARED_GROUP_H_

#define GL_API
#ifndef ANDROID
#define GL_APIENTRY
#define GL_APIENTRYP
#endif

#include <GLES/gl.h>
#include <GLES/glext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

#include <stdio.h>
#include <stdlib.h>
#include "ErrorLog.h"
#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include "FixedBuffer.h"
#include "SmartPtr.h"

struct BufferData {
    BufferData();
    BufferData(GLsizeiptr size, void * data);
    GLsizeiptr  m_size;
    FixedBuffer m_fixedBuffer;    
};

class ProgramData {
private:
    typedef struct _IndexInfo {
        GLint base;
        GLint size;
        GLenum type;
        GLint appBase;
        GLint hostLocsPerElement;
        GLuint flags;
        GLint samplerValue; // only set for sampler uniforms
    } IndexInfo;

    GLuint m_numIndexes;
    IndexInfo* m_Indexes;
    bool m_initialized;
    bool m_locShiftWAR;

    android::Vector<GLuint> m_shaders;

public:
    enum {
        INDEX_FLAG_SAMPLER_EXTERNAL = 0x00000001,
    };

    ProgramData();
    void initProgramData(GLuint numIndexes);
    bool isInitialized();
    virtual ~ProgramData();
    void setIndexInfo(GLuint index, GLint base, GLint size, GLenum type);
    void setIndexFlags(GLuint index, GLuint flags);
    GLuint getIndexForLocation(GLint location);
    GLenum getTypeForLocation(GLint location);

    bool needUniformLocationWAR() const { return m_locShiftWAR; }
    void setupLocationShiftWAR();
    GLint locationWARHostToApp(GLint hostLoc, GLint arrIndex);
    GLint locationWARAppToHost(GLint appLoc);

    GLint getNextSamplerUniform(GLint index, GLint* val, GLenum* target);
    bool setSamplerUniform(GLint appLoc, GLint val, GLenum* target);

    bool attachShader(GLuint shader);
    bool detachShader(GLuint shader);
    size_t getNumShaders() const { return m_shaders.size(); }
    GLuint getShader(size_t i) const { return m_shaders[i]; }
};

struct ShaderData {
    typedef android::List<android::String8> StringList;
    StringList samplerExternalNames;
    int refcount;
};

class GLSharedGroup {
private:
    android::DefaultKeyedVector<GLuint, BufferData*> m_buffers;
    android::DefaultKeyedVector<GLuint, ProgramData*> m_programs;
    android::DefaultKeyedVector<GLuint, ShaderData*> m_shaders;
    mutable android::Mutex m_lock;

    void refShaderDataLocked(ssize_t shaderIdx);
    void unrefShaderDataLocked(ssize_t shaderIdx);

public:
    GLSharedGroup();
    ~GLSharedGroup();
    bool isObject(GLuint obj);
    BufferData * getBufferData(GLuint bufferId);
    void    addBufferData(GLuint bufferId, GLsizeiptr size, void * data);
    void    updateBufferData(GLuint bufferId, GLsizeiptr size, void * data);
    GLenum  subUpdateBufferData(GLuint bufferId, GLintptr offset, GLsizeiptr size, void * data);
    void    deleteBufferData(GLuint);

    bool    isProgram(GLuint program);
    bool    isProgramInitialized(GLuint program);
    void    addProgramData(GLuint program); 
    void    initProgramData(GLuint program, GLuint numIndexes);
    void    attachShader(GLuint program, GLuint shader);
    void    detachShader(GLuint program, GLuint shader);
    void    deleteProgramData(GLuint program);
    void    setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type, const char* name);
    GLenum  getProgramUniformType(GLuint program, GLint location);
    void    setupLocationShiftWAR(GLuint program);
    GLint   locationWARHostToApp(GLuint program, GLint hostLoc, GLint arrIndex);
    GLint   locationWARAppToHost(GLuint program, GLint appLoc);
    bool    needUniformLocationWAR(GLuint program);
    GLint   getNextSamplerUniform(GLuint program, GLint index, GLint* val, GLenum* target) const;
    bool    setSamplerUniform(GLuint program, GLint appLoc, GLint val, GLenum* target);

    bool    addShaderData(GLuint shader);
    // caller must hold a reference to the shader as long as it holds the pointer
    ShaderData* getShaderData(GLuint shader);
    void    unrefShaderData(GLuint shader);
};

typedef SmartPtr<GLSharedGroup> GLSharedGroupPtr; 

#endif //_GL_SHARED_GROUP_H_