/*
 Bmp
 Copyright (C) 2010 Martin Štrympl

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
 License as published by the Free Software Foundation; either
 version 3.0 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 General Public License for more details.

 You should have received a copy of the GNU General Public
 License along with this library.

 Martin Štrympl
 xstrym00@stud.feec.vutbr.cz
 */

/*
 *  main.c
 *  Bmp
 *
 *  Created by Martin Štrympl on 11/4/10.
 *  Copyright 2010 School. All rights reserved.
 *
 */

#include "main.h"

#include "bmp.h"

#include "mymath.h"

/*struct sBMP
{
  long double ld_x_scaler, ld_y_scaler;

  struct sBMP_PixelMask s_pixels;

  char *t_file;

  uint32_t i4_debuglevel;
};*/

struct sBMP s_bmp;


Mac_Fun_Gerber_Module_Start(Start)
{
  uint32_t u4_n;

  printf("Module BMP: Version 1.2\n");
  printf("Module BMP: Programed by Martin Stympl. Mail: xstrym00@stud.feec.vutbr.cz\n");
  printf("Module BMP: Compiled time: %s %s\n", __DATE__, __TIME__);

  // default sets
  s_bmp.ld_x_scaler=1.000000000000L;
  s_bmp.ld_y_scaler=1.000000000000L;

  s_bmp.t_file = NULL;

  s_bmp.i4_debuglevel=0;

  s_bmp.s_pixels.au1_pixels=0;
  s_bmp.s_pixels.u4_x_min=s_bmp.s_pixels.u4_x_max=0;
  s_bmp.s_pixels.u4_y_min=s_bmp.s_pixels.u4_y_max=0;
  s_bmp.s_pixels.u4_width=s_bmp.s_pixels.u4_height=0;
  s_bmp.s_pixels.i4_x_move=s_bmp.s_pixels.i4_y_move=-2147483648;

  for (u4_n=0;u4_n<u4_args;u4_n++)
  {
    if (strcmp(at_args[u4_n],"--x_scaler")==0)
    {
      u4_n++;
      if (u4_n<u4_args)
      {
        s_bmp.ld_x_scaler=atof(at_args[u4_n]+1);
        if ((s_bmp.ld_x_scaler>-0.000000000001L)&&(s_bmp.ld_x_scaler<0.000000000001L))
        {
          printf("Module BMP: Bad axis Y scaler value %e. Value is nearing to zero.\n", (double)s_bmp.ld_x_scaler);
          return -1;
        }
        else
        {
          if (s_bmp.ld_x_scaler<1.000000000000L) printf("Module BMP: Axis Y scaler set to 1/(%e)\n", (double)(1.000000000000L/s_bmp.ld_x_scaler));
          else printf("Module BMP: Axis Y scaler set to %e\n", (double)s_bmp.ld_x_scaler);
        }
      }
      else
      {
        printf("Module BMP: Missing modificator '%s' value.\n", at_args[u4_n-1]);
        return -1;
      }
    }
    else if (strcmp(at_args[u4_n],"--y_scaler")==0)
    {
      u4_n++;
      if (u4_n<u4_args)
      {
        s_bmp.ld_y_scaler=atof(at_args[u4_n]+1);
        if ((s_bmp.ld_y_scaler>-0.000000000001L)&&(s_bmp.ld_y_scaler<0.000000000001L))
        {
          printf("Module BMP: Bad axis Y scaler value %e. Value is nearing to zero.\n", (double)s_bmp.ld_y_scaler);
          return -1;
        }
        else
        {
          if (s_bmp.ld_y_scaler<1.000000000000L) printf("Module BMP: Axis Y scaler set to 1/(%e)\n", (double)(1.000000000000L/s_bmp.ld_y_scaler));
          else printf("Module BMP: Axis Y scaler set to %e\n", (double)s_bmp.ld_y_scaler);
        }
      }
      else
      {
        printf("Module BMP: Missing modificator '%s' value.\n", at_args[u4_n-1]);
        return -1;
      }
    }
    else if (strcmp(at_args[u4_n],"--bmp")==0)
    {
      u4_n++;
      if (u4_n<u4_args)
      {
        // output filename
        uint32_t u4_len;

        u4_len=strlen(at_args[u4_n]+1)+1;

        if (s_bmp.t_file) free(s_bmp.t_file);
        s_bmp.t_file = malloc(u4_len);

        if (s_bmp.t_file)
        {
          memcpy(s_bmp.t_file, at_args[u4_n]+1, u4_len);
          printf("Module BMP: Output picture name set to '%s'\n", at_args[u4_n]+1);
        }
        else
        {
          printf("Module BMP: Copy output picture name failed.\n");
          return -1;
        }
      }
      else
      {
        printf("Module BMP: Missing modificator '%s' value.\n", at_args[u4_n-1]);
        return -1;
      }
    }
    else if (strcmp(at_args[u4_n],"--debug")==0)
    {
      u4_n++;
      if (u4_n<u4_args)
      {
        s_bmp.i4_debuglevel=atoi(at_args[u4_n]+1);
        printf("Module BMP: Debug level set to %i\n", s_bmp.i4_debuglevel);
      }
      else
      {
        printf("Module BMP: Missing modificator '%s' value.\n", at_args[u4_n-1]);
        return -1;
      }
    }
    else
    {
      printf("Module BMP: Unknown modificator '%s'.\n", at_args[u4_n]);
      return -1;
    }

  }

  *pv_memory=&s_bmp;

  return 0;
}
Mac_Fun_Gerber_Module_End(End)
{
  struct sBMP *ps_bmp;

  FILE *fi_file;

  uint32_t u4_line, u4_zero, u4_zeros, u4_n;

  ps_bmp=(struct sBMP *)v_memory;

  if (ps_bmp->t_file) fi_file=fopen(ps_bmp->t_file,"wb");
  else fi_file=fopen("gerber.bmp","wb");

  if (fi_file)
  {
    uint32_t u4_write;
    uint16_t u2_write;

    printf("Module BMP: Writing to picture file.\n");

    u4_line=ps_bmp->s_pixels.u4_width>>3;

    if (u4_line%4) u4_zeros=4-(u4_line%4);
    else u4_zeros=0;

    // header
    u2_write=0x4D42;
    fwrite(&u2_write,1,2,fi_file);

    // file size, 54 header, 8 sablona
    u4_write=54+8+(((ps_bmp->s_pixels.u4_width>>3)+u4_zeros)*ps_bmp->s_pixels.u4_height);
    fwrite(&u4_write,1,4,fi_file);

    u2_write=0x0;
    fwrite(&u2_write,1,2,fi_file);
    u2_write=0x0;
    fwrite(&u2_write,1,2,fi_file);

    // offset to image data
    u4_write=0x3E;
    fwrite(&u4_write,1,4,fi_file);

    u4_write=40;
    fwrite(&u4_write,1,4,fi_file);

    // width
    u4_write=ps_bmp->s_pixels.u4_width;
    fwrite(&u4_write,1,4,fi_file);
    // height
    u4_write=ps_bmp->s_pixels.u4_height;
    fwrite(&u4_write,1,4,fi_file);

    u2_write=1;
    fwrite(&u2_write,1,2,fi_file);

    // bits per pixel
    // can supported any other color model in future
    u2_write=1;
    fwrite(&u2_write,1,2,fi_file);

    u4_write=0x0;
    fwrite(&u4_write,1,4,fi_file);

    // size of image data
    u4_write=(ps_bmp->s_pixels.u4_width*ps_bmp->s_pixels.u4_height)>>3;
    fwrite(&u4_write,1,4,fi_file);

    u4_write=0x0;
    fwrite(&u4_write,1,4,fi_file);
    u4_write=0x0;
    fwrite(&u4_write,1,4,fi_file);
    u4_write=0x0;
    fwrite(&u4_write,1,4,fi_file);
    u4_write=0x0;
    fwrite(&u4_write,1,4,fi_file);

    // pallete ?
    u4_write=0x0;
    fwrite(&u4_write,1,4,fi_file);
    u4_write=0x00FFFFFF;
    fwrite(&u4_write,1,4,fi_file);

    // body
    u4_zero=0;

    /*u4_line=ps_bmp->s_pixels.u4_width>>3;

    if (ps_bmp->s_pixels.u4_width%8) u4_zeros=4-(ps_bmp->s_pixels.u4_width%8);
    else u4_zeros=0;*/

    for (u4_n=0;u4_n<ps_bmp->s_pixels.u4_height;u4_n++)
    {
      fwrite(ps_bmp->s_pixels.au1_pixels+u4_n*u4_line,1,u4_line,fi_file);

      if (u4_zeros) fwrite(&u4_zero,1,u4_zeros,fi_file);
    }

    fclose(fi_file);
  }
  else printf("Module BMP: Creating picture file failed.\n");
}

