/* gDiagramm.hpp   */
/* class Diagramm */

#define FIELDINT   0
#define FIELDUINT  1
#define FIELDFLOAT 2
#define FIELDCHAR  3
#define FIELDSTRING    4
#define FIELDDOUBLE    5


class Field
{  public:
      int type; // field type
//тип поля:
//integer          0 (d,o,x,X)
//Unsigned integer 1 (u)
//Floating-point   2 (e,f,g,E,G)
//Character        3 (c)
//String           4 (s)
//double           5 (le,lf,lg,lE,lG)
      int use;        //use=1, no=0
      int num;        // field number
      char name[128]; // имя поля
      int color;      // plot RGB color, -1 = rand
      double minf,maxf; // min/max of data
      double amplification;   //  коэффициент усиления
      double shift;   //  сдвиг
      int markType;   // (не=0)отмечать отдельные точки
      int markColor;  // цвет отметки
      int vectorType; // 0, иначе поле есть координата ветора: 1 - начало первого X,  2 начало первого Y, 3 - конец первого X, 4 - конец первого Y, 5  о второго

    Field(void)
    {  name[0] = 0;
       use = 0;
       num = 0;
       type = FIELDSTRING;
       color = -1;
       minf = maxf = 0.;
       markType = 0;
       markColor=-1;
       amplification = 1.;
       shift = 0.;
       vectorType = 0;
    }
/* запись запоминаемых параметров */
   int Write(FILE *fp)
   {  char prefix[20]="field.";
      sprintf(prefix,"field%i.",num);
      fprintf(fp,"%sname=%s\n", prefix,name);
      if(use > 1)
      {   fprintf(fp,"%suse=1\n",  prefix);
          fprintf(fp,"%suseAsX=1\n",  prefix);
      } else {
          fprintf(fp,"%suse=%i\n",  prefix,use);
      }
      fprintf(fp,"%stype=%i\n", prefix,type);
      fprintf(fp,"%scolor=%x\n", prefix,color);
      if(markType)
      {    fprintf(fp,"%smarkType=%i\n", prefix,markType);
           fprintf(fp,"%smarkColor=%i\n", prefix,markColor);
      }
      fprintf(fp,"%samplification=%f\n", prefix,amplification);
      fprintf(fp,"%sshift=%f\n", prefix,shift);


   }

   int AnalizeRecodrRead(char *parname, char *par)// анализ прочитанной строки конфига
   {
      int i,is=0,npar=0;
      static char *psNames[]=
        { "num","name","use","color","markType","markColor","useAsX","amplification","shift","vectorType",
          NULL };

      for(i=0;psNames[i];i++)
      {   if(!strcmp(parname,psNames[i]))
          {  is = 1;
             npar = i;
             break;
          }
      }
      if(!is) return 1;
      switch(npar)
      {  case 0:
           sscanf(par,"%i", &num);
          break;
         case 1:
           strncpy(name,par,sizeof(name)-1);
           name[sizeof(name)-1] = 0;
            break;
         case 2:
             sscanf(par,"%i", &i);
             if(i && use>1) use = 2;
             else use = i;
           break;
         case 3:
           sscanf(par,"%x", &color);
           break;
         case 4:
           sscanf(par,"%x", &markType);
            break;
         case 5:
           sscanf(par,"%x", &markColor);
            break;
         case 6:
           sscanf(par,"%x", &i);
           if(i > 0) use = 2;
            break;
         case 7:
           sscanf(par,"%lf", &amplification);
            break;
         case 8:
           sscanf(par,"%lf", &shift);
            break;
         case 9:
           sscanf(par,"%i", &vectorType);
            break;
      }
      return 0;
   }

};


