/*
 GerberConversion
 Copyright (C) 2010 Martin Štrympl
 
 This program 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 program 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 program.
 
 Martin Štrympl
 xstrym00@stud.feec.vutbr.cz
 */

/*
 *  gerber.c
 *  GerberConversion
 *
 *  Created by Martin Štrympl on 10/31/10.
 *  Copyright 2010 School. All rights reserved.
 *
 */

#include "Gerber.h"

#include "Settings.h"
#include "Module.h"
 
struct sGerber_Gerber_Code
{
  char *t_begin;
  uint32_t u4_len;
  fGerber_Gerber_Code f_code;
};
struct sGerber_Gerber_Settings
{
  char *t_begin;
  uint32_t u4_len;
  fGerber_Gerber_Settings f_settings;
};

struct sGerber_Gerber_Output
{
  DLHANDLE v_lib;
  void *v_memory;
  fGerber_Draw_LineSegment f_linesegment;
  fGerber_Draw_OvalSegment f_ovalsegment;
};

struct sGerber_Gerber
{
  struct sGerber_Gerber_Code * as_codes;
  uint32_t u4_codes;
  
  struct sGerber_Gerber_Settings * as_settings;
  uint32_t u4_settings;
  
  struct sGerber_Gerber_Output * as_outputs;
  uint32_t u4_outputs;
  
  // add gerber actual sets for draw
  struct sGerber_Gerber_Actual s_actual;
};

struct sGerber_Gerber * Gerber_Gerber_Construct()
{
  struct sGerber_Gerber *ps_gerber;
  
  ps_gerber = (struct sGerber_Gerber *)malloc(sizeof(struct sGerber_Gerber));
  
  if (ps_gerber)
  {
    ps_gerber->as_codes=NULL;
    ps_gerber->u4_codes=0;
    
    ps_gerber->as_settings=NULL;
    ps_gerber->u4_settings=0;
    
    ps_gerber->as_outputs=NULL;
    ps_gerber->u4_outputs=0;
    
    
    // default settigns for actual
    ps_gerber->s_actual.s_xy.s_old.ld_x=0.0L;
    ps_gerber->s_actual.s_xy.s_old.ld_y=0.0L;
    ps_gerber->s_actual.s_xy.s_new=ps_gerber->s_actual.s_xy.s_old;
    ps_gerber->s_actual.s_xy.s_offset=ps_gerber->s_actual.s_xy.s_old;
    
    // axis select
    ps_gerber->s_actual.s_as.b_A=true;  // for axis A, X select
    ps_gerber->s_actual.s_as.b_B=false; // for axis B, Y select
    
    // following standard
    ps_gerber->s_actual.s_fs.b_LT=true;
    ps_gerber->s_actual.s_fs.b_AI=true;
    ps_gerber->s_actual.s_fs.u1_X_integer=ps_gerber->s_actual.s_fs.u1_Y_integer=6;
    ps_gerber->s_actual.s_fs.u1_X_decimal=ps_gerber->s_actual.s_fs.u1_Y_decimal=6;
    
    ps_gerber->s_actual.s_fs.u1_N=2;
    ps_gerber->s_actual.s_fs.u1_G=2;
    ps_gerber->s_actual.s_fs.u1_D=2;
    ps_gerber->s_actual.s_fs.u1_M=2;
    
    // image offset
    ps_gerber->s_actual.s_io.ld_A=0.0L;
    ps_gerber->s_actual.s_io.ld_B=0.0L;
    
    // image polarity
    ps_gerber->s_actual.s_ip.b_positive=true;
    
    // image rotation
    ps_gerber->s_actual.s_ir.u2_rot=0;
    
    // layer polarty
    ps_gerber->s_actual.s_lp.b_positive=true;
    
    // mirror
    ps_gerber->s_actual.s_mi.b_A=false;
    ps_gerber->s_actual.s_mi.b_B=false;
    
    // inch
    ps_gerber->s_actual.s_mo.b_inch=true;
    
    // offset
    ps_gerber->s_actual.s_of.ld_A=0.0L;
    ps_gerber->s_actual.s_of.ld_B=0.0L;
    
    // scale
    ps_gerber->s_actual.s_sf.ld_A=1.0L;
    ps_gerber->s_actual.s_sf.ld_B=1.0L;
    
    // sets
    // axis select, image rotation, scale factor, mirror image, image offset, offset, units mode 
    memcpy(ps_gerber->s_actual.s_sets.at_pointnumeringpriority, "aqsmnoi", 8);
    ps_gerber->s_actual.s_sets.b_millimeters=true;
    
    ps_gerber->s_actual.s_sets.i4_debug=0;
    
    // D codes set to unset
    {
      uint32_t u4_n;
      for (u4_n=0;u4_n<DCODES;u4_n++)
      {
        ps_gerber->s_actual.s_dcodes.as_codes[u4_n].f_dcode=NULL;
        //ps_gerber->s_actual.s_dcodes.as_codes[u4_n].v_data=NULL;
      }
    }
    ps_gerber->s_actual.u4_selectedDcode=0;
    ps_gerber->s_actual.f_gcode=Gcode_01;
  }
  
  return ps_gerber;
}
void Gerber_Gerber_Destruct(struct sGerber_Gerber *ps_gerber)
{
  if (ps_gerber)
  {
    uint32_t u4_n;
    
    for (u4_n=0;u4_n<ps_gerber->u4_codes;u4_n++)
    {
      free(ps_gerber->as_codes[u4_n].t_begin);
    }
    if (ps_gerber->as_codes) free(ps_gerber->as_codes);
    
    for (u4_n=0;u4_n<ps_gerber->u4_settings;u4_n++)
    {
      free(ps_gerber->as_settings[u4_n].t_begin);
    }
    if (ps_gerber->as_settings) free(ps_gerber->as_settings);
    
    {
      fGerber_Module_End f_end;
      for (u4_n=0;u4_n<ps_gerber->u4_outputs;u4_n++)
      {
        f_end=(fGerber_Module_End)DLOBJECT(ps_gerber->as_outputs[u4_n].v_lib, "End");
        if (f_end) f_end(ps_gerber->as_outputs[u4_n].v_memory);
      }
      if (ps_gerber->as_outputs) free(ps_gerber->as_outputs);
    }
    
    free(ps_gerber);
  }
}

struct sGerber_Gerber_Actual * Gerber_Gerber_GetActual(struct sGerber_Gerber *ps_gerber)
{
  return &ps_gerber->s_actual;
}

bool Gerber_Gerber_RegisterCode(struct sGerber_Gerber *ps_gerber, const char *t_begin, fGerber_Gerber_Code f_code)
{
  char *t_text;
  uint32_t u4_text;
  
  u4_text=strlen(t_begin)+1;
  t_text = (char *)malloc(u4_text);

  if (t_text)
  {
    struct sGerber_Gerber_Code * ps_mem;
  
    ps_mem = (struct sGerber_Gerber_Code *)realloc(ps_gerber->as_codes, sizeof(struct sGerber_Gerber_Code)*(ps_gerber->u4_codes+1));
  
    if (ps_mem)
    {
      memcpy(t_text, t_begin, u4_text);
      
      ps_gerber->as_codes=ps_mem;
      
      ps_gerber->as_codes[ps_gerber->u4_codes].t_begin=t_text;
      ps_gerber->as_codes[ps_gerber->u4_codes].u4_len=u4_text-1;
      ps_gerber->as_codes[ps_gerber->u4_codes].f_code=f_code;
      
      ps_gerber->u4_codes++;
    
      return true;
    }
    free(t_text);
  }
  
  return false;
}
bool Gerber_Gerber_RegisterSettings(struct sGerber_Gerber *ps_gerber, const char *t_begin, fGerber_Gerber_Settings f_settings)
{
  char *t_text;
  uint32_t u4_text;
  
  u4_text=strlen(t_begin)+1;
  t_text = (char *)malloc(u4_text);
  
  if (t_text)
  {
    struct sGerber_Gerber_Settings * ps_mem;
    
    ps_mem = (struct sGerber_Gerber_Settings *)realloc(ps_gerber->as_settings, sizeof(struct sGerber_Gerber_Settings)*(ps_gerber->u4_settings+1));
    
    if (ps_mem)
    {
      memcpy(t_text, t_begin, u4_text);
      
      ps_gerber->as_settings=ps_mem;
      
      ps_gerber->as_settings[ps_gerber->u4_settings].t_begin=t_text;
      ps_gerber->as_settings[ps_gerber->u4_settings].u4_len=u4_text-1;
      ps_gerber->as_settings[ps_gerber->u4_settings].f_settings=f_settings;
      
      ps_gerber->u4_settings++;
      
      return true;
    }
    free(t_text);
  }
  
  return false;
}
bool Gerber_Gerber_RegisterOutput(struct sGerber_Gerber *ps_gerber, DLHANDLE v_lib, void *v_memory, fGerber_Draw_LineSegment f_linesegment, fGerber_Draw_OvalSegment f_ovalsegment)
{
  struct sGerber_Gerber_Output * ps_mem;
  
  ps_mem = (struct sGerber_Gerber_Output *)realloc(ps_gerber->as_outputs, sizeof(struct sGerber_Gerber_Output)*(ps_gerber->u4_outputs+1));
  
  if (ps_mem)
  {
    ps_gerber->as_outputs=ps_mem;
    
    ps_gerber->as_outputs[ps_gerber->u4_outputs].v_lib=v_lib;
    ps_gerber->as_outputs[ps_gerber->u4_outputs].v_memory=v_memory;
    ps_gerber->as_outputs[ps_gerber->u4_outputs].f_linesegment=f_linesegment;
    ps_gerber->as_outputs[ps_gerber->u4_outputs].f_ovalsegment=f_ovalsegment;
    
    ps_gerber->u4_outputs++;
    
    return true;
  }
  
  return false;
}

