#import "GPUImageLineGenerator.h"

NSString *const kGPUImageLineGeneratorVertexShaderString = SHADER_STRING
(
 attribute vec4 position;
 
 void main()
 {
     gl_Position = position;
 }
);

#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSString *const kGPUImageLineGeneratorFragmentShaderString = SHADER_STRING
(
 uniform lowp vec3 lineColor;
 
 void main()
 {
     gl_FragColor = vec4(lineColor, 1.0);
 }
);
#else
NSString *const kGPUImageLineGeneratorFragmentShaderString = SHADER_STRING
(
 uniform vec3 lineColor;
 
 void main()
 {
     gl_FragColor = vec4(lineColor, 1.0);
 }
);
#endif

@interface GPUImageLineGenerator()

- (void)generateLineCoordinates;

@end

@implementation GPUImageLineGenerator

@synthesize lineWidth = _lineWidth;

#pragma mark -
#pragma mark Initialization and teardown

- (id)init;
{
    if (!(self = [super initWithVertexShaderFromString:kGPUImageLineGeneratorVertexShaderString fragmentShaderFromString:kGPUImageLineGeneratorFragmentShaderString]))
    {
        return nil;
    }
    
    runSynchronouslyOnVideoProcessingQueue(^{
        lineWidthUniform = [filterProgram uniformIndex:@"lineWidth"];
        lineColorUniform = [filterProgram uniformIndex:@"lineColor"];
        
        self.lineWidth = 1.0;
        [self setLineColorRed:0.0 green:1.0 blue:0.0];
    });
    
    return self;
}

- (void)dealloc
{
    if (lineCoordinates)
    {
        free(lineCoordinates);
    }
}

#pragma mark -
#pragma mark Rendering

- (void)generateLineCoordinates;
{
    lineCoordinates = calloc(1024 * 4, sizeof(GLfloat));
}

- (void)renderLinesFromArray:(GLfloat *)lineSlopeAndIntercepts count:(NSUInteger)numberOfLines frameTime:(CMTime)frameTime;
{
    if (self.preventRendering)
    {
        return;
    }
    
    if (lineCoordinates == NULL)
    {
        [self generateLineCoordinates];
    }
    
    // Iterate through and generate vertices from the slopes and intercepts
    NSUInteger currentVertexIndex = 0;
    NSUInteger currentLineIndex = 0;
    NSUInteger maxLineIndex = numberOfLines *2;
    while(currentLineIndex < maxLineIndex)
    {
        GLfloat slope = lineSlopeAndIntercepts[currentLineIndex++];
        GLfloat intercept = lineSlopeAndIntercepts[currentLineIndex++];
        
        if (slope > 9000.0) // Vertical line
        {
            lineCoordinates[currentVertexIndex++] = intercept;
            lineCoordinates[currentVertexIndex++] = -1.0;
            lineCoordinates[currentVertexIndex++] = intercept;
            lineCoordinates[currentVertexIndex++] = 1.0;
        }
        else
        {
            lineCoordinates[currentVertexIndex++] = -1.0;
            lineCoordinates[currentVertexIndex++] = slope * -1.0 + intercept;
            lineCoordinates[currentVertexIndex++] = 1.0;
            lineCoordinates[currentVertexIndex++] = slope * 1.0 + intercept;
        }
    }
    
    runSynchronouslyOnVideoProcessingQueue(^{
        [GPUImageContext setActiveShaderProgram:filterProgram];
        
        outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:[self sizeOfFBO] textureOptions:self.outputTextureOptions onlyTexture:NO];
        [outputFramebuffer activateFramebuffer];
        
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        
        glBlendEquation(GL_FUNC_ADD);
        glBlendFunc(GL_ONE, GL_ONE);
        glEnable(GL_BLEND);
        
        glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, lineCoordinates);
        glDrawArrays(GL_LINES, 0, ((unsigned int)numberOfLines * 2));
        
        glDisable(GL_BLEND);

        [self informTargetsAboutNewFrameAtTime:frameTime];
    });
}

- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
{
    // Prevent rendering of the frame by normal means
}

#pragma mark -
#pragma mark Accessors

- (void)setLineWidth:(CGFloat)newValue;
{
    _lineWidth = newValue;
    [GPUImageContext setActiveShaderProgram:filterProgram];
    glLineWidth(newValue);
}

- (void)setLineColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
{
    GPUVector3 lineColor = {redComponent, greenComponent, blueComponent};
    
    [self setVec3:lineColor forUniform:lineColorUniform program:filterProgram];
}


@end
