#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <png.h>
#include <SDL/SDL.h>
#include <unistd.h>

#include "read_png.h"
#include "bez.h"

char	     **row_ptr;
SDL_Surface *png_data=0;
/* alpha blend routine */
unsigned int AlphaBlend(const unsigned int bg, const unsigned int src)
{
	unsigned int a = src >> 24; /* alpha */
       
	/* If source pixel is transparent, just return the background */
	if (0 == a)
	return bg;
 
	/* alpha blending the source and background colors */
	unsigned int rb = (((src & 0x00ff00ff) * a) +
	((bg & 0x00ff00ff) * (0xff - a))) & 0xff00ff00;
	unsigned int g = (((src & 0x0000ff00) * a) +
	((bg & 0x0000ff00) * (0xff - a))) & 0x00ff0000;
 
	return (src & 0xff000000) | ((rb | g) >> 8);
}

void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
		if ( x<0 || y<0 ) return ;
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to set */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

		//if (*((Uint32*) p))
	  //printf("%x %x\n", *((Uint32*) p), pixel );
    pixel = AlphaBlend ( *((Uint32*) p), pixel );

    switch(bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}

void anti_alias ( int direction, float p1, float p2, SDL_Surface *s ) {
	float frac;
	int   shift; 
	frac = p1 - floorf(p1);
	if (frac > 0.5) shift=1; else shift=-1;
	frac = fabsf(0.5-frac);
	if (!direction) {
		putpixel ( s, p2, p1, (int)   ((1-frac)*255) << 24 | 0xffff00 );
		putpixel ( s, p2, p1+shift, (int) (frac*255) << 24 | 0xffff00 );
	} else {
		putpixel ( s, p1, p2, (int)   ((1-frac)*255) << 24 | 0xffdd22 );
		putpixel ( s, p1+shift, p2, (int) (frac*255) << 24 | 0xffdd22 );
	}

}


void draw_line ( float x1, float y1, float x2, float y2, SDL_Surface *s) {
  static SDL_Surface* surface;
	surface = s?s:surface; // default to last non-null surface
  if (!surface) {
		printf("Error no surface to draw to\n");
		exit  ( 99 );
	}

	if ( x1<0    || y1<0      || x2<0      || y2<0  ||
       x1>=640 || x2 >= 640 || y1 >= 480 || y2 >= 480 ) {
		printf("Error, attempted to draw off screen.\n");
		return ;
	} 


	float sx, sy; 
	sx = x2 - x1;
	sy = y2 - y1;

//  printf ("p1 %d,%d p2 %d,%d s1 %d,%d a %d\n",x1,y1, x2,y2, sx,sy, abs(sx));
//  
// XXX: For partial pixels we need to add transparency proportional to where
//      the line was started. 

  if (fabsf(sx) > fabsf(sy)) 
		for (float f=0; f <= fabsf (sx) ; f++) 
			anti_alias ( 0, y1+f/fabsf(sx)*sy, x1+(sx/fabsf(sx)*f), surface );
	else 
		for (float f=0; f <= fabsf (sy) ; f++) 
			anti_alias ( 1, x1+f/fabsf(sy)*sx, y1+(sy/fabsf(sy)*f), surface );
	
}

