/********************************************************
 * Line-draw function to be optimized for the CS:APP Performance Lab
 ********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "defs.h"
#include "line-versions.h"

/***************
 * DRAW-LINE KERNEL
 ***************/

/***************************************************************
* Various typedefs and helper functions for the draw_line function
* You may modify these any way you like.
**************************************************************/
#define INTENSITY(src) (src.green + src.red + src.blue)

/* static int intensity (pixel src)
{
        return src.green + src.red + src.blue;
} */


/*
* maximum - Returns the adress of the pixel with the highest intensity in src
*/
static pixel *maximum(int dim, pixel *src)
{
    int ii, jj;
    pixel *maxi = src;
    int darkness = 0;

    for(jj = dim-1; jj >= 0; jj--)
        for(ii = 0; ii <= dim-1; ii++)
            if (INTENSITY(src[RIDX(ii, jj, dim)]) > darkness)
            {
                darkness = INTENSITY(src[RIDX(ii, jj, dim)]);
                maxi = &(src[RIDX(ii, jj, dim)]);
            }

    return maxi;
}


static pixel *maximum3(int dim, pixel *src) {
	pixel *maxi = src, *pix_p = &(src[0]);
	pixel *lastpix_p = &(src[dim*dim-1]);
	int darkness = 0, temp;
	
	for(pix_p = &src[0]; pix_p != lastpix_p; pix_p++) {
		/*temp = INTENSITY((*pix_p));
		if (temp > darkness) {
			darkness = temp;
			maxi = pix_p;
		}*/
		if (INTENSITY((*pix_p)) > darkness) {
			darkness = INTENSITY((*pix_p));
			maxi = pix_p;
		}
	}
	return maxi;
}

static pixel *maximum4(int dim, pixel *src) {
	pixel *maxi = src, *pix_p = &(src[0]);
	pixel *lastpix_p = &(src[dim*dim-1]);
	int darkness = 0, temp;
	
	while(pix_p <= lastpix_p) {
		/* begin python ... */
		if (INTENSITY((*(pix_p + 0 ))) > darkness) { darkness = INTENSITY((*(pix_p + 0 ))); maxi = (pix_p + 0 ); } 
		if (INTENSITY((*(pix_p + 1 ))) > darkness) { darkness = INTENSITY((*(pix_p + 1 ))); maxi = (pix_p + 1 ); } 
		if (INTENSITY((*(pix_p + 2 ))) > darkness) { darkness = INTENSITY((*(pix_p + 2 ))); maxi = (pix_p + 2 ); } 
		if (INTENSITY((*(pix_p + 3 ))) > darkness) { darkness = INTENSITY((*(pix_p + 3 ))); maxi = (pix_p + 3 ); } 
		if (INTENSITY((*(pix_p + 4 ))) > darkness) { darkness = INTENSITY((*(pix_p + 4 ))); maxi = (pix_p + 4 ); } 
		if (INTENSITY((*(pix_p + 5 ))) > darkness) { darkness = INTENSITY((*(pix_p + 5 ))); maxi = (pix_p + 5 ); } 
		if (INTENSITY((*(pix_p + 6 ))) > darkness) { darkness = INTENSITY((*(pix_p + 6 ))); maxi = (pix_p + 6 ); } 
		if (INTENSITY((*(pix_p + 7 ))) > darkness) { darkness = INTENSITY((*(pix_p + 7 ))); maxi = (pix_p + 7 ); } 
		if (INTENSITY((*(pix_p + 8 ))) > darkness) { darkness = INTENSITY((*(pix_p + 8 ))); maxi = (pix_p + 8 ); } 
		if (INTENSITY((*(pix_p + 9 ))) > darkness) { darkness = INTENSITY((*(pix_p + 9 ))); maxi = (pix_p + 9 ); } 
		if (INTENSITY((*(pix_p + 10 ))) > darkness) { darkness = INTENSITY((*(pix_p + 10 ))); maxi = (pix_p + 10 ); } 
		if (INTENSITY((*(pix_p + 11 ))) > darkness) { darkness = INTENSITY((*(pix_p + 11 ))); maxi = (pix_p + 11 ); } 
		if (INTENSITY((*(pix_p + 12 ))) > darkness) { darkness = INTENSITY((*(pix_p + 12 ))); maxi = (pix_p + 12 ); } 
		if (INTENSITY((*(pix_p + 13 ))) > darkness) { darkness = INTENSITY((*(pix_p + 13 ))); maxi = (pix_p + 13 ); } 
		if (INTENSITY((*(pix_p + 14 ))) > darkness) { darkness = INTENSITY((*(pix_p + 14 ))); maxi = (pix_p + 14 ); } 
		if (INTENSITY((*(pix_p + 15 ))) > darkness) { darkness = INTENSITY((*(pix_p + 15 ))); maxi = (pix_p + 15 ); } 
		if (INTENSITY((*(pix_p + 16 ))) > darkness) { darkness = INTENSITY((*(pix_p + 16 ))); maxi = (pix_p + 16 ); } 
		if (INTENSITY((*(pix_p + 17 ))) > darkness) { darkness = INTENSITY((*(pix_p + 17 ))); maxi = (pix_p + 17 ); } 
		if (INTENSITY((*(pix_p + 18 ))) > darkness) { darkness = INTENSITY((*(pix_p + 18 ))); maxi = (pix_p + 18 ); } 
		if (INTENSITY((*(pix_p + 19 ))) > darkness) { darkness = INTENSITY((*(pix_p + 19 ))); maxi = (pix_p + 19 ); } 
		if (INTENSITY((*(pix_p + 20 ))) > darkness) { darkness = INTENSITY((*(pix_p + 20 ))); maxi = (pix_p + 20 ); } 
		if (INTENSITY((*(pix_p + 21 ))) > darkness) { darkness = INTENSITY((*(pix_p + 21 ))); maxi = (pix_p + 21 ); } 
		if (INTENSITY((*(pix_p + 22 ))) > darkness) { darkness = INTENSITY((*(pix_p + 22 ))); maxi = (pix_p + 22 ); } 
		if (INTENSITY((*(pix_p + 23 ))) > darkness) { darkness = INTENSITY((*(pix_p + 23 ))); maxi = (pix_p + 23 ); } 
		if (INTENSITY((*(pix_p + 24 ))) > darkness) { darkness = INTENSITY((*(pix_p + 24 ))); maxi = (pix_p + 24 ); } 
		if (INTENSITY((*(pix_p + 25 ))) > darkness) { darkness = INTENSITY((*(pix_p + 25 ))); maxi = (pix_p + 25 ); } 
		if (INTENSITY((*(pix_p + 26 ))) > darkness) { darkness = INTENSITY((*(pix_p + 26 ))); maxi = (pix_p + 26 ); } 
		if (INTENSITY((*(pix_p + 27 ))) > darkness) { darkness = INTENSITY((*(pix_p + 27 ))); maxi = (pix_p + 27 ); } 
		if (INTENSITY((*(pix_p + 28 ))) > darkness) { darkness = INTENSITY((*(pix_p + 28 ))); maxi = (pix_p + 28 ); } 
		if (INTENSITY((*(pix_p + 29 ))) > darkness) { darkness = INTENSITY((*(pix_p + 29 ))); maxi = (pix_p + 29 ); } 
		if (INTENSITY((*(pix_p + 30 ))) > darkness) { darkness = INTENSITY((*(pix_p + 30 ))); maxi = (pix_p + 30 ); } 
		if (INTENSITY((*(pix_p + 31 ))) > darkness) { darkness = INTENSITY((*(pix_p + 31 ))); maxi = (pix_p + 31 ); } 
		/* end python */
		pix_p += 32;
	}
	return maxi;
}