class Diagramm
{  public:
      int nfields;          /* число полей */
      int nfieldsAlloc;     /* memory allocated for */
      class Field *pfields; /* указатель на nfields * class Field */
      int  *offsets;  /* массив длиной nfields со смещениями полей данных */
      char *databuff; /* буфер данных */
      int  Lendatabuf;/* длина буфера данных */
      int  recordLen; /* длина записи в буфере данных */
      int  NumRec;    /* число записей */
      char * *strptrbuf; /* буфер указателей на память для временных строковых переменных, длиной nfields (для простоты)*/
      char *strBuff;     /* буфер для строк */
      char *pstrBuff;    /* указатель на свободное место в буфере строк */
      int  LenstrBuff;   /* длина буфера для строк */
      char FormatStr[256];   /* format string */
      char FormName[256];    /* Form's file name */
      char AppName[256];     /* Application Name */
      char FileOut[256];     /* Output filename  */
      char Title[80];        /* Title */
      int nfieldUsedAsX;     /* index of field, used as X */
      double MinX,MaxX,MinY,MaxY;
      double mashtX, mashtY;
      int DimX,DimY;     /* sizeof diagramm in pixels*/
      int BackColor;     /* Background Color */
      int TitleColor;    /* Title Color */
      int TitleFont;     /* Title Font 0-4 */
      int AxesColor;     /* Axes Color */
      int AxesNumColor;  /* Axes Numbers Color */
      int AxesNumFont;   /* Axes Numbers Font */
/* Grid */
      int GridColor;     /* grid color       */
      int GridXon;
      int GridYon;
      double GridXStart, GridXStep;
      double GridYStart, GridYStep;
      char FormatX[80];   /* format string for X axis */
      char FormatY[80];   /* format string for Y axis */
      int LegendUse;       /* Legend */
      int LegendStartX;
      int LegendStartY;


   Diagramm(void)
   {  nfields = 0;
      pfields = NULL;
      offsets = NULL;
      nfieldsAlloc = 0;
      databuff = NULL;
      strptrbuf = NULL;
      Lendatabuf = 0;
      recordLen = 0;
      LenstrBuff = 0;
      strBuff = NULL;
      NumRec = 0;
      Title[0] = 0;
      BackColor  = 0xb0b0b0;
      GridColor  = 0x808080;
      TitleColor = 0xa00000;
      TitleFont  = 2;
      AxesColor = 0x303030;
      AxesNumColor = 0xa02020;
      AxesNumFont=1;
      DimX = DimY = 64;
      mashtX = mashtY = 1.;
      GridXon = GridYon = 1;
      GridXStart = 0.;
      GridXStep = 1.;
      GridYStart = 0.;
      GridYStep  = 1.;
      nfieldUsedAsX = -1;
      strcpy(FormatX,"%.f");
      strcpy(FormatY,"%.f");
      strcpy(FormatStr,"%f %f");
      strcpy(FileOut,"demo.png");
      LegendUse=1;
      LegendStartX = -1;
      LegendStartY = -1;
   }
   int AddField(int type)
   {  int l;
      class Field tmp;

      if(pfields == NULL)
      {   nfieldsAlloc = 16;
          l = sizeof(class Field);
          pfields = (Field *) calloc(l,nfieldsAlloc);
          offsets = (int *)   calloc(sizeof(int),nfieldsAlloc+1);
          strptrbuf = (char * *) calloc(sizeof(char *),nfieldsAlloc+1);
      } else if(nfieldsAlloc <= nfields+1) {
          nfieldsAlloc += 16;
          pfields = (Field *) realloc((void *)pfields, sizeof(class Field) *nfieldsAlloc);
          offsets  = (int *) realloc((void *)offsets , sizeof(int) *(nfieldsAlloc+1));
          strptrbuf = (char * *) realloc((void *)strptrbuf , sizeof(char *) *(nfieldsAlloc+1));
      }
      tmp.type = type;
      tmp.num  = nfields;
      pfields[nfields] = tmp;

      nfields++;
      return 0;
   }

