#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include "mask.h"


mask_t* allocMask() {
  mask_t* tmp = (mask_t*) malloc(sizeof(mask_t));
  assert(tmp);
  clearMask(tmp);
  return tmp;
}

void freeMask(mask_t* m) {
  assert(m);
  free(m);
}

void clearMask(mask_t* m) {
  int i;
  for (i = 0; i < IMAGE_WIDTH * IMAGE_HEIGHT; i++) {
    (*m)[i] = 0;
  }
}

void printMask(mask_t* m) {
  int row, col;
  for (row = 0; row < IMAGE_HEIGHT; row++) {
    for (col = 0; col < IMAGE_WIDTH; col++) {
      printf("%d", (*m)[row * IMAGE_WIDTH + col]);
    }
    printf("\n");
  }
}

uchar getMask(mask_t* m, int x, int y) {
  return (*m)[y * IMAGE_WIDTH + x];
}

void markMask(mask_t* m, int x, int y, uchar val) {
  if ((0 <= x) && (x < IMAGE_WIDTH) &&
      (0 <= y) && (y < IMAGE_HEIGHT)) {
    (*m)[y * IMAGE_WIDTH + x] = val;
  }
}




int round(float x) {
  return (int) floorf(x + 0.5);
}





void generateCircle(mask_t* mask, int x, int y, int rad) {
  int i,j;
  int base, height;
  for (i = x - rad; i <= x + rad; i++) {
    base = abs(i - x);
    height = round(sqrt(rad*rad - base*base));
    for (j = y - height; j <= y + height; j++) {
      // mark [j][i]
      markMask(mask, i, j, 1);
    }
  }
}

void swap(int* a, int* b) {
  int tmp = *a;
  *a = *b;
  *b = tmp;
}

void generateRectangle(mask_t* mask,
		       int x1, int y1,
		       int x2, int y2,
		       int rad) {
  int i;
  int base, height;

  /**** better renderation... but slow ****/

  if (abs(x2-x1) >= abs(y2-y1)) {      // more samples on the X direction
    // to guarrantee x2 >= x1
    if (x1 > x2) {
      swap(&x1,&x2);
      swap(&y1,&y2);
    }
    for (i = x1; i <= x2; i++) {
      base = i - x1;
      height = round(base * (y2 - y1) / (float) (x2 - x1));
      generateCircle(mask, i, y1 + height, rad);
    }
  } else {                             // more in the Y direction
    // to guarrantee y2 >= y1
    if (y1 > y2) {
      swap(&x1,&x2);
      swap(&y1,&y2);
    }
    for (i = y1; i <= y2; i++) {
      height = i - y1;
      base = round(height * (x2 - x1) / (float) (y2 - y1));
      generateCircle(mask, x1 + base, i, rad);
    }
  }



  /**** not very good renderation with steep angles ****/
  /*
  int deltaX = abs(x2 - x1);
  int deltaY = abs(y2 - y1);
  float hyp = sqrt(deltaX*deltaX + deltaY*deltaY);
  float offset = hyp * rad / (float) deltaX;
  float height;
  int base;
  int i,j;
  int tmp;

  // to guarrantee x2 >= x1
  if (x1 > x2) {
    tmp = x1;
    x1 = x2;
    x2 = tmp;
    tmp = y1;
    y1 = y2;
    y2 = tmp;
  }

  for (i = x1; i <= x2; i++) {
    base = i - x1;
    height = (base * (y2 - y1)) / (float) (x2 - x1);
    for (j = round(y1+height-offset); j <= round(y1+height+offset); j++) {
      // mark [j][i]
      markMask(mask, i, j, 1);
    }
  }
  */
}

void generateSegment(mask_t* mask, int x1, int y1, int x2, int y2, int width) {
  generateCircle(mask, x1, y1, width);
  generateCircle(mask, x2, y2, width);
  generateRectangle(mask, x1, y1, x2, y2, width);
}



model_t* allocModel(int x, int y, int ang1, int ang2, int ang3) {
  model_t* tmp = (model_t*) malloc(sizeof(model_t));
  assert(tmp);
  tmp->x = x;
  tmp->y = y;
  tmp->ang1 = ang1;
  tmp->ang2 = ang2;
  tmp->ang3 = ang3;
  return tmp;
}

void freeModel(model_t* t) {
  assert(t);
  free(t);
}

void copyModel(model_t* src, model_t* des) {
  assert(src);
  assert(des);
  des->x = src->x;
  des->y = src->y;
  des->ang1 = src->ang1;
  des->ang2 = src->ang2;
  des->ang3 = src->ang3;
}

void printModel(model_t* t) {
  printf("[%3d %3d %3d %3d %3d]\n",
	 t->x,
	 t->y,
	 t->ang1,
	 t->ang2,
	 t->ang3);
}

mask_t* generateModelMask(model_t* t) {
  mask_t* m;
  float x1,y1;
  float x2,y2;
  float x3,y3;
  float x4,y4;
  float a1, a2, a3;

  // degree to radian
  a1 = t->ang1 * M_PI / 180;
  a2 = t->ang2 * M_PI / 180;
  a3 = t->ang3 * M_PI / 180;

  x1 = t->x;
  y1 = t->y;
  x2 = x1 + cos(a1) * CHUNK_LENGTH;
  y2 = y1 + sin(a1) * CHUNK_LENGTH;
  x3 = x2 + cos(a2) * CHUNK_LENGTH;
  y3 = y2 + sin(a2) * CHUNK_LENGTH;
  x4 = x3 + cos(a3) * CHUNK_LENGTH;
  y4 = y3 + sin(a3) * CHUNK_LENGTH;

  m = allocMask();
  generateCircle(m, round(x1), round(y1), CHUNK_WIDTH);
  generateCircle(m, round(x2), round(y2), CHUNK_WIDTH);
  generateCircle(m, round(x3), round(y3), CHUNK_WIDTH);
  generateCircle(m, round(x4), round(y4), CHUNK_WIDTH);
  generateRectangle(m, round(x1), round(y1), round(x2), round(y2),CHUNK_WIDTH);
  generateRectangle(m, round(x2), round(y2), round(x3), round(y3),CHUNK_WIDTH);
  generateRectangle(m, round(x3), round(y3), round(x4), round(y4),CHUNK_WIDTH);

  /* debug
  markMask(m, round(x1), round(y1), 8);
  markMask(m, round(x2), round(y2), 8);
  markMask(m, round(x3), round(y3), 8);
  markMask(m, round(x4), round(y4), 8);
  */

  return m;
}

// test program
/*
int main() {
  model_t* t = allocModel(15,40,-30,10,100);
  mask_t* m = generateModelMask(t);
  
  printMask(m);

  freeModel(t);
  freeMask(m);
  return 0;
}
*/