/******************************************************
 * Your different versions of the draw_line function go here
 ******************************************************/

void apointer_line(int dim, pixel *src, pixel *dst) {
	int x0 = 0;
	int y0 = floor (dim / 3); /* left endpoint */
	int x1 = dim - 1;
	int y1 = ceil (dim - 1 - dim / 3); /* right endpoint */

	double dy = y1 - y0;
	double dx = x1 - x0;
	double slope = dy / dx;

	double y = y0;
	int x = x0;
	
	/* use temp. var instead of func call in loop */
	register pixel max = *maximum3(dim, src);
	register pixel *pix_p = &(dst[RIDX(0, (int)rint(y), dim)]);
	register pixel *lastpix_p = &(dst[RIDX(dim - 1, dim - 1, dim)]);
	register int step = RIDX(1, (int)rint(slope), dim);
	
	/* move the pointer 1 pixel horiz., and (slope) lines downards, each
 	 * line is (dim) pixels */
	while(pix_p <= lastpix_p) {
		*pix_p = max;
		pix_p += step;
	} 
}

void aunst_line(int dim, pixel *src, pixel *dst) {
	int x0 = 0;
	int y0 = floor (dim / 3); /* left endpoint */
	int x1 = dim - 1;
	int y1 = ceil (dim - 1 - dim / 3); /* right endpoint */

	double dy = y1 - y0;
	double dx = x1 - x0;
	double slope = dy / dx;

	double y = y0 + 0.5;
	pixel *mydst = dst + (int)y;
	int x = x0;
	
	/* use temp. var instead of func call in loop */
	pixel max = *maximum4(dim, src);

	/* move the pointer 1 pixel horiz., and (slope) lines downards, each
 	 * line is (dim) pixels */
	for(x=0; x <= x1; x++) {
		*(dst + x * dim + (int)(slope*x + y)) = max;
	}
}
	 
void aunst_line_new(int dim, pixel *src, pixel *dst) {
	int x0 = 0;
	int y0 = floor (dim / 3); /* left endpoint */
	int x1 = dim - 1;
	int y1 = ceil (dim - 1 - dim / 3); /* right endpoint */

	double dy = y1 - y0;
	double dx = x1 - x0;
	double slope = dy / dx;

	double y = y0;
	double step;
	int x = x0;
	
	/* use temp. var instead of func call in loop */
	pixel max = *maximum4(dim, src);

	/* move the pointer 1 pixel horiz., and (slope) lines downards, each
 	 * line is (dim) pixels */
	
	step =  RIDX(1, slope, dim);
	register pixel *pix_p = &(dst[RIDX(0, (int)rint(y), dim)]);
	register pixel *lastpix_p = &(dst[RIDX(x1, (int)(slope*x1+y+0.5), dim)]);

	for(x=0; x <= x1; x++) {
		//pix_p += (int)(x * step + 0.5);
		*pix_p = max;
		y += step;

		pix_p += (int)(y + step + 0.5);
		
	}
}
	 
/* 
 * naive_version - The naive baseline version of draw_line
 */
void naive_line(int dim, pixel *src, pixel *dst) {
	int x0 = 0;
	int y0 = floor (dim / 3); /* left endpoint */
	int x1 = dim - 1;
	int y1 = ceil (dim - 1 - dim / 3); /* right endpoint */

	double dy = y1 - y0;
	double dx = x1 - x0;
	double slope = dy / dx;

	double y = y0;
	int x = x0;

	for (; x <= x1; x++) {
		dst[RIDX(x,(int )rint(y), dim)] = *maximum(dim, src);
		y += slope;
	}
}