   int AddData(int iy, char *str)
   {   char  *pdata;
       int i, n;
       void *ptr[64];

       pdata = databuff + iy * recordLen;
       for(i=0;i<nfields;i++)
       {  ptr[i] = (void *)(pdata+offsets[i]);
          if(pfields[i].type == FIELDSTRING)
          { *((char * *) pdata+offsets[i]) = strptrbuf[i];
            ptr[i] = (void *)(strptrbuf[i]);
          }
       }
       n = sscanf(str,FormatStr,ptr[0],ptr[1],ptr[2],ptr[3],ptr[4],ptr[5],ptr[6],ptr[7],ptr[8],ptr[9],
                                ptr[10],ptr[11],ptr[12],ptr[13],ptr[14],ptr[15],ptr[16],ptr[17],ptr[18],ptr[19],
                                ptr[20],ptr[21],ptr[22],ptr[23],ptr[24],ptr[25],ptr[26],ptr[27],ptr[28],ptr[29],
                                ptr[30],ptr[31],ptr[32],ptr[33],ptr[34],ptr[35],ptr[36],ptr[37],ptr[38],ptr[39],
                                ptr[40],ptr[41],ptr[42],ptr[43],ptr[44],ptr[45],ptr[46],ptr[47],ptr[48],ptr[49],
                                ptr[50],ptr[51],ptr[52],ptr[53],ptr[54],ptr[55],ptr[56],ptr[57],ptr[58],ptr[59]);
       for(i=0;i<nfields;i++)
       {  ptr[i] = (void *)(pdata+offsets[i]);
          if(pfields[i].type == FIELDSTRING)
          {  strcpy(pstrBuff,strptrbuf[i]);
             *((char * *) pdata+offsets[i]) = pstrBuff;
             pstrBuff += strlen(strptrbuf[i]);
//             printf("%s ",strptrbuf[i]);
             *pstrBuff = 0;
             pstrBuff++;
          }
       }
       NumRec++;
       return 0;
   }
   int CalcMinMax(void)
   {  int i,j, startX=1, startY=1;
       void *ptr[64];
      char  *pdata, cd;
      int id;
      float fd;
      double dd;
      int iGridXStep,nx50,ny50;
      double lgxstep, tmp, k;

      MinX = 0;
      MaxX = NumRec;
      MinY = MaxY = 0.;
/* определяем, есть ли nfieldUsedAsX */
      for(j=0;j<nfields;j++)
      {   if(!pfields[j].use) continue;
           if(pfields[j].use > 1)
           {  nfieldUsedAsX = j;
              break;
           }
      }

      for(i=0;i<NumRec;i++)
      {
         pdata = databuff + i * recordLen;
         for(j=0;j<nfields;j++)
         {   if(!pfields[j].use) continue;
             dd = GetValue(i,j);

             if(nfieldUsedAsX == j || ((pfields[j].vectorType&0x01) == 1) )
             {  if(startX)
                { MinX = MaxX = dd; startX = 0;
                }
                if(dd < MinX) MinX = dd;
                if(dd > MaxX) MaxX = dd;
             } else {
                if(startY)
                { MinY = MaxY = dd; startY = 0;
                }
                if(dd < MinY) MinY = dd;
                if(dd > MaxY) MaxY = dd;
             }

         }
      }

      nx50 = (DimX-32)/5;
      ny50 = (DimY-32)/4;

         GridXStart = MinX;
         GridXStep = (MaxX-MinX)/nx50;

         lgxstep = log10(GridXStep);
         iGridXStep = (int) lgxstep;
         if(lgxstep < 0.) iGridXStep--;
         tmp = pow(10.,(double)iGridXStep);

         k = GridXStep / tmp;
         if(k < 1.1) k = 1.;
         else if(k < 2.5) k = 2.;
         else if(k < 6.) k = 5.;
         else k = 10.;

         GridXStep = k * tmp;
         iGridXStep = int(MinX/tmp * 1000);
         GridXStart = iGridXStep/1000.*tmp;

         GridYStart = MinY;
         GridYStep = (MaxY-MinY)/ny50;

         lgxstep = log10(GridYStep);
         iGridXStep = (int) lgxstep;
         if(lgxstep < 0.) iGridXStep--;
         tmp = pow(10.,(double)iGridXStep);

         k = GridYStep / tmp;
         if(k < 1.1) k = 1.;
         else if(k < 2.5) k = 2.;
         else if(k < 6.) k = 5.;
         else k = 10.;
         GridYStep = k * tmp;
         iGridXStep = int(MinY/(GridYStep*10));
         GridYStart = iGridXStep * GridYStep*10;


      return 0;
   }

   double  GetValue(int nrec, int ifield)
   {  int i,j;
       void *ptr[64];
      char  *pdata, cd;
      int id;
      float fd;
      double dd=0.;

         pdata = databuff + nrec * recordLen;
         j = ifield;
         {
             switch(pfields[j].type)
             {  case FIELDINT:
                    id = *((int *) (pdata + offsets[j]));
                    dd = double(id);
                    if(pfields[j].shift != 0.)
                          dd += pfields[j].shift;
                    if(pfields[j].amplification != 1.)
                          dd *= pfields[j].amplification;
                   break;
                case FIELDUINT:
                    id = *((int *) (pdata + offsets[j]));
                    dd = double(id);
                    if(pfields[j].shift != 0.)
                          dd += pfields[j].shift;
                    if(pfields[j].amplification != 1.)
                          dd *= pfields[j].amplification;
                   break;
                case FIELDFLOAT:
                    fd = *((float *) (pdata + offsets[j]));
                    dd = double(fd);
                    if(pfields[j].shift != 0.)
                          dd += pfields[j].shift;
                    if(pfields[j].amplification != 1.)
                          dd *= pfields[j].amplification;
                   break;
                case FIELDCHAR:
                    cd = *((char *) (pdata + offsets[j]));
                    dd = double(cd);
                    if(pfields[j].shift != 0.)
                          dd += pfields[j].shift;
                    if(pfields[j].amplification != 1.)
                          dd *= pfields[j].amplification;
                   break;
                case FIELDSTRING:
                   break;
                case FIELDDOUBLE:
                    dd = *((double *) (pdata + offsets[j]));
                    if(pfields[j].shift != 0.)
                          dd += pfields[j].shift;
                    if(pfields[j].amplification != 1.)
                          dd *= pfields[j].amplification;
                   break;
             }
         }
         return dd;
   }

