/*
***************************************************************************
*
* Author: Teunis van Beelen
*
* Copyright (C) 2007 - 2024 Teunis van Beelen
*
* Email: teuniz@protonmail.com
*
***************************************************************************
*
* 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, version 3 of the License.
*
* 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.  If not, see <http://www.gnu.org/licenses/>.
*
***************************************************************************
*/



#include "check_edf_file.h"


static long long get_long_duration(char *);


edfhdrblck_t * check_edf_file(FILE *inputfile, char *txt_string, int txt_len, int live_stream, int import_annots_active)
{
  int i, j, p, r=0, n,
      dotposition,
      error;

  char *edf_hdr=NULL,
       scratchpad_128[128],
       scratchpad_64[64];

  long long l_tmp,
            l_tmp2;

  date_time_t date_time;

  edfhdrblck_t *edfhdr=NULL;

/***************** check header ******************************/

  fseeko(inputfile, 0LL, SEEK_END);
  l_tmp = ftello(inputfile);
  if(l_tmp < 512LL)
  {
    snprintf(txt_string, txt_len, "Error, filesize is less than 512 bytes");
    return NULL;
  }

/***************** check header ******************************/

  edf_hdr = (char *)calloc(1, 256);
  if(edf_hdr==NULL)
  {
    snprintf(txt_string, txt_len, "Memory allocation error. (edf_hdr)");
    return NULL;
  }

  edfhdr = (edfhdrblck_t *)calloc(1, sizeof(edfhdrblck_t));
  if(edfhdr==NULL)
  {
    snprintf(txt_string, txt_len, "Memory allocation error. (edfhdr)");
    free(edf_hdr);
    return NULL;
  }

  rewind(inputfile);
  if(fread(edf_hdr, 256, 1, inputfile)!=1)
  {
    snprintf(txt_string, txt_len, "Error, cannot read 256 bytes from file");
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

/**************************** VERSION ***************************************/

  strncpy(scratchpad_128, edf_hdr, 8);
  scratchpad_128[8] = 0;

  if(((signed char *)scratchpad_128)[0]==-1)   // BDF-file
  {
    for(i=1; i<8; i++)
    {
      if((scratchpad_128[i]<32)||(scratchpad_128[i]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of version field is not a valid 7-bit ASCII character.",
        i + 1);
        free(edf_hdr);
        free(edfhdr);
        return NULL;
      }
    }

    if(strcmp(scratchpad_128 + 1, "BIOSEMI"))
    {
      snprintf(txt_string, txt_len, "Error, header has unknown version: \".%s\",\n"
            "expected \".BIOSEMI\"",
            scratchpad_128 + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }

    edfhdr->bdf = 1;
  }
  else    // EDF-file
  {
    for(i=0; i<8; i++)
    {
      if((scratchpad_128[i]<32)||(scratchpad_128[i]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of version field is not a valid 7-bit ASCII character.",
        i + 1);
        free(edf_hdr);
        free(edfhdr);
        return NULL;
      }
    }

    if(strcmp(scratchpad_128, "0       "))
    {
      snprintf(txt_string, txt_len, "Error, header has unknown version: \"%s\",\n"
            "expected \"0       \"",
            scratchpad_128);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }

    edfhdr->edf = 1;
  }

  strncpy(edfhdr->version, edf_hdr, 8);
  edfhdr->version[8] = 0;
  if(edfhdr->bdf)  edfhdr->version[0] = '.';

/********************* PATIENTNAME *********************************************/

  strncpy(scratchpad_128, edf_hdr + 8, 80);
  scratchpad_128[80] = 0;
  for(i=0; i<80; i++)
  {
    if((((unsigned char *)scratchpad_128)[i]<32)||(((unsigned char *)scratchpad_128)[i]>126))
    {
      snprintf(txt_string, txt_len, "Error, %ith character of local patient identification field is not a valid 7-bit ASCII character.\n"
                          "You can repair the file with the header editor, have a look at the manual for the details.",
      i + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

  strncpy(edfhdr->patient, edf_hdr + 8, 80);
  edfhdr->patient[80] = 0;

/********************* RECORDING *********************************************/

  strncpy(scratchpad_128, edf_hdr + 88, 80);
  scratchpad_128[80] = 0;
  for(i=0; i<80; i++)
  {
    if((((unsigned char *)scratchpad_128)[i]<32)||(((unsigned char *)scratchpad_128)[i]>126))
    {
      snprintf(txt_string, txt_len, "Error, %ith character of local recording identification field is not a valid 7-bit ASCII character.\n"
                          "You can repair the file with the header editor, have a look at the manual for the details.",
      i + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

  strncpy(edfhdr->recording, edf_hdr + 88, 80);
  edfhdr->recording[80] = 0;

/********************* STARTDATE *********************************************/

  strncpy(scratchpad_128, edf_hdr + 168, 8);
  scratchpad_128[8] = 0;
  for(i=0; i<8; i++)
  {
    if((scratchpad_128[i]<32)||(scratchpad_128[i]>126))
    {
      snprintf(txt_string, txt_len, "Error, %ith character of startdate field is not a valid 7-bit ASCII character.",
      i + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

  error = 0;

  if((edf_hdr[170]!='.')||(edf_hdr[173]!='.'))
  {
    snprintf(txt_string, txt_len, "Error, separator character of startdate is not a dot.\n"
                        "You can repair the file with the header editor, check the manual for details.");
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  if((edf_hdr[168]<48)||(edf_hdr[168]>57))      error = 1;
  if((edf_hdr[169]<48)||(edf_hdr[169]>57))      error = 1;
  if((edf_hdr[171]<48)||(edf_hdr[171]>57))      error = 1;
  if((edf_hdr[172]<48)||(edf_hdr[172]>57))      error = 1;
  if((edf_hdr[174]<48)||(edf_hdr[174]>57))      error = 1;
  if((edf_hdr[175]<48)||(edf_hdr[175]>57))      error = 1;
  strncpy(scratchpad_128, edf_hdr + 168, 8);

  if(error)
  {
    scratchpad_128[8] = 0;
    snprintf(txt_string, txt_len, "Error, startdate of recording is invalid: \"%s\",\n"
           "expected \"dd.mm.yy\"",
           scratchpad_128);
    free(edf_hdr);
    return NULL;
  }

  scratchpad_128[2] = 0;
  scratchpad_128[5] = 0;
  scratchpad_128[8] = 0;

  if((atoi(scratchpad_128)<1)||(atoi(scratchpad_128)>31))
  {
    strncpy(scratchpad_128, edf_hdr + 168, 8);
    scratchpad_128[8] = 0;
    snprintf(txt_string, txt_len, "Error, startdate of recording is invalid: \"%s\",\n"
           "expected \"dd.mm.yy\" where \"dd\" should be more than 00 and less than 32",
           scratchpad_128);
    free(edf_hdr);
    return NULL;
  }

  if((atoi(scratchpad_128+3)<1)||(atoi(scratchpad_128+3)>12))
  {
    strncpy(scratchpad_128, edf_hdr + 168, 8);
    scratchpad_128[8] = 0;
    snprintf(txt_string, txt_len, "Error, startdate of recording is invalid: \"%s\",\n"
           "expected \"dd.mm.yy\" where \"mm\" should be more than 00 and less than 13",
           scratchpad_128);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  date_time.day = atoi(scratchpad_128);
  date_time.month = atoi(scratchpad_128 + 3);
  date_time.year = atoi(scratchpad_128 + 6);
  if(date_time.year>84)
  {
    date_time.year += 1900;
  }
  else
  {
    date_time.year += 2000;
  }

/********************* STARTTIME *********************************************/

  strncpy(scratchpad_128, edf_hdr + 176, 8);
  scratchpad_128[8] = 0;
  for(i=0; i<8; i++)
  {
    if((scratchpad_128[i]<32)||(scratchpad_128[i]>126))
    {
      snprintf(txt_string, txt_len, "Error, %ith character of starttime field is not a valid 7-bit ASCII character.",
      i + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

  error = 0;

  if((edf_hdr[178]!='.')||(edf_hdr[181]!='.'))
  {
    snprintf(txt_string, txt_len, "Error, separator character of starttime is not a dot.\n"
                        "You can repair the file with the header editor, check the manual for details.");
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  if((edf_hdr[176]<48)||(edf_hdr[176]>57))      error = 1;
  if((edf_hdr[177]<48)||(edf_hdr[177]>57))      error = 1;
  if((edf_hdr[179]<48)||(edf_hdr[179]>57))      error = 1;
  if((edf_hdr[180]<48)||(edf_hdr[180]>57))      error = 1;
  if((edf_hdr[182]<48)||(edf_hdr[182]>57))      error = 1;
  if((edf_hdr[183]<48)||(edf_hdr[183]>57))      error = 1;

  strncpy(scratchpad_128, edf_hdr + 176, 8);

  if(error)
  {
    strncpy(scratchpad_128, edf_hdr + 176, 8);
    scratchpad_128[8] = 0;
    snprintf(txt_string, txt_len, "Error, starttime of recording is invalid: \"%s\",\n"
           "expected \"hh.mm.ss\"",
           scratchpad_128);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  scratchpad_128[2] = 0;
  scratchpad_128[5] = 0;
  scratchpad_128[8] = 0;

  if(atoi(scratchpad_128)>23)
  {
    strncpy(scratchpad_128, edf_hdr + 176, 8);
    scratchpad_128[8] = 0;
    snprintf(txt_string, txt_len, "Error, starttime of recording is invalid: \"%s\",\n"
           "expected \"hh.mm.ss\" where \"hh\" should be less than 24",
           scratchpad_128);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  if(atoi(scratchpad_128+3)>59)
  {
    strncpy(scratchpad_128, edf_hdr + 176, 8);
    scratchpad_128[8] = 0;
    snprintf(txt_string, txt_len, "Error, starttime of recording is invalid: \"%s\",\n"
           "expected \"hh.mm.ss\" where \"mm\" should be less than 60",
           scratchpad_128);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  if(atoi(scratchpad_128+6)>59)
  {
    strncpy(scratchpad_128, edf_hdr + 176, 8);
    scratchpad_128[8] = 0;
    snprintf(txt_string, txt_len, "Error, starttime of recording is invalid: \"%s\",\n"
           "expected \"hh.mm.ss\" where \"ss\" should be less than 60",
           scratchpad_128);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  date_time.hour = atoi(scratchpad_128);
  date_time.minute = atoi(scratchpad_128 + 3);
  date_time.second = atoi(scratchpad_128 + 6);

  date_time_to_utc(&edfhdr->utc_starttime, date_time);

  edfhdr->utc_starttime_hr = edfhdr->utc_starttime * TIME_FIXP_SCALING;

  edfhdr->starttime_hr = 3600 * atoi(scratchpad_128);
  edfhdr->starttime_hr += 60 * atoi(scratchpad_128 + 3);
  edfhdr->starttime_hr += atoi(scratchpad_128 + 6);

  edfhdr->starttime_hr *= TIME_FIXP_SCALING;

/***************** NUMBER OF SIGNALS IN HEADER *******************************/

  strncpy(scratchpad_128, edf_hdr + 252, 4);
  scratchpad_128[4] = 0;
  for(i=0; i<4; i++)
  {
    if((scratchpad_128[i]<32)||(scratchpad_128[i]>126))
    {
      snprintf(txt_string, txt_len, "Error, %ith character of number of signals field is not a valid 7-bit ASCII character.",
      i + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

  if(is_integer_number(scratchpad_128))
  {
    snprintf(txt_string, txt_len, "Error, number of signals field is invalid: \"%s\".",
           scratchpad_128);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }
  edfhdr->edfsignals = atoi(scratchpad_128);
  if(edfhdr->edfsignals<1)
  {
    snprintf(txt_string, txt_len, "Error, number of signals is %i, expected >0.",
           edfhdr->edfsignals);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  if(edfhdr->edfsignals>MAXSIGNALS)
  {
    snprintf(txt_string, txt_len, "Error, number of signals in file is %i, cannot support more than %i signals.",
           edfhdr->edfsignals,
           MAXSIGNALS);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

/***************** NUMBER OF BYTES IN HEADER *******************************/

  strncpy(scratchpad_128, edf_hdr + 184, 8);
  scratchpad_128[8] = 0;

  for(i=0; i<8; i++)
  {
    if((scratchpad_128[i]<32)||(scratchpad_128[i]>126))
    {
      snprintf(txt_string, txt_len, "Error, %ith character of number of bytes in header field is not a valid 7-bit ASCII character.",
      i + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

  if(is_integer_number(scratchpad_128))
  {
    snprintf(txt_string, txt_len, "Error, number of bytes in header field is invalid: \"%s\".",
           scratchpad_128);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  n  = atoi(scratchpad_128);
  if((edfhdr->edfsignals * 256 + 256)!=n)
  {
    snprintf(txt_string, txt_len, "Error, number of bytes in header does not match: %i,\n"
           "expected %i signals * 256 + 256 = %i bytes.",
           n, edfhdr->edfsignals, edfhdr->edfsignals * 256 + 256);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

/********************* RESERVED FIELD *************************************/

  edfhdr->edfplus = 0;
  edfhdr->discontinuous = 0;
  strncpy(scratchpad_128, edf_hdr + 192, 44);
  scratchpad_128[44] = 0;

  for(i=0; i<44; i++)
  {
    if((scratchpad_128[i]<32)||(scratchpad_128[i]>126))
    {
      snprintf(txt_string, txt_len, "Error, %ith character of reserved field is not a valid 7-bit ASCII character.",
      i + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

  if(edfhdr->edf)
  {
    if(!strcmp(scratchpad_128, "EDF+C                                       "))
    {
      edfhdr->edfplus = 1;
      edfhdr->discontinuous = 0;
    }
    else if(!strcmp(scratchpad_128, "EDF+D                                       "))
      {
        edfhdr->edfplus = 1;
        edfhdr->discontinuous = 1;
      }
      else if(strcmp(scratchpad_128, "                                            "))
      {
        snprintf(txt_string, txt_len, "Error, reserved field contains unrecognized data:\n"
                                      " \n"
                                      "\"%s\"\n"
                                      " \n"
                                      "You can fix this by using the EDF header editor.\n"
                                      "Have a look at the manual for details.",
                                      scratchpad_128);
        free(edf_hdr);
        free(edfhdr);
        return NULL;
      }
  }

  if(edfhdr->bdf)
  {
    if(!strcmp(scratchpad_128, "BDF+C                                       "))
    {
      edfhdr->bdfplus = 1;
      edfhdr->discontinuous = 0;
    }
    else if(!strcmp(scratchpad_128, "BDF+D                                       "))
      {
        edfhdr->bdfplus = 1;
        edfhdr->discontinuous = 1;
      }
      else if(!strcmp(scratchpad_128, "24BIT                                       "))
        {
          edfhdr->bdfplus = 0;
          edfhdr->discontinuous = 0;
        }
        else if(strcmp(scratchpad_128, "                                            "))
          {
            snprintf(txt_string, txt_len, "Error, reserved field contains unrecognized data:\n"
                                          " \n"
                                          "\"%s\"\n"
                                          " \n"
                                          "You can fix this by using the EDF header editor.\n"
                                          "Have a look at the manual for details.",
                                          scratchpad_128);
            free(edf_hdr);
            free(edfhdr);
            return NULL;
          }
  }

  strncpy(edfhdr->reserved, edf_hdr + 192, 44);
  edfhdr->reserved[44] = 0;

/********************* NUMBER OF DATARECORDS *************************************/

  strncpy(scratchpad_128, edf_hdr + 236, 8);
  scratchpad_128[8] = 0;

  for(i=0; i<8; i++)
  {
    if((scratchpad_128[i]<32)||(scratchpad_128[i]>126))
    {
      snprintf(txt_string, txt_len, "Error, %ith character of number of datarecords field is not a valid ASCII character.",
      i + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

  if(is_integer_number(scratchpad_128))
  {
    snprintf(txt_string, txt_len, "Error, number of datarecords field is invalid: \"%s\".\n"
                        "You can repair the file with the header editor, have a look at the manual for the details.",
           scratchpad_128);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  edfhdr->datarecords = atoi(scratchpad_128);
  if(live_stream == 0)
  {
    if(edfhdr->datarecords<1)
    {
#ifdef __MINGW32__
      __mingw_snprintf(txt_string, txt_len, "Error, number of datarecords is %lli, expected >0.\n"
                          "You can repair the file with the header editor, have a look at the manual for the details.",
            edfhdr->datarecords);
#else
      snprintf(txt_string, txt_len, "Error, number of datarecords is %lli, expected >0.\n"
                          "You can repair the file with the header editor, have a look at the manual for the details.",
            edfhdr->datarecords);
#endif
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

/********************* DATARECORD DURATION *************************************/

  strncpy(scratchpad_128, edf_hdr + 244, 8);
  scratchpad_128[8] = 0;

  for(i=0; i<8; i++)
  {
    if((scratchpad_128[i]<32)||(scratchpad_128[i]>126))
    {
      snprintf(txt_string, txt_len, "Error, %ith character of recordduration field is not a valid 7-bit ASCII character.",
      i + 1);
      free(edf_hdr);
      free(edfhdr);
      return NULL;
    }
  }

  if(is_number(scratchpad_128))
  {
    snprintf(txt_string, txt_len, "Error, record duration field is invalid: \"%s\".",
            scratchpad_128);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  edfhdr->data_record_duration = atof(scratchpad_128);
  if(dblcmp(edfhdr->data_record_duration, 0) == 0)
  {
    edfhdr->data_record_duration = 0;
    edfhdr->long_data_record_duration = 0LL;
    edfhdr->recording_duration_hr = 0LL;
    edfhdr->recording_len_sec = 0;
  }
  else
  {
    if(edfhdr->data_record_duration<-0.000001)
    {
      snprintf(txt_string, txt_len, "Error, datarecord duration is less than 0");
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }

    edfhdr->long_data_record_duration = get_long_duration(scratchpad_128);

    edfhdr->recording_duration_hr = edfhdr->long_data_record_duration * edfhdr->datarecords;

    edfhdr->recording_len_sec = (int)(edfhdr->recording_duration_hr / TIME_FIXP_SCALING);

    edfhdr->utc_endtime_hr = edfhdr->utc_starttime_hr + edfhdr->recording_duration_hr;
  }

  free(edf_hdr);

/********************* START WITH THE SIGNALS IN THE HEADER *********************/

  edf_hdr = (char *)calloc(1, (edfhdr->edfsignals + 1) * 256);
  if(edf_hdr==NULL)
  {
    snprintf(txt_string, txt_len, "Memory allocation error. (edf_hdr takes %i bytes)", (edfhdr->edfsignals + 1) * 256);
    free(edfhdr);
    return NULL;
  }

  rewind(inputfile);
  if(fread(edf_hdr, (edfhdr->edfsignals + 1) * 256, 1, inputfile)!=1)
  {
    snprintf(txt_string, txt_len, "Error, reading %i bytes from file", (edfhdr->edfsignals + 1) * 256);
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

  edfhdr->edfparam = (edfpar_t *)calloc(1, sizeof(edfpar_t[edfhdr->edfsignals]));
  if(edfhdr->edfparam==NULL)
  {
    snprintf(txt_string, txt_len, "Memory allocation error! (edfparam)");
    free(edf_hdr);
    free(edfhdr);
    return NULL;
  }

/**************************** LABELS *************************************/

  edfhdr->nr_annot_chns = 0;
  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (i * 16), 16);
    for(j=0; j<16; j++)
    {
      if((scratchpad_128[j]<32)||(scratchpad_128[j]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of label field of signal %i is not a valid 7-bit ASCII character.\n"
                            "You can repair the file with the header editor, have a look at the manual for the details.",
        j + 1,
        i + 1);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }
    if(!strncmp(scratchpad_128, "EDF Annotations ", 16))
    {
      if(edfhdr->edfplus)
      {
        edfhdr->annot_ch[edfhdr->nr_annot_chns] = i;
        edfhdr->nr_annot_chns++;
        edfhdr->edfparam[i].annotation = 1;
      }
      else
      {
        scratchpad_128[3] = '_';
      }
    }
    if(!strncmp(scratchpad_128, "BDF Annotations ", 16))
    {
      if(edfhdr->bdfplus)
      {
        edfhdr->annot_ch[edfhdr->nr_annot_chns] = i;
        edfhdr->nr_annot_chns++;
        edfhdr->edfparam[i].annotation = 1;
      }
      else
      {
        scratchpad_128[3] = '_';
      }
    }
    memcpy(edfhdr->edfparam[i].label, scratchpad_128, 16);
    edfhdr->edfparam[i].label[16] = 0;
  }
  if(edfhdr->edfplus&&(!edfhdr->nr_annot_chns))
  {
    snprintf(txt_string, txt_len, "Error, file is marked as EDF+ but it has no annotations-signal.");
    free(edf_hdr);
    free(edfhdr->edfparam);
    free(edfhdr);
    return NULL;
  }
  if(edfhdr->bdfplus&&(!edfhdr->nr_annot_chns))
  {
    snprintf(txt_string, txt_len, "Error, file is marked as BDF+ but it has no annotations-signal.");
    free(edf_hdr);
    free(edfhdr->edfparam);
    free(edfhdr);
    return NULL;
  }
  if((!import_annots_active) && (edfhdr->long_data_record_duration == 0LL))
  {
    if((edfhdr->edfplus)||(edfhdr->bdfplus))
    {
      snprintf(txt_string, txt_len, "Datarecord duration of this file is 0.\n"
                                    "If you want to import annotations from this file,\n"
                                    "go to Tools -> Import annotations/events and select\n"
                                    "tab \"EDF+/BDF+\"");
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }
    else
    {
      snprintf(txt_string, txt_len, "Error, datarecord duration is invalid, should be > 0");
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }
  }

/**************************** TRANSDUCER TYPES *************************************/

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (edfhdr->edfsignals * 16) + (i * 80), 80);
    for(j=0; j<80; j++)
    {
      if((scratchpad_128[j]<32)||(scratchpad_128[j]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of transducer type field of signal %i is not a valid 7-bit ASCII character.\n"
                            "You can repair the file with the header editor, have a look at the manual for the details.",
        j + 1,
        i + 1);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }
    strncpy(edfhdr->edfparam[i].transducer, edf_hdr + 256 + (edfhdr->edfsignals * 16) + (i * 80), 80);
    edfhdr->edfparam[i].transducer[80] = 0;

    if(edfhdr->edfplus)
    {
      if(edfhdr->edfparam[i].annotation)
      {
        for(j=0; j<80; j++)
        {
          if(edfhdr->edfparam[i].transducer[j]!=' ')
          {
            snprintf(txt_string, txt_len, "Error, transducer type field of EDF annotationchannel must be empty.\n"
                                "You can repair the file with the header editor, have a look at the manual for the details.");
            free(edf_hdr);
            free(edfhdr->edfparam);
            free(edfhdr);
            return NULL;
          }
        }
      }
    }
    if(edfhdr->bdfplus)
    {
      if(edfhdr->edfparam[i].annotation)
      {
        for(j=0; j<80; j++)
        {
          if(edfhdr->edfparam[i].transducer[j]!=' ')
          {
            snprintf(txt_string, txt_len, "Error, transducer type field of BDF annotationchannel must be empty."
                                "You can repair the file with the header editor, have a look at the manual for the details.");
            free(edf_hdr);
            free(edfhdr->edfparam);
            free(edfhdr);
            return NULL;
          }
        }
      }
    }
  }

/**************************** PHYSICAL DIMENSIONS *************************************/

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (edfhdr->edfsignals * 96) + (i * 8), 8);
    for(j=0; j<8; j++)
    {
      if((scratchpad_128[j]<32)||(scratchpad_128[j]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of physical dimension field of signal %i is not a valid 7-bit ASCII character.\n"
                            "You can repair the file with the header editor, have a look at the manual for the details.",
        j + 1,
        i + 1);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }
    strncpy(edfhdr->edfparam[i].physdimension, edf_hdr + 256 + (edfhdr->edfsignals * 96) + (i * 8), 8);
    edfhdr->edfparam[i].physdimension[8] = 0;
  }

/**************************** PHYSICAL MINIMUMS *************************************/

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (edfhdr->edfsignals * 104) + (i * 8), 8);
    scratchpad_128[8] = 0;

    for(j=0; j<8; j++)
    {
      if((scratchpad_128[j]<32)||(scratchpad_128[j]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of physical minimum field of signal %i is not a valid 7-bit ASCII character.",
        j + 1,
        i + 1);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }

    if(is_number(scratchpad_128))
    {
      snprintf(txt_string, txt_len, "Error, physical minimum field of signal %i is invalid: \"%s\".\n"
                          "You can try to repair the file with the header editor, check the manual for the procedure.",
             i + 1,
             scratchpad_128);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }

    edfhdr->edfparam[i].phys_min = atof(scratchpad_128);
  }

/**************************** PHYSICAL MAXIMUMS *************************************/

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (edfhdr->edfsignals * 112) + (i * 8), 8);
    scratchpad_128[8] = 0;

    for(j=0; j<8; j++)
    {
      if((scratchpad_128[j]<32)||(scratchpad_128[j]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of physical maximum field of signal %i is not a valid 7-bit ASCII character.",
        j + 1,
        i + 1);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }

    if(is_number(scratchpad_128))
    {
      snprintf(txt_string, txt_len, "Error, physical maximum field of signal %i is invalid: \"%s\".\n"
                          "You can try to repair the file with the header editor, check the manual for the procedure.",
             i + 1,
             scratchpad_128);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }

    edfhdr->edfparam[i].phys_max = atof(scratchpad_128);
    if(edfhdr->edfparam[i].phys_max==edfhdr->edfparam[i].phys_min)
    {
      snprintf(txt_string, txt_len, "Error, physical maximum of signal %i is equal to physical minimum\n"
                          "You can try to repair the file with the header editor, check the manual for the procedure.",
             i + 1);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }
  }

/**************************** DIGITAL MINIMUMS *************************************/

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (edfhdr->edfsignals * 120) + (i * 8), 8);
    scratchpad_128[8] = 0;

    for(j=0; j<8; j++)
    {
      if((scratchpad_128[j]<32)||(scratchpad_128[j]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of digital minimum field of signal %i is not a valid 7-bit ASCII character.",
        j + 1,
        i + 1);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }

    if(is_integer_number(scratchpad_128))
    {
      snprintf(txt_string, txt_len, "Error, digital minimum field of signal %i is invalid: \"%s\".\n"
                          "You can repair the file with the header editor, have a look at the manual for the details.",
             i + 1,
             scratchpad_128);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }

    n = atoi(scratchpad_128);
    if(edfhdr->edfplus)
    {
      if(edfhdr->edfparam[i].annotation)
      {
        if(n!=-32768)
        {
          snprintf(txt_string, txt_len, "Error, digital minimum of signal %i is wrong: %i,"
                              "\ndigital minimum of an EDF Annotations signal should be -32768\n"
                              "You can repair the file with the header editor, have a look at the manual for the details.",
                i + 1,
                n);
          free(edf_hdr);
          free(edfhdr->edfparam);
          free(edfhdr);
          return NULL;
        }
      }
    }
    if(edfhdr->bdfplus)
    {
      if(edfhdr->edfparam[i].annotation)
      {
        if(n!=-8388608)
        {
          snprintf(txt_string, txt_len, "Error, digital minimum of signal %i is wrong: %i,"
                              "\ndigital minimum of a BDF Annotations signal should be -8388608\n"
                              "You can repair the file with the header editor, have a look at the manual for the details.",
                i + 1,
                n);
          free(edf_hdr);
          free(edfhdr->edfparam);
          free(edfhdr);
          return NULL;
        }
      }
    }
    if(edfhdr->edf)
    {
      if((n>32767)||(n<-32768))
      {
        snprintf(txt_string, txt_len, "Error, digital minimum of signal %i is out of range: %i,\nshould be in the range -32768 to 32767",
              i + 1,
              n);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }
    if(edfhdr->bdf)
    {
      if((n>8388607)||(n<-8388608))
      {
        snprintf(txt_string, txt_len, "Error, digital minimum of signal %i is out of range: %i,\nshould be in the range -8388608 to 8388607",
              i + 1,
              n);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }
    edfhdr->edfparam[i].dig_min = n;
  }

/**************************** DIGITAL MAXIMUMS *************************************/

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (edfhdr->edfsignals * 128) + (i * 8), 8);
    scratchpad_128[8] = 0;

    for(j=0; j<8; j++)
    {
      if((scratchpad_128[j]<32)||(scratchpad_128[j]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of digital maximum field of signal %i is not a valid 7-bit ASCII character.",
        j + 1,
        i + 1);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }

    if(is_integer_number(scratchpad_128))
    {
      snprintf(txt_string, txt_len, "Error, digital maximum field of signal %i is invalid: \"%s\".\n"
                          "You can repair the file with the header editor, have a look at the manual for the details.",
             i + 1,
             scratchpad_128);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }

    n = atoi(scratchpad_128);
    if(edfhdr->edfplus)
    {
      if(edfhdr->edfparam[i].annotation)
      {
        if(n!=32767)
        {
          snprintf(txt_string, txt_len, "Error, digital maximum of signal %i is wrong: %i,"
                              "\ndigital maximum of an EDF Annotations signal should be 32767\n"
                              "You can repair the file with the header editor, have a look at the manual for the details.",
                i + 1,
                n);
          free(edf_hdr);
          free(edfhdr->edfparam);
          free(edfhdr);
          return NULL;
        }
      }
    }
    if(edfhdr->bdfplus)
    {
      if(edfhdr->edfparam[i].annotation)
      {
        if(n!=8388607)
        {
          snprintf(txt_string, txt_len, "Error, digital maximum of signal %i is wrong: %i,"
                              "\ndigital maximum of a BDF Annotations signal should be 8388607\n"
                              "You can repair the file with the header editor, have a look at the manual for the details.",
                i + 1,
                n);
          free(edf_hdr);
          free(edfhdr->edfparam);
          free(edfhdr);
          return NULL;
        }
      }
    }
    if(edfhdr->edf)
    {
      if((n>32767)||(n<-32768))
      {
        snprintf(txt_string, txt_len, "Error, digital maximum of signal %i is out of range: %i,\nshould be in the range -32768 to 32767",
              i + 1,
              n);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }
    else
    {
      if((n>8388607)||(n<-8388608))
      {
        snprintf(txt_string, txt_len, "Error, digital maximum of signal %i is out of range: %i,\nshould be in the range -8388608 to 8388607",
              i + 1,
              n);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }
    edfhdr->edfparam[i].dig_max = n;
    if(edfhdr->edfparam[i].dig_max<(edfhdr->edfparam[i].dig_min + 1))
    {
      snprintf(txt_string, txt_len, "Error, digital maximum of signal %i is less than or equal to digital minimum.\n"
                          "You can repair the file with the header editor, have a look at the manual for the details.",
             i + 1);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }
  }

/**************************** PREFILTER FIELDS *************************************/

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (edfhdr->edfsignals * 136) + (i * 80), 80);
    for(j=0; j<80; j++)
    {
      if((scratchpad_128[j]<32)||(scratchpad_128[j]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of prefilter field of signal %i is not a valid 7-bit ASCII character.\n"
                            "You can repair the file with the header editor, have a look at the manual for the details.",
        j + 1,
        i + 1);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }
    strncpy(edfhdr->edfparam[i].prefilter, edf_hdr + 256 + (edfhdr->edfsignals * 136) + (i * 80), 80);
    edfhdr->edfparam[i].prefilter[80] = 0;

    if(edfhdr->edfplus)
    {
      if(edfhdr->edfparam[i].annotation)
      {
        for(j=0; j<80; j++)
        {
          if(edfhdr->edfparam[i].prefilter[j]!=' ')
          {
            snprintf(txt_string, txt_len, "Error, prefilter field of EDF annotationchannel must be empty.\n"
                                "You can repair the file with the header editor, have a look at the manual for the details.");
            free(edf_hdr);
            free(edfhdr->edfparam);
            free(edfhdr);
            return NULL;
          }
        }
      }
    }
    if(edfhdr->bdfplus)
    {
      if(edfhdr->edfparam[i].annotation)
      {
        for(j=0; j<80; j++)
        {
          if(edfhdr->edfparam[i].prefilter[j]!=' ')
          {
            snprintf(txt_string, txt_len, "Error, prefilter field of BDF annotationchannel must be empty.\n"
                                "You can repair the file with the header editor, have a look at the manual for the details.");
            free(edf_hdr);
            free(edfhdr->edfparam);
            free(edfhdr);
            return NULL;
          }
        }
      }
    }
  }

/*********************** NR OF SAMPLES IN EACH DATARECORD ********************/

  edfhdr->recordsize = 0;

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (edfhdr->edfsignals * 216) + (i * 8), 8);
    scratchpad_128[8] = 0;

    for(j=0; j<8; j++)
    {
      if((scratchpad_128[j]<32)||(scratchpad_128[j]>126))
      {
        snprintf(txt_string, txt_len, "Error, %ith character of number of samples field of signal %i is not a valid 7-bit ASCII character.",
        j + 1,
        i + 1);
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }
    }

    if(is_integer_number(scratchpad_128))
    {
      snprintf(txt_string, txt_len, "Error, number of samples in datarecord field of signal %i is invalid: \"%s\".",
             i + 1,
             scratchpad_128);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }

    n = atoi(scratchpad_128);
    if(n<1)
    {
      snprintf(txt_string, txt_len, "Error, number of samples in datarecord of signal %i is out of range: %i,\nshould be >0",
             i + 1,
             n);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }
    edfhdr->edfparam[i].smp_per_record = n;
    edfhdr->recordsize += n;

    if(edfhdr->long_data_record_duration == 0)
    {
      edfhdr->edfparam[i].sf_int = 0;

      edfhdr->edfparam[i].sf_f = 0;
    }
    else
    {
      edfhdr->edfparam[i].sf_f = (double)(edfhdr->edfparam[i].smp_per_record) / edfhdr->data_record_duration;

      if(((long long)(edfhdr->edfparam[i].smp_per_record) * TIME_FIXP_SCALING) % edfhdr->long_data_record_duration)
      {
        edfhdr->edfparam[i].sf_int = 0;
      }
      else
      {
        edfhdr->edfparam[i].sf_int = ((long long)(edfhdr->edfparam[i].smp_per_record) * TIME_FIXP_SCALING) / edfhdr->long_data_record_duration;
      }
    }

    edfhdr->edfparam[i].smpls = edfhdr->edfparam[i].smp_per_record * edfhdr->datarecords;

    if(edfhdr->long_data_record_duration == 0)
    {
      if(!edfhdr->edfparam[i].annotation)
      {
        if(edfhdr->edfparam[i].smp_per_record > 1)
        {
          snprintf(txt_string, txt_len, "Error, Datarecord duration is 0 but number of samples in datarecord of signal %i is out of range: %i,\nshould be 1",
                 i + 1,
                 n);
          free(edf_hdr);
          free(edfhdr->edfparam);
          free(edfhdr);
          return NULL;
        }
      }
    }
  }

  if(edfhdr->bdf)
  {
    edfhdr->recordsize *= 3;

    if(edfhdr->recordsize > 15728640)
    {
      snprintf(txt_string, txt_len, "Error, the datarecordsize is %i bytes. The datarecordsize should not exceed 15 MB.",
                          edfhdr->recordsize);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }
  }
  else
  {
    edfhdr->recordsize *= 2;

    if(edfhdr->recordsize > 10485760)
    {
      snprintf(txt_string, txt_len, "Error, the datarecordsize is %i bytes. The datarecordsize should not exceed 10 MB.",
                          edfhdr->recordsize);
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }
  }

/**************************** RESERVED FIELDS *************************************/

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    strncpy(scratchpad_128, edf_hdr + 256 + (edfhdr->edfsignals * 224) + (i * 32), 32);
    if(edfhdr->edf || edfhdr->bdfplus)
    {
      for(j=0; j<32; j++)
      {
        if(scratchpad_128[j] != ' ')
        {
          snprintf(txt_string, txt_len, "Error, %ith character of reserved field of signal %i is not a space.\n"
                                        "You can repair the file with the header editor, have a look at the manual for the details.",
          j + 1,
          i + 1);
          free(edf_hdr);
          free(edfhdr->edfparam);
          free(edfhdr);
          return NULL;
        }
      }
    }
    else
    {
      for(j=0; j<32; j++)
      {
        if((scratchpad_128[j] < 32) || (scratchpad_128[j] > 126))
        {
          snprintf(txt_string, txt_len, "Error, %ith character of reserved field of signal %i is not a valid 7-bit ASCII character.\n"
                                        "You can repair the file with the header editor, have a look at the manual for the details.",
          j + 1,
          i + 1);
          free(edf_hdr);
          free(edfhdr->edfparam);
          free(edfhdr);
          return NULL;
        }
      }
    }
    strncpy(edfhdr->edfparam[i].reserved, edf_hdr + 256 + (edfhdr->edfsignals * 224) + (i * 32), 32);
    edfhdr->edfparam[i].reserved[32] = 0;
  }

/********************* EDF+ PATIENTNAME *********************************************/

  if(edfhdr->edfplus || edfhdr->bdfplus)
  {
    error = 0;
    dotposition = 0;
    strncpy(scratchpad_128, edf_hdr + 8, 80);
    scratchpad_128[80] = 0;
    for(i=0; i<80; i++)
    {
      if(scratchpad_128[i]==' ')
      {
        dotposition = i;
        break;
      }
    }
    dotposition++;
    if((dotposition>73)||(dotposition<2))  error = 1;
    if(scratchpad_128[dotposition + 2]!='X')
    {
      if(dotposition>65)  error = 1;
    }
    if((scratchpad_128[dotposition]!='M')&&(scratchpad_128[dotposition]!='F')&&(scratchpad_128[dotposition]!='X'))  error = 1;
    dotposition++;
    if(scratchpad_128[dotposition]!=' ')  error = 1;
    if(scratchpad_128[dotposition + 1]=='X')
    {
      if(scratchpad_128[dotposition + 2]!=' ')  error = 1;
      if(scratchpad_128[dotposition + 3]==' ')  error = 1;
    }
    else
    {
      if(scratchpad_128[dotposition + 12]!=' ')  error = 1;
      if(scratchpad_128[dotposition + 13]==' ')  error = 1;
      dotposition++;
      strncpy(scratchpad_64, scratchpad_128 + dotposition, 11);
      scratchpad_64[11] = 0;
      if((scratchpad_64[2]!='-')||(scratchpad_64[6]!='-'))  error = 1;
      scratchpad_64[2] = 0;
      scratchpad_64[6] = 0;
      if((scratchpad_64[0]<48)||(scratchpad_64[0]>57))  error = 1;
      if((scratchpad_64[1]<48)||(scratchpad_64[1]>57))  error = 1;
      if((scratchpad_64[7]<48)||(scratchpad_64[7]>57))  error = 1;
      if((scratchpad_64[8]<48)||(scratchpad_64[8]>57))  error = 1;
      if((scratchpad_64[9]<48)||(scratchpad_64[9]>57))  error = 1;
      if((scratchpad_64[10]<48)||(scratchpad_64[10]>57))  error = 1;
      if((atoi(scratchpad_64)<1)||(atoi(scratchpad_64)>31))  error = 1;
      if(strcmp(scratchpad_64 + 3, "JAN"))
        if(strcmp(scratchpad_64 + 3, "FEB"))
          if(strcmp(scratchpad_64 + 3, "MAR"))
            if(strcmp(scratchpad_64 + 3, "APR"))
              if(strcmp(scratchpad_64 + 3, "MAY"))
                if(strcmp(scratchpad_64 + 3, "JUN"))
                  if(strcmp(scratchpad_64 + 3, "JUL"))
                    if(strcmp(scratchpad_64 + 3, "AUG"))
                      if(strcmp(scratchpad_64 + 3, "SEP"))
                        if(strcmp(scratchpad_64 + 3, "OCT"))
                          if(strcmp(scratchpad_64 + 3, "NOV"))
                            if(strcmp(scratchpad_64 + 3, "DEC"))
                              error = 1;
    }

    if(error)
    {
      if(edfhdr->edfplus)
      {
        snprintf(txt_string, txt_len, "Error, file is marked as EDF+ but local patient identification field does not\ncomply to the EDF+ standard:\n"
              "\"%.80s\"",
              edf_hdr+8);
      }
      if(edfhdr->bdfplus)
      {
        snprintf(txt_string, txt_len, "Error, file is marked as BDF+ but local patient identification field does not\ncomply to the BDF+ standard:\n"
              "\"%.80s\"",
              edf_hdr+8);
      }
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }

    p = 0;
    if((edfhdr->patient[p]=='X') && (edfhdr->patient[p+1]==' '))
    {
      edfhdr->plus_patientcode[0] = 0;
      p += 2;
    }
    else
    {
      for(i=0; i<(80-p); i++)
      {
        if(edfhdr->patient[i+p]==' ')
        {
          break;
        }
        edfhdr->plus_patientcode[i] = edfhdr->patient[i+p];
        if(edfhdr->plus_patientcode[i]=='_')  edfhdr->plus_patientcode[i] = ' ';
      }
      edfhdr->plus_patientcode[i] = 0;
      p += i + 1;
    }

    edfhdr->plus_sex_offset = p;

    if(edfhdr->patient[p]=='M')
    {
      strlcpy(edfhdr->plus_sex, "Male", 16);
    }
    if(edfhdr->patient[p]=='F')
    {
      strlcpy(edfhdr->plus_sex, "Female", 16);
    }
    if(edfhdr->patient[p]=='X')
    {
      edfhdr->plus_sex[0] = 0;
    }
    for(i=0; i<(80-p);i++)
    {
      if(edfhdr->patient[i+p]==' ')
      {
        break;
      }
    }
    p += i + 1;

    edfhdr->plus_birthdate_offset = p;

    if(edfhdr->patient[p]=='X')
    {
      edfhdr->plus_birthdate[0] = 0;
      p += 2;
    }
    else
    {
      for(i=0; i<(80-p); i++)
      {
        if(edfhdr->patient[i+p]==' ')
        {
          break;
        }
        edfhdr->plus_birthdate[i] = edfhdr->patient[i+p];
      }
      edfhdr->plus_birthdate[2] = ' ';
      edfhdr->plus_birthdate[3] += 32;
      edfhdr->plus_birthdate[4] += 32;
      edfhdr->plus_birthdate[5] += 32;
      edfhdr->plus_birthdate[6] = ' ';
      edfhdr->plus_birthdate[11] = 0;
      p += i + 1;
    }

    edfhdr->plus_patient_name_offset = p;

    for(i=0; i<(80-p);i++)
    {
      if(edfhdr->patient[i+p]==' ')
      {
        break;
      }
      edfhdr->plus_patient_name[i] = edfhdr->patient[i+p];
      if(edfhdr->plus_patient_name[i]=='_')  edfhdr->plus_patient_name[i] = ' ';
    }
    edfhdr->plus_patient_name[i] = 0;
    p += i + 1;

    edfhdr->plus_patient_additional_offset = p;

    for(i=0; i<(80-p);i++)
    {
      edfhdr->plus_patient_additional[i] = edfhdr->patient[i+p];
    }
    edfhdr->plus_patient_additional[i] = 0;
    p += i + 1;
  }

/********************* EDF+ RECORDINGFIELD *********************************************/

  if(edfhdr->edfplus || edfhdr->bdfplus)
  {
    error = 0;
    strncpy(scratchpad_128, edf_hdr + 88, 80);
    scratchpad_128[80] = 0;
    if(strncmp(scratchpad_128, "Startdate ", 10))  error = 1;

    edfhdr->plus_startdate_offset = 10;

    if(scratchpad_128[10]=='X')
    {
      if(scratchpad_128[11]!=' ')  error = 1;
      if(scratchpad_128[12]==' ')  error = 1;
      p = 12;
    }
    else
    {
      if(scratchpad_128[21]!=' ')  error = 1;
      if(scratchpad_128[22]==' ')  error = 1;
      p = 22;
      strncpy(scratchpad_64, scratchpad_128 + 10, 11);
      scratchpad_64[11] = 0;
      if((scratchpad_64[2]!='-')||(scratchpad_64[6]!='-'))  error = 1;
      scratchpad_64[2] = 0;
      scratchpad_64[6] = 0;
      if((scratchpad_64[0]<48)||(scratchpad_64[0]>57))  error = 1;
      if((scratchpad_64[1]<48)||(scratchpad_64[1]>57))  error = 1;
      if((scratchpad_64[7]<48)||(scratchpad_64[7]>57))  error = 1;
      if((scratchpad_64[8]<48)||(scratchpad_64[8]>57))  error = 1;
      if((scratchpad_64[9]<48)||(scratchpad_64[9]>57))  error = 1;
      if((scratchpad_64[10]<48)||(scratchpad_64[10]>57))  error = 1;
      if((atoi(scratchpad_64)<1)||(atoi(scratchpad_64)>31))  error = 1;
      r = 0;
      if(!strcmp(scratchpad_64 + 3, "JAN"))  r = 1;
        else if(!strcmp(scratchpad_64 + 3, "FEB"))  r = 2;
          else if(!strcmp(scratchpad_64 + 3, "MAR"))  r = 3;
            else if(!strcmp(scratchpad_64 + 3, "APR"))  r = 4;
              else if(!strcmp(scratchpad_64 + 3, "MAY"))  r = 5;
                else if(!strcmp(scratchpad_64 + 3, "JUN"))  r = 6;
                  else if(!strcmp(scratchpad_64 + 3, "JUL"))  r = 7;
                    else if(!strcmp(scratchpad_64 + 3, "AUG"))  r = 8;
                      else if(!strcmp(scratchpad_64 + 3, "SEP"))  r = 9;
                        else if(!strcmp(scratchpad_64 + 3, "OCT"))  r = 10;
                          else if(!strcmp(scratchpad_64 + 3, "NOV"))  r = 11;
                            else if(!strcmp(scratchpad_64 + 3, "DEC"))  r = 12;
                              else error = 1;
    }

    n = 0;
    for(i=p; i<80; i++)
    {
      if(i>78)
      {
        error = 1;
        break;
      }
      if(scratchpad_128[i]==' ')
      {
        n++;
        if(scratchpad_128[i + 1]==' ')
        {
          error = 1;
          break;
        }
      }
      if(n>1)  break;
    }

    if(error)
    {
      if(edfhdr->edfplus)
      {
        snprintf(txt_string, txt_len, "Error, file is marked as EDF+ but recording field does not comply to the\nEDF+ standard:\n"
              "\"%.80s\"",
              edf_hdr+88);
      }
      if(edfhdr->bdfplus)
      {
        snprintf(txt_string, txt_len, "Error, file is marked as BDF+ but recording field does not comply to the\nBDF+ standard:\n"
              "\"%.80s\"",
              edf_hdr+88);
      }
      free(edf_hdr);
      free(edfhdr->edfparam);
      return NULL;
    }

    if(edf_hdr[98]!='X')
    {
      error = 0;

      strncpy(scratchpad_128, edf_hdr + 168, 8);
      scratchpad_128[2] = 0;
      scratchpad_128[5] = 0;
      scratchpad_128[8] = 0;

      if(atoi(scratchpad_128)!=atoi(scratchpad_64))  error = 1;
      if(atoi(scratchpad_128+3)!=r)  error = 1;
      if(atoi(scratchpad_128+6)!=atoi(scratchpad_64+9))  error = 1;
      if(error)
      {
        if(edfhdr->edfplus)
        {
          snprintf(txt_string, txt_len, "Error, file is marked as EDF+ but startdate field does not match with startdate in\nrecordfield:\n"
                "\"%.8s\" <-> \"%.11s\".",
                edf_hdr+168,
                edf_hdr+98);
        }
        if(edfhdr->bdfplus)
        {
          snprintf(txt_string, txt_len, "Error, file is marked as BDF+ but startdate field does not match with startdate in\nrecordfield:\n"
                "\"%.8s\" <-> \"%.11s\".",
                edf_hdr+168,
                edf_hdr+98);
        }
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }

      date_time.year = atoi(scratchpad_64 + 7);

      if(date_time.year<1985)
      {
        snprintf(txt_string, txt_len, "Error, recording startdate is before 1985.");
        free(edf_hdr);
        free(edfhdr->edfparam);
        free(edfhdr);
        return NULL;
      }

      date_time_to_utc(&edfhdr->utc_starttime, date_time);
    }

    p = 10;
    for(i=0; i<(80-p); i++)
    {
      if(edfhdr->recording[i+p]==' ')
      {
        break;
      }
      edfhdr->plus_startdate[i] = edfhdr->recording[i+p];
    }
    edfhdr->plus_startdate[2] = ' ';
    edfhdr->plus_startdate[3] += 32;
    edfhdr->plus_startdate[4] += 32;
    edfhdr->plus_startdate[5] += 32;
    edfhdr->plus_startdate[6] = ' ';
    edfhdr->plus_startdate[11] = 0;
    p += i + 1;

    edfhdr->plus_admincode_offset = p;

    if((edfhdr->recording[p]=='X') && (edfhdr->recording[p+1]==' '))
    {
      edfhdr->plus_admincode[0] = 0;
      p += 2;
    }
    else
    {
      for(i=0; i<(80-p); i++)
      {
        if(edfhdr->recording[i+p]==' ')
        {
          break;
        }
        edfhdr->plus_admincode[i] = edfhdr->recording[i+p];
        if(edfhdr->plus_admincode[i]=='_')  edfhdr->plus_admincode[i] = ' ';
      }
      edfhdr->plus_admincode[i] = 0;
      p += i + 1;
    }

    edfhdr->plus_technician_offset = p;

    if((edfhdr->recording[p]=='X') && (edfhdr->recording[p+1]==' '))
    {
      edfhdr->plus_technician[0] = 0;
      p += 2;
    }
    else
    {
      for(i=0; i<(80-p); i++)
      {
        if(edfhdr->recording[i+p]==' ')
        {
          break;
        }
        edfhdr->plus_technician[i] = edfhdr->recording[i+p];
        if(edfhdr->plus_technician[i]=='_')  edfhdr->plus_technician[i] = ' ';
      }
      edfhdr->plus_technician[i] = 0;
      p += i + 1;
    }

    edfhdr->plus_equipment_offset = p;

    if((edfhdr->recording[p]=='X') && (edfhdr->recording[p+1]==' '))
    {
      edfhdr->plus_equipment[0] = 0;
      p += 2;
    }
    else
    {
      for(i=0; i<(80-p); i++)
      {
        if(edfhdr->recording[i+p]==' ')
        {
          break;
        }
        edfhdr->plus_equipment[i] = edfhdr->recording[i+p];
        if(edfhdr->plus_equipment[i]=='_')  edfhdr->plus_equipment[i] = ' ';
      }
      edfhdr->plus_equipment[i] = 0;
      p += i + 1;
    }

    edfhdr->plus_recording_additional_offset = p;

    for(i=0; i<(80-p);i++)
    {
      edfhdr->plus_recording_additional[i] = edfhdr->recording[i+p];
    }
    edfhdr->plus_recording_additional[i] = 0;
    p += i + 1;
  }

/********************* FILESIZE *********************************************/

  edfhdr->hdrsize = edfhdr->edfsignals * 256 + 256;

  if(live_stream)
  {
    fseeko(inputfile, 0LL, SEEK_END);

    edfhdr->datarecords = ftello(inputfile);

    edfhdr->datarecords -= (long long)edfhdr->hdrsize;

    edfhdr->datarecords /= (long long)edfhdr->recordsize;
  }
  else
  {
    l_tmp = edfhdr->recordsize;
    l_tmp *= edfhdr->datarecords;
    l_tmp += (edfhdr->edfsignals * 256 + 256);

    fseeko(inputfile, 0LL, SEEK_END);
    l_tmp2 = ftello(inputfile);

    if(l_tmp != l_tmp2)
    {
#ifdef __MINGW32__
      __mingw_snprintf(txt_string, txt_len, "Error, filesize does not match with the calculated filesize based on the parameters\n"
                          "in the header. Filesize is %lli and filesize according to header is %lli.\n"
                          "You can repair the file with the header editor, have a look at the manual for the details.",
                          l_tmp2, l_tmp);
#else
      snprintf(txt_string, txt_len, "Error, filesize does not match with the calculated filesize based on the parameters\n"
                          "in the header. Filesize is %lli and filesize according to header is %lli.\n"
                          "You can repair the file with the header editor, have a look at the manual for the details.",
                          l_tmp2, l_tmp);
#endif
      free(edf_hdr);
      free(edfhdr->edfparam);
      free(edfhdr);
      return NULL;
    }
  }

  n = 0;

  for(i=0; i<edfhdr->edfsignals; i++)
  {
    edfhdr->edfparam[i].datrec_offset = n;
    if(edfhdr->bdf)  n += edfhdr->edfparam[i].smp_per_record * 3;
    else  n += edfhdr->edfparam[i].smp_per_record * 2;

    edfhdr->edfparam[i].bitvalue = (edfhdr->edfparam[i].phys_max - edfhdr->edfparam[i].phys_min) / (edfhdr->edfparam[i].dig_max - edfhdr->edfparam[i].dig_min);
    edfhdr->edfparam[i].offset = edfhdr->edfparam[i].phys_max / edfhdr->edfparam[i].bitvalue - edfhdr->edfparam[i].dig_max;
  }

  txt_string[0] = 0;

  free(edf_hdr);

  return edfhdr;
}


static long long get_long_duration(char *str)
{
  int i, len=8, hasdot=0, dotposition=0;

  long long value=0, radix;

  if((str[0] == '+') || (str[0] == '-'))
  {
    for(i=0; i<7; i++)
    {
      str[i] = str[i+1];
    }

    str[7] = ' ';
  }

  for(i=0; i<8; i++)
  {
    if(str[i]==' ')
    {
      len = i;
      break;
    }
  }

  for(i=0; i<len; i++)
  {
    if(str[i]=='.')
    {
      hasdot = 1;
      dotposition = i;
      break;
    }
  }

  if(hasdot)
  {
    radix = TIME_FIXP_SCALING;

    for(i=dotposition-1; i>=0; i--)
    {
        value += ((long long)(str[i] - 48)) * radix;
        radix *= 10;
    }

    radix = TIME_FIXP_SCALING / 10;

    for(i=dotposition+1; i<len; i++)
    {
        value += ((long long)(str[i] - 48)) * radix;
        radix /= 10;
    }
  }
  else
  {
    radix = TIME_FIXP_SCALING;

    for(i=len-1; i>=0; i--)
    {
        value += ((long long)(str[i] - 48)) * radix;
        radix *= 10;
    }
  }

  return value;
}



