#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"

using namespace ci;
using namespace ci::app;
using namespace std;

class CirclesApp : public App {
  public:
    void setup() override;
    void update() override;
    void draw() override;

  private:
    gl::BatchRef      mBatch;
    gl::VboRef        mInstances;
    std::vector<vec4> mData;
};

void CirclesApp::setup()
{
    // Create the shader.
    const char *vertShader = R"V0G0N(
        #version 150
        uniform mat4   ciModelViewProjection;
        in vec4        ciPosition;
        in vec2        ciTexCoord0;
        in vec4        ciColor;

        in vec4        iData; // per instance: xy = center, z = radius, w = width

        out vec4       vertColor;

        void main( void )
        {
            vertColor    = ciColor;

            vec4 position = ciPosition;
            position.xy *= mix( iData.z, iData.w / 0.9, ciTexCoord0.x ); // Vertices on inner radius have coordinate x = 1 and radius 0.9, so compensate for that.
            position.xy += iData.xy;

            gl_Position	 = ciModelViewProjection * position;
        }
	)V0G0N";

    const char *fragShader = R"V0G0N(
        #version 150

        in vec4 vertColor;

        out vec4 fragColor;

        void main( void )
        {
            fragColor = pow( vertColor, vec4( 1.0 / 2.2 ) );
        }
	)V0G0N";

    gl::GlslProgRef glsl = gl::GlslProg::create( vertShader, fragShader );

    // Create ring mesh. With a width of 0.1, we have an inner radius of 0.9. See shader.
    gl::VboMeshRef mesh = gl::VboMesh::create( geom::Ring().radius( 1.0f ).width( 0.1f ).subdivisions( 128 ) );

    // Create ring instances.
    mData.clear();
    mData.emplace_back( 0, 0, 150.0f, 144.0f ); // x,y = center, z = outer radius, w = inner radius
    mData.emplace_back( 0, 0, 140.0f, 135.0f );
    mData.emplace_back( 0, 0, 130.0f, 126.0f );
    mData.emplace_back( 0, 0, 120.0f, 117.0f );
    mData.emplace_back( 0, 0, 110.0f, 108.0f );

    mInstances = gl::Vbo::create( GL_ARRAY_BUFFER, mData.size() * sizeof( vec4 ), mData.data(), GL_STATIC_DRAW );

    // Describe the layout of the instance data.
    geom::BufferLayout layout;
    layout.append( geom::Attrib::CUSTOM_0, sizeof( vec4 ) / sizeof( float ), 0, 0, 1 /* per instance */ );

    // Append instance data to mesh.
    mesh->appendVbo( layout, mInstances );

    // Create batch, combining the mesh, the shader and a full description of the data.
    mBatch = gl::Batch::create( mesh, glsl, { { geom::Attrib::CUSTOM_0, "iData" } } );
}

void CirclesApp::update()
{
}

void CirclesApp::draw()
{
    gl::clear();

    gl::ScopedColor       scpColor( 1, 1, 1 );
    gl::ScopedModelMatrix scpModel;
    gl::translate( getWindowSize() / 2 );

    mBatch->drawInstanced( mData.size() );
}

CINDER_APP( CirclesApp, RendererGl( RendererGl::Options().msaa( 32 ) ) )