Mac_Fun_Gerber_Draw_LineSegment(LineSegment)
{
  struct sBMP *ps_bmp;

  ps_bmp=(struct sBMP *)v_mem;

  // applicate scaler
  ps_From->ld_x*=ps_bmp->ld_x_scaler;
  ps_From->ld_y*=ps_bmp->ld_y_scaler;

  ps_To->ld_x*=ps_bmp->ld_x_scaler;
  ps_To->ld_y*=ps_bmp->ld_y_scaler;

  // round values
  ps_From->ld_x=floor(ps_From->ld_x+0.500000000000L);
  ps_From->ld_y=floor(ps_From->ld_y+0.500000000000L);

  ps_To->ld_x=floor(ps_To->ld_x+0.500000000000L);
  ps_To->ld_y=floor(ps_To->ld_y+0.500000000000L);

  if (ps_bmp->i4_debuglevel>0) printf("Module BMP: Debug: LINE: From: [%e;%e] To: [%e;%e]\n", (double)ps_From->ld_x, (double)ps_From->ld_y, (double)ps_To->ld_x, (double)ps_To->ld_y);

  // resize picture
  if (BMP_PixelMask_Resize(ps_bmp, &ps_bmp->s_pixels, ps_From->ld_x, ps_To->ld_x, ps_From->ld_y, ps_To->ld_y))
  {
    long double ld_x_dif, ld_y_dif;
    long double ld_x, ld_y;
    // must count line, and draw it

    ld_x_dif=ps_To->ld_x-ps_From->ld_x;
    ld_y_dif=ps_To->ld_y-ps_From->ld_y;

    if (abs(ld_x_dif)>abs(ld_y_dif))
    {
      // for ld_x_dif is every times greater than zero
      if (ps_From->ld_x>ps_To->ld_x)
      {
        ld_x=ps_From->ld_x;
        ld_y=ps_From->ld_y;
        ps_From->ld_x=ps_To->ld_x;
        ps_From->ld_y=ps_To->ld_y;
        ps_To->ld_x=ld_x;
        ps_To->ld_y=ld_y;

        ld_x_dif=ps_To->ld_x-ps_From->ld_x+0.100000000000L;
      }
      ld_y_dif=ps_To->ld_y-ps_From->ld_y;

      if (ps_bmp->i4_debuglevel>0) printf("Module BMP: Debug: LINE by X: From: [%e;%e] Xdiff: %e Ydiff: %e\n", (double)ps_From->ld_x, (double)ps_From->ld_y, (double)ld_x_dif, (double)ld_y_dif);

      for (ld_x=0.000000000000L;ld_x<ld_x_dif;ld_x=floor(ld_x+1.100000000000L))
      {
        ld_y=floor(ps_From->ld_y+(ld_x/ld_x_dif)*ld_y_dif+0.500000000000L);

        BMP_PixelMask_Pixel(ps_bmp, &ps_bmp->s_pixels, ps_From->ld_x+ld_x, ld_y, b_positive);
      }
    }
    else
    {
      if (ps_From->ld_y>ps_To->ld_y)
      {
        ld_x=ps_From->ld_x;
        ld_y=ps_From->ld_y;
        ps_From->ld_x=ps_To->ld_x;
        ps_From->ld_y=ps_To->ld_y;
        ps_To->ld_x=ld_x;
        ps_To->ld_y=ld_y;

        ld_y_dif=ps_To->ld_y-ps_From->ld_y+0.100000000000L;
      }
      ld_x_dif=ps_To->ld_x-ps_From->ld_x;

      if (ps_bmp->i4_debuglevel>0) printf("Module BMP: Debug: LINE by Y: From: [%e;%e] Xdiff: %e Ydiff: %e\n", (double)ps_From->ld_x, (double)ps_From->ld_y, (double)ld_x_dif, (double)ld_y_dif);

      for (ld_y=0.000000000000L;ld_y<ld_y_dif;ld_y=floor(ld_y+1.100000000000L))
      {
        ld_x=floor(ps_From->ld_x+(ld_y/ld_y_dif)*ld_x_dif+0.500000000000L);

        BMP_PixelMask_Pixel(ps_bmp, &ps_bmp->s_pixels, ld_x, ps_From->ld_y+ld_y, b_positive);
      }
    }
  }
  else
  {
    printf("Module BMP: Error: LINE: Resize failed.\n");
    return -1;
  }

  return 0;
}
#define COMPARE(a, b) (abs(a-b)<0.000001L)
Mac_Fun_Gerber_Draw_OvalSegment(OvalSegment)
{
  struct sBMP *ps_bmp;

  ps_bmp=(struct sBMP *)v_mem;

  //ps_centre->ld_x*=ps_bmp->ld_x_scaler;
  //ps_centre->ld_y*=ps_bmp->ld_y_scaler;

  if (ps_bmp->i4_debuglevel>0) printf("Module BMP: Debug: OVAL CENTER: %3.4f,%3.4f RADIUSX: %3.4f RADIUSY: %3.4f ANGLE: %3.4f to %3.4f\n",
                                      (double)ps_centre->ld_x, (double)ps_centre->ld_y, (double)ld_radiusX, (double)ld_radiusX,
                                      (double)ld_aglefrom, (double)ld_agleto);

  if (BMP_PixelMask_Resize(ps_bmp, &ps_bmp->s_pixels,
                           (ps_centre->ld_x+ld_radiusX)*ps_bmp->ld_x_scaler,
                           (ps_centre->ld_x-ld_radiusY)*ps_bmp->ld_x_scaler,
                           (ps_centre->ld_y+ld_radiusX)*ps_bmp->ld_y_scaler,
                           (ps_centre->ld_y-ld_radiusY)*ps_bmp->ld_y_scaler))

  {
    if (COMPARE(ld_aglefrom, ld_agleto))
    {
      ld_aglefrom=0.0L;
      ld_agleto=360.0L;
    }
    
    if (ld_aglefrom<0.0L) ld_aglefrom+=360.0L;
    if (ld_agleto<0.0L) ld_agleto+=360.0L;
    if (ld_aglefrom>ld_agleto)
    {
      ld_agleto+=360.0L;
    }

    ld_aglefrom=(ld_aglefrom/180.0L)*MY_PI;
    ld_agleto=(ld_agleto/180.0L)*MY_PI;

    /*if (abs(ld_radiusX-ld_radiusY)<=0.000001L)
    {
      long double ld_min, ld_max;
      long double ld_stepX, ld_stepY;
      long double ld_from, ld_into;
      long double ld_r2;
      long double ld_a, ld_agle;

      long double ld_agleMin, ld_agleMax;

      ld_agleMin=ld_aglefrom;
      if (ld_agleto>(2.0L*MY_PI)) ld_agleMax=ld_agleto-2.0L*MY_PI;
      else ld_agleMax=ld_agleto;

      //struct sGerber_Point s_from, s_to;

      ld_min=sin(MY_PI/4.0L);
      ld_max=ld_min*ld_radiusX;
      ld_min*=-ld_radiusX;

      ld_stepX=1.0L/ps_bmp->ld_x_scaler;
      ld_stepY=1.0L/ps_bmp->ld_y_scaler;

      ld_r2=ld_radiusX*ld_radiusX;

      // by X
      if (ps_bmp->i4_debuglevel>0) printf("Module BMP: Debug: OVAL by X: From: %3.4f To: %3.4f Diff: %3.4f Scale: %3.4f\n",
                                          (double)ld_min, (double)ld_max, (double)ld_stepX, (double)ps_bmp->ld_x_scaler);
      ld_from=ld_min;
      ld_into=ld_max;
      do
      {
        ld_a=sqrt(ld_r2-ld_from*ld_from);

        ld_agle=acos(ld_from/ld_radiusX);

        if ((ld_agle>=ld_agleMin)&&(ld_agle<=ld_agleMax))
        {
          BMP_PixelMask_Pixel(ps_bmp, &ps_bmp->s_pixels,
                              floor((ld_from+ps_centre->ld_x)*ps_bmp->ld_x_scaler+0.500000000000L),
                              floor((ld_a+ps_centre->ld_y)*ps_bmp->ld_y_scaler+0.500000000000L),
                              b_positive);
        }
        ld_agle+=MY_PI;
        if ((ld_agle>=ld_agleMin)&&(ld_agle<=ld_agleMax))
        {
          BMP_PixelMask_Pixel(ps_bmp, &ps_bmp->s_pixels,
                              floor((ld_from+ps_centre->ld_x)*ps_bmp->ld_x_scaler+0.500000000000L),
                              floor((-ld_a+ps_centre->ld_y)*ps_bmp->ld_y_scaler+0.500000000000L),
                              b_positive);
        }

        ld_from+=ld_stepX;
      }
      while (ld_from<ld_into);

      // by Y
      if (ps_bmp->i4_debuglevel>0) printf("Module BMP: Debug: OVAL by Y: From: %3.4f To: %3.4f Diff: %3.4f Scale: %3.4f\n",
                                          (double)ld_min, (double)ld_max, (double)ld_stepY, (double)ps_bmp->ld_y_scaler);
      ld_from=ld_min;
      ld_into=ld_max;
      do
      {
        ld_a=sqrt(ld_r2-ld_from*ld_from);

        ld_agle=asin(ld_from/ld_radiusX);

        if (ld_agle<=0.0L) ld_agle+=2.0*MY_PI;

        if ((ld_agle>=ld_agleMin)&&(ld_agle<=ld_agleMax))
        {
          BMP_PixelMask_Pixel(ps_bmp, &ps_bmp->s_pixels,
                              floor((ld_a+ps_centre->ld_x)*ps_bmp->ld_x_scaler+0.500000000000L),
                              floor((ld_from+ps_centre->ld_y)*ps_bmp->ld_y_scaler+0.500000000000L),
                              b_positive);
        }
        if (ld_agle>MY_PI) ld_agle-=0.5L*MY_PI;
        else ld_agle+=0.5L*MY_PI;
        if ((ld_agle>=ld_agleMin)&&(ld_agle<=ld_agleMax))
        {
          BMP_PixelMask_Pixel(ps_bmp, &ps_bmp->s_pixels,
                              floor((-ld_a+ps_centre->ld_x)*ps_bmp->ld_x_scaler+0.500000000000L),
                              floor((ld_from+ps_centre->ld_y)*ps_bmp->ld_y_scaler+0.500000000000L),
                              b_positive);
        }

        ld_from+=ld_stepX;
      }
      while (ld_from<ld_into);
    }
    else*/
    {
      // bmp draw code respected conversion code
      struct sGerber_Point s_a, s_b;
      struct sGerber_Point s_A, s_B;
      //long double ld_radius;

      //ld_radius=ld_radiusX;

      // first point
      s_b.ld_x=cos(ld_aglefrom)*ld_radiusX+ps_centre->ld_x;
      s_b.ld_y=sin(ld_aglefrom)*ld_radiusY+ps_centre->ld_y;
      do
      {
        ld_aglefrom+=((2.0L*MY_PI)/180.0L);

        if (ld_aglefrom>ld_agleto) ld_aglefrom=ld_agleto;

        s_a=s_b;
        s_b.ld_x=cos(ld_aglefrom)*ld_radiusX+ps_centre->ld_x;
        s_b.ld_y=sin(ld_aglefrom)*ld_radiusY+ps_centre->ld_y;

        s_A=s_a;
        s_B=s_b;
        if (LineSegment(v_mem, &s_A, &s_B, true, false)!=0)
        {
          printf("Module BMP: Error: OVAL: Drawing line segment failed.\n");
          return -1;
        }
      }
      while (ld_aglefrom<ld_agleto);
    }
  }
  else
  {
    printf("Module BMP: Error: OVAL: Resize failed.\n");
    return -1;
  }

  return 0;
}

#ifdef __WIN32__
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
  switch (fdwReason)
  {
    case DLL_PROCESS_ATTACH:
      // attach to process
      // return FALSE to fail DLL load
      // inicializace manualni
      break;

    case DLL_PROCESS_DETACH:
      // detach from process
      // sem dat ukoncovaci funkci
      break;

    case DLL_THREAD_ATTACH:
      // attach to thread
      // vytvoreni vlakna
      break;

    case DLL_THREAD_DETACH:
      // detach from thread
      // konec vlakna
      break;
  }
  return TRUE; // succesful
}
#endif // __WIN32__