int do_sdl () {
  int  done=0, draw=0, i,j;
	SDL_Surface *screen;
  SDL_Event   s_event;


	if ( (screen=SDL_SetVideoMode(640,480,0,SDL_HWSURFACE|SDL_NOFRAME|SDL_ASYNCBLIT)) == NULL ) {
		fprintf(stderr, "Failed to set video mode: %s\n", SDL_GetError());
		return(2);
	}

	if (png_data) {
		SDL_BlitSurface( png_data, NULL ,screen, NULL);
		SDL_UpdateRect(screen, 0, 0, 0, 0);
	}

  SDL_LockSurface(screen);
  draw_line ( 10, 10, 400, 100, screen );
/*draw_line ( 10, 10, 100, 400, screen );
  draw_line ( 100, 400, 500, 475, screen );
  draw_line ( 500, 475, 400, 100, screen );
  draw_line ( 100, 400, 400, 100, screen );
  draw_line ( 500, 475, 10, 10, screen );
  draw_line ( 10, 10, 475, 10, screen );
  draw_line ( 475, 10, 475, 475, screen );
  draw_line ( 475, 475, 10, 475, screen );
  draw_line ( 10, 475, 10, 10, screen );
  draw_line ( 10, 10, 475, 475, screen );
	draw_line ( 10, 475, 475, 10, screen );
*/

	point p[15];

  p[0].x =  50; p[0].y =  50;
  p[1].x = 150; p[1].y = 200;
  p[2].x = 400; p[2].y = 300;
  p[3].x = 600; p[3].y =  50;
  p[4].x = 625; p[4].y = 450;
  p[5].x = 600; p[5].y = 400;
	draw_envelope ( (point*) &p,5, (void*) draw_line );
  draw_bez ( (point*) &p , 5, 6, (void*) draw_line  );


  p[0].x = 600; p[0].y = 400;
  p[1].x =  20; p[1].y =  20;
  p[2].x = 11;  p[2].y = 469;
  p[3].x = 420; p[3].y = 400;
  p[4].x = 300; p[4].y = 100;
  p[5].x =  50; p[5].y =  50;
	draw_envelope ( (point*) &p,5, (void*) draw_line );
  draw_bez ( (point*) &p , 5, 6, (void*) draw_line );

/*
	for (float t=0; t < 2*3.1415; t = t+3.1415926535897932385/64)
		draw_line ( 225, 475, 200+200*sinf(t), 200+200*cosf(t), screen);
	
	for (float t=0; t < 2*3.1415; t = t+3.1415926535897932385/64)
		draw_line ( 25, 450, 200+200*sinf(t), 200+200*cosf(t), screen);
*/
	
	SDL_UnlockSurface(screen);
	SDL_UpdateRect(screen, 0, 0, 0, 0);

	while ( !done && SDL_WaitEvent(&s_event) ) 
		switch (s_event.type) {
			case SDL_MOUSEBUTTONDOWN: 
			    draw=1;
			case SDL_MOUSEMOTION: 
				if ( draw ) {
					SDL_LockSurface(screen);
					putpixel( screen, s_event.button.x, s_event.button.y, 0x1fffff00 );
					SDL_UnlockSurface(screen);
					SDL_UpdateRect(screen, s_event.button.x, s_event.button.y, 1, 1);
				}
				break;
			case SDL_MOUSEBUTTONUP: 
				draw=0;
				break;
			case SDL_KEYDOWN: {
				done=1;
			}
		}
}


int read_png (char* file_name) {
  FILE* png_file;

  char   png_header[8];



  printf("Using PNG %s\n", PNG_LIBPNG_VER_STRING);

  png_file = fopen(file_name, "r");
  if (!png_file) {
    printf("Error opening file\n");
    return 0;
  }

  fread(&png_header, 1, 8, png_file);

  if (png_sig_cmp(png_header, 0, 8)) {
    printf("Error reading PNG\n");
    return 0;
  }

	png_structp png_ptr = png_create_read_struct (
													PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	png_infop   info_ptr= png_create_info_struct (png_ptr);

  if (!png_ptr || !info_ptr) {
    printf("PNG ERROR");
    return 0;
  }

  png_set_sig_bytes(png_ptr, 8);
  png_init_io(png_ptr, png_file);

  png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
  row_ptr = (char**) png_get_rows(png_ptr, info_ptr);

	//printf("  W=%4d H=%4d\n", info_ptr->width, info_ptr->height );
 
  png_data=SDL_CreateRGBSurface(SDL_SWSURFACE,info_ptr->width,
																info_ptr->height,24,0xff,0xff00,0xff0000,0);
  
  for (int i=0; i < info_ptr->height; i++) 
		memcpy ( png_data->pixels + png_data->pitch*i, row_ptr[i], info_ptr->width*3 );

  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

	


}

