/**
 * Copyright (c) 2013 James King [metapyziks@gmail.com]
 *
 * This file is part of OpenTKTK.
 * 
 * OpenTKTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * OpenTKTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with OpenTKTK. If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTKTK.Textures;
using OpenTKTK.Utils;
namespace OpenTKTK.Shaders
{
    public class ShaderProgram : IDisposable
    {
        public class AttributeInfo
        {
            public ShaderProgram Shader { get; private set; }
            public String Identifier { get; private set; }
            public int Location { get; private set; }
            public int Size { get; private set; }
            public int Offset { get; private set; }
            public int Divisor { get; private set; }
            public int InputOffset { get; private set; }
            public VertexAttribPointerType PointerType { get; private set; }
            public bool Normalize { get; private set; }
            public int Length
            {
                get
                {
                    switch (PointerType) {
                        case VertexAttribPointerType.Byte:
                        case VertexAttribPointerType.UnsignedByte:
                            return Size * sizeof(byte);
                        case VertexAttribPointerType.Short:
                        case VertexAttribPointerType.UnsignedShort:
                            return Size * sizeof(short);
                        case VertexAttribPointerType.Int:
                        case VertexAttribPointerType.UnsignedInt:
                            return Size * sizeof(int);
                        case VertexAttribPointerType.HalfFloat:
                            return Size * sizeof(float) / 2;
                        case VertexAttribPointerType.Float:
                            return Size * sizeof(float);
                        case VertexAttribPointerType.Double:
                            return Size * sizeof(double);
                        default:
                            return 0;
                    }
                }
            }
            public AttributeInfo(ShaderProgram shader, String identifier,
                int size, int offset, int divisor, int inputOffset,
                VertexAttribPointerType pointerType =
                    VertexAttribPointerType.Float,
                bool normalize = false)
            {
                Shader = shader;
                Identifier = identifier;
                Location = GL.GetAttribLocation(shader.Program, Identifier);
                Size = size;
                Offset = offset;
                Divisor = divisor;
                InputOffset = inputOffset;
                PointerType = pointerType;
                Normalize = normalize;
            }
            public override String ToString()
            {
                return Identifier + " @" + Location + ", Size: " + Size + ", Offset: " + Offset;
            }
        }
        private class TextureInfo
        {
            public ShaderProgram Shader { get; private set; }
            public String Identifier { get; private set; }
            public int UniformLocation { get; private set; }
            public TextureUnit TextureUnit { get; private set; }
            public Texture CurrentTexture { get; private set; }
            public TextureInfo(ShaderProgram shader, String identifier,
                TextureUnit textureUnit = TextureUnit.Texture0)
            {
                Shader = shader;
                Identifier = identifier;
                UniformLocation = GL.GetUniformLocation(Shader.Program, Identifier);
                TextureUnit = textureUnit;
                Shader.Use();
                int val = (int) TextureUnit - (int) TextureUnit.Texture0;
                GL.Uniform1(UniformLocation, val);
                CurrentTexture = null;
            }
            public void SetCurrentTexture(Texture texture)
            {
                CurrentTexture = texture;
                GL.ActiveTexture(TextureUnit);
                CurrentTexture.Bind();
            }
        }
        public class AttributeCollection : IEnumerable<AttributeInfo>
        {
            private static int GetAttributeSize(ShaderVarType type)
            {
                switch (type) {
                    case ShaderVarType.Float:
                    case ShaderVarType.Int:
                        return 1;
                    case ShaderVarType.Vec2:
                        return 2;
                    case ShaderVarType.Vec3:
                        return 3;
                    case ShaderVarType.Vec4:
                        return 4;
                    default:
                        throw new ArgumentException("Invalid attribute type (" + type + ").");
                }
            }
            private ShaderProgram _shader;
            internal AttributeCollection(ShaderProgram shader)
            {
                _shader = shader;
            }
            public AttributeInfo this[int index]
            {
                get { return _shader._attributes[index]; }
            }
            public AttributeInfo this[String ident]
            {
                get { return _shader._attributes.First(x => x.Identifier == ident); }
            }
            public IEnumerator<AttributeInfo> GetEnumerator()
            {
                return _shader._attributes.GetEnumerator();
            }
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return _shader._attributes.GetEnumerator();
            }
        }
        private static ShaderProgram _sCurProgram;
        
        public int VertexDataStride { get; private set; }
        public int VertexDataSize { get; private set; }
        private List<AttributeInfo> _attributes;
        private Dictionary<String, TextureInfo> _textures;
        private Dictionary<String, int> _uniforms;
        public int Program { get; private set; }
        public PrimitiveType PrimitiveType { get; protected set; }
        public bool Flat { get; private set; }
        public bool Active
        {
            get { return _sCurProgram == this; }
        }
        public bool Immediate { get; protected set; }
        public bool Started { get; protected set; }
        public AttributeCollection Attributes { get; private set; }
        public ShaderProgram(bool flat)
        {
            PrimitiveType = PrimitiveType.Triangles;
            Flat = flat;


Answer:             _attributes = new List<AttributeInfo>();