int Gerber_Gerber_LineSegment(struct sGerber_Gerber *ps_gerber, struct sGerber_Point *ps_From, struct sGerber_Point *ps_To, bool b_incut)
{
  uint32_t u4_n;
  int i4_ret=0;
  struct sGerber_Point s_From, s_To;
  
  for (u4_n=0;u4_n<ps_gerber->u4_outputs;u4_n++)
  {
    s_From=*ps_From;
    s_To=*ps_To;
    i4_ret=ps_gerber->as_outputs[u4_n].f_linesegment(ps_gerber->as_outputs[u4_n].v_memory, &s_From, &s_To, true, b_incut);
    
    if (i4_ret<0) return i4_ret;
  }
  
  return i4_ret;
}
int Gerber_Gerber_OvalSegment(struct sGerber_Gerber *ps_gerber, struct sGerber_Point *ps_centre, double do_radiusX, double do_radiusY,
                                 double do_aglefrom, double do_agleto, bool b_incut)
{
  uint32_t u4_n;
  int i4_ret=0;
  
  for (u4_n=0;u4_n<ps_gerber->u4_outputs;u4_n++)
  {
    i4_ret=ps_gerber->as_outputs[u4_n].f_ovalsegment(ps_gerber->as_outputs[u4_n].v_memory, ps_centre, do_radiusX, do_radiusY, do_aglefrom, do_agleto, true, b_incut);
    
    if (i4_ret<0) return i4_ret;
  }
  
  return i4_ret;
}