   int PlotData(void);
   int PlotMark(gdImagePtr im, int x, int y, int type, int color);

   /* Read and analyze  formfile(project) */
   int  Read(char *fname)
   {  int id,rc,rc0,nf, NstrAnalyzed=0;
      FILE *fp;
      char str[256],nameClass[128],name[128],par[128], *pstr;

      fp = fopen(fname,"r");
      if(fp == NULL)
         return 1;
      strcpy(FormName,fname);
      do
      {  rc = ReadStr(fp, str,nameClass,name,par);
         if(!strcmpi(nameClass,"form"))
         {    AnalizeRecodrRead(name,par);
              NstrAnalyzed++;
         }  else if(strstr(nameClass,"field")) {

            pstr = nameClass;
            pstr += strlen("field");
            rc0 = sscanf(pstr,"%i", &nf);
            if(rc0 != 1 || nf < 0 || nf >= nfields) nf = 0;
            pfields[nf].AnalizeRecodrRead(name,par);
            NstrAnalyzed++;
         }

      } while(!rc);

      fclose(fp);
      rc = 0;
      if(!NstrAnalyzed)    //Bad form file
      {  rc = 2;
      }
      return rc;
   }

   /* Read string from formfile(project), ignoger comments,
      and parse string ClassName.Name=par to ClassName Name par
    */
   int ReadStr(FILE *fp, char *str, char *nameClass, char *name, char *par )
   {  int i,iscomment,l;
      char *pstr;
/* читаем строку */
   M: pstr= fgets(str,128,fp);
      if(pstr == NULL) return 1; //EOF
/* игнорируем строки нулевой длины */
      l = strlen(str);
      if(str[l-1] == '\n')
                  str[--l] = 0;
      if(l == 0) goto M;
/* игнорируем комментарии */
      iscomment = 0;
      for(i=0;i<l;i++)
      { if(str[i] > 32)
         {   if(str[i] == ';') iscomment = 1;
             break;
         }
      }
      if(iscomment) goto M;
      pstr = strstr(str,"=");
      if(pstr == NULL)
         goto M; // игнорируем строки без "="
      *pstr = 0;
      strcpy(par,pstr+1);      // читаем параметры
      pstr = strstr(str,".");  // ищем точку для определения имени класса
      if(pstr == NULL)
          goto M; // игнорируем строки без "."
      *pstr = 0;
      sscanf(str,"%s",nameClass);
      sscanf(pstr+1,"%s",name); // читаем имя параметра
      return 0;
   }

/* запись запоминаемых параметров */
   int Write(FILE *fp)
   {  char *prefix="form.";
      struct tm *newtime;
      int i;
      time_t cur_t = time(NULL);

      /* Get the time in seconds */
       time(&cur_t);

      /* Convert it to the structure tm */
       newtime = localtime(&cur_t);

      fprintf(fp,";%s\n",AppName);
      fprintf(fp,";%s %s",FormName,asctime(newtime));
      fprintf(fp,";format string\n");
      fprintf(fp,"%sFormat=%s\n", prefix,FormatStr);
      fprintf(fp,"%sDimX=%i\n",  prefix,DimX);
      fprintf(fp,"%sDimY=%i\n",  prefix,DimY);
      fprintf(fp,"%sFileOut=%s\n",  prefix,FileOut);

      fprintf(fp,"%sBackColor=%x\n",  prefix,BackColor);
      fprintf(fp,"%sTitle=%s\n",  prefix,Title);

      fprintf(fp,"%sGridColor=%x\n",prefix,GridColor);
      fprintf(fp,"%sGridXon=%i\n",  prefix,GridXon);
      fprintf(fp,"%sGridYon=%i\n",  prefix,GridYon);
      fprintf(fp,"%sFormatX=%s\n",  prefix,FormatX);
      fprintf(fp,"%sFormatY=%s\n",  prefix,FormatY);


      fprintf(fp,";field properties\n");
      for(i=0;i<nfields;i++)
      {   pfields[i].Write(fp);
      }

       return 0;
   }