bool Gerber_Gerber_LoadFile(struct sGerber_Gerber *ps_gerber, const char *t_file)
{
  FILE *fi_file;
  
  fi_file=fopen(t_file, "rb");
  
  if (fi_file)
  {
    long i8_size;
    
    fseek(fi_file, 0, SEEK_END);
    
    i8_size=ftell(fi_file);
    
    rewind(fi_file);
    
    if (i8_size>0)
    {
      char *t_file;
      
      t_file=(char *)malloc(i8_size+1);
      
      if (t_file)
      {
        if (fread(t_file, 1, i8_size, fi_file)==i8_size)
        {
          
          fclose(fi_file);
          
          t_file[i8_size]=0;
          
          {
            // load text by lines
            char *t_find;
            bool b_code = true;
            uint32_t u4_n, u4_len, u4_maxlen;
            int i4_ret;
            
            t_find = t_file;
            
            u4_maxlen = strlen(t_find);
            
            while (*t_find)
            {
              if (*t_find=='%')
              {
                b_code=!b_code;
                t_find++;
                continue;
              }
              if (*t_find=='*')
              {
                t_find++;
                continue;
              }
              u4_len = u4_maxlen - (t_find - t_file);
              if (*t_find>0x20)
              {
                if (b_code)
                {
                  for (u4_n=0;u4_n<ps_gerber->u4_codes;u4_n++)
                  {
                    if (u4_len>=ps_gerber->as_codes[u4_n].u4_len)
                    {
                      if (memcmp(t_find, ps_gerber->as_codes[u4_n].t_begin, ps_gerber->as_codes[u4_n].u4_len)==0)
                      {
                        i4_ret=ps_gerber->as_codes[u4_n].f_code(ps_gerber, &ps_gerber->s_actual, &t_find);
                        
                        if (i4_ret==100)
                        {
                          //end file
                          free(t_file);
                          return true;
                        }
                        else
                        {
                          if (i4_ret!=0)
                          {
                            printf("Code %s analyze error.\n", ps_gerber->as_codes[u4_n].t_begin);
                            
                            free(t_file);
                            return false;
                          }
                        }
                        
                        break;
                      }
                    }
                  }
                  if (u4_n>=ps_gerber->u4_codes)
                  {
                    // find not supported codes
                    if (ps_gerber->s_actual.s_behavior.b_ignore_badsets)
                    {
                      char *t_begin = t_find;
                      // find end '*' character
                      while (true)
                      {
                        t_find++;
                        
                        
                        // is it good check for '%' character? 
                        if ((*t_find==0)||(*t_find=='%'))
                        {
                          printf("Gerber format error.\n");
                          
                          free(t_file);
                          return false;
                        }
                        
                        // character '*' ending unknown settings
                        if ((*t_find=='*')||(*t_find<=0x20))
                        {
                          *t_find=0;
                          printf("Ingnoring unknown code '%s'.\n", t_begin);
                          break;
                        }
                        else if ((*t_find<'0')||(*t_find>'9'))
                        {
                          char ch_zl;
                          
                          ch_zl=*t_find;
                          
                           *t_find=0;
                          printf("Ingnoring unknown code '%s'.\n", t_begin);
                          *t_find=ch_zl;
                          t_find--;
                          break;
                        }
                      }
                    }
                    else
                    {
                      printf("Unknown code '%.4s' is detected.\n", t_find);
                      
                      free(t_file);
                      return false;
                    }
                  }
                }
                else
                {
                  for (u4_n=0;u4_n<ps_gerber->u4_settings;u4_n++)
                  {
                    if (u4_len>=ps_gerber->as_settings[u4_n].u4_len)
                    {
                      if (memcmp(t_find, ps_gerber->as_settings[u4_n].t_begin, ps_gerber->as_settings[u4_n].u4_len)==0)
                      {
                        i4_ret=ps_gerber->as_settings[u4_n].f_settings(ps_gerber, &ps_gerber->s_actual, &t_find);
                        
                        if (i4_ret!=0)
                        {
                          printf("Setting %s analyze error.\n", ps_gerber->as_codes[u4_n].t_begin);
                          
                          free(t_file);
                          return false;
                        }
                        
                        break;
                      }
                    }
                  }
                  if (u4_n>=ps_gerber->u4_settings)
                  {
                    // find not supported settings
                    if (ps_gerber->s_actual.s_behavior.b_ignore_badsets)
                    {
                      char *t_begin = t_find;
                      // find end '*' character
                      while (true)
                      {
                        t_find++;
                        
                        
                        // is it good check for '%' character? 
                        if ((*t_find==0)||(*t_find=='%'))
                        {
                          printf("Gerber format error.\n");
                          
                          free(t_file);
                          return false;
                        }
                        
                        // character '*' ending unknown settings
                        if ((*t_find=='*')||(*t_find<=0x20))
                        {
                          *t_find=0;
                          printf("Ingnoring unknown settings '%s'.\n", t_begin);
                          break;
                        }
                      }
                    }
                    else
                    {
                      // is it good check for '%' character? 
                      if ((*t_find==0)||(*t_find=='%'))
                      {
                        printf("Gerber format error.\n");
                        
                        free(t_file);
                        return false;
                      }
                      
                      printf("Unknown settings '%.4s' is detected.\n", t_find);
                      
                      free(t_file);
                      return false;
                    }
                  }
                }
              }
              t_find++;
            }
            
            return true;
            //bool b_rezim;// in % section, or classic D or ozhers codes section
          }
        }
        
        free(t_file);
      }
    }
    
    fclose(fi_file);
  }
  
  return false;
}