   int AnalizeRecodrRead(char *name, char *par)// анализ прочитанной строки конфига
   {
      int i,is=0,npar=0;
      int j,l;
      static char *psNames[]=
        { "Format","DimX","DimY","Title","BackColor",
          "GridColor","TitleColor", "TitleFont", "GridXon",   "GridYon",
          "FormatX",   "FormatY",   "FileOut",   "LegendUse", "LegendStartX",
          "LegendStartY","AxesColor","AxesNumColor","AxesNumFont",
          NULL
        };

     for(i=0;psNames[i];i++)
     {   if(!strcmp(name,psNames[i]))
         {  is = 1;
            npar = i;
            break;
         }
     }
     if(!is) return 1;
     switch(npar)
     {  case 0:  /* UstavkaCurrent */
           strcpy(FormatStr,par);
/***************************************************/
        /* анализируем format string */
         l = strlen(FormatStr);
         for(i=0;i<l;i++)
         {  if(FormatStr[i] != '%') continue;
            for(j=i+1;j<l;j++)
            {
               if(FormatStr[j] == 'i' || FormatStr[j] == 'd' || FormatStr[j] == 'o' ||
                  FormatStr[j] == 'x' || FormatStr[j] == 'X')
                {  AddField(FIELDINT);  i = j;
                    break;
                }
               if(FormatStr[j] == 'u' )
                {  AddField(FIELDUINT); i = j;
                   break;
                }
               if(FormatStr[j] == 'e' || FormatStr[j] == 'f' || FormatStr[j] == 'g' ||
                  FormatStr[j] == 'E' || FormatStr[j] == 'G')
                {  if(FormatStr[j-1] == 'l')
                           AddField(FIELDDOUBLE);
                   else
                           AddField(FIELDFLOAT);
                   i = j;
                   break;
                }
               if(FormatStr[j] == 'c' )
                {  AddField(FIELDCHAR);   i = j;
                    break;
                }
               if(FormatStr[j] == 's' )
                {  AddField(FIELDSTRING); i = j;
                   break;
                }
            } //endof for(j
         } //endof for(i

/* рассчитываем смещения */
        offsets[0] = 0;
        for(i=1;i<=nfields;i++)
        {   j = 4;
            if(pfields[i-1].type == FIELDCHAR) j = 1;
            else
                 if(pfields[i-1].type == FIELDDOUBLE) j = sizeof(double);
            offsets[i] = offsets[i-1] + j;
        }
        recordLen = offsets[nfields];

/***************************************************/
         break;
        case 1:
           sscanf(par,"%i", &DimX);
           break;
        case 2:
           sscanf(par,"%i", &DimY);
           break;
        case 3:
           strcpy(Title,par);
           break;
        case 4:
           sscanf(par,"%x", &BackColor);
           break;
        case 5:
           sscanf(par,"%x", &GridColor);
           break;
        case 6:
           sscanf(par,"%x", &TitleColor);
           break;
        case 7: //   TitleFont
          if(!stricmp(par,"T"))      TitleFont = 0;
          else if(!stricmp(par,"S")) TitleFont = 1;
          else if(!stricmp(par,"M")) TitleFont = 2;
          else if(!stricmp(par,"L")) TitleFont = 3;
          else if(!stricmp(par,"G")) TitleFont = 4;
           break;
        case 8:
           sscanf(par,"%x", &GridXon);
           break;
        case 9:
           sscanf(par,"%x", &GridYon);
           break;
        case 10:
           strcpy(FormatX, par);
           break;
        case 11:
           strcpy(FormatY, par);
           break;
        case 12:
           strcpy(FileOut, par);
           break;
        case 13:
           sscanf(par,"%i", &LegendUse);
           break;
        case 14:
           sscanf(par,"%i", &LegendStartX);
           break;
        case 15:
           sscanf(par,"%i", &LegendStartY);
           break;
        case 16:
           sscanf(par,"%x", &AxesColor);
           break;
        case 17:
           sscanf(par,"%x", &AxesNumColor);
           break;
        case 18:
          if(!stricmp(par,"T"))      AxesNumFont = 0;
          else if(!stricmp(par,"S")) AxesNumFont = 1;
          else if(!stricmp(par,"M")) AxesNumFont = 2;
          else if(!stricmp(par,"L")) AxesNumFont = 3;
          else if(!stricmp(par,"G")) AxesNumFont = 4;
           break;
     }
     return 0;
  }



};
