#ifndef __COMMON_H
#define __COMMON_H

#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>

#include <cwchar>
#include <cwctype>

#include "../lib/const.h"

#ifndef _CMDLINE_FILE
#error _CMDLINE_FILE constant not defined!
#else
#include _CMDLINE_FILE
#endif


/**************************************************
 * Stale dotyczace wejscia/wyjscia
 */

#define EMPTYFORM '*'
#define INFIELD_SEP ':'
#define MAXAUX 16
#define FIELD_SEP " \t\n"
#define FIELD_PREFIX_MAXLEN 32


// katalogi z plikami konfiguracyjnymi
// nowe
// stare - do wyrzucenia
// #define CONFIG_DIR ".utt/conf"

// nazwa zmiennej okreslajaca sciezke do danych

// #define UTT_DIR_VAR "UTT_DIR"

// sciezka do plikow z danymi (np UTT_DIR/pliki) wzgledem $HOME!

// #define UTT_DIR_DEFAULT ".utt/pl/"

/**************************************************/


extern FILE* inputf;
extern FILE* outputf;
extern FILE* failedf;

extern char* input_filename;
extern char* output_filename;
extern char* failed_filename;
extern bool one_line;
extern bool one_field;

extern char input_field_prefix[];
extern char output_field_prefix[];

extern bool copy_processed;
extern bool append_output;
extern bool append_failed;

//sciezka do katalogu z danymi
extern char utt_dir[];

extern void process_common_options(gengetopt_args_info* args, char* argv0);
extern void process_config_files(gengetopt_args_info* args, char* argv0);

extern int expand_path(char* inpath, char* outpath);

/**************************************************
 * problems with casing                           */
// sprawdzenie wielkosci liter
// warto¶æ zwracana:
// 0 - wszystkie ma³e litery
// 1 - pierwsza wielka, reszta male
// 2 - wszystkie wielkie
// 3 - inne
inline int casing(char* s)
{
  int ret = isupper(*s) ? 1 : 0;
  while(*++s != '\0')
  {
    if(isupper(*s))
    {
      if(ret==1) ret=2;
      else if(ret==0) ret=3;
    }
    else
    {
      if(ret==2) ret=3;
    }
  }
  return ret;
}

// 
inline void tolowers(char* s, char* d)
{
  *d=tolower(*s);
  while(*s != '\0') * ++d = tolower(* ++s);
}


// przepisuje s do d
// nadajac wielko¶æ liter zgodnie z warto¶ci± casing
// casing - warto¶æ zwracana przez casing()
// je¶li casing==3 przepisuje bez zmian (za ma³o informacji)
inline void restorecasing(char *s, char *d, int casing)
{
  switch(casing)
  {
  case 0:
  case 3:
    *d=*s;
    while(*s != '\0') * ++d = * ++s;
    break;
  case 1:
    *d=toupper(*s);
    while(*s != '\0') * ++d = * ++s;
    break;
  case 2:
    *d=toupper(*s);
    while(*s != '\0') * ++d = toupper(* ++s);
    break;
  }
}

/**************************************************/

/*
parameters:
  -seg  - segment
  -pref - field name or "1", "2", "3", "4" for the first four fields
  +val  - field contents
return value:
  1 if specified field exists, 0 otherwise
*/

inline int getfield(char* seg, const char* pref, char* val)
{

  char* p=seg;
  char* p0;

  while(isspace(*p)) ++p;

  // field "1"
  p0=p; while(isdigit(*p)) ++p;
  if(*pref=='1') if(p!=p0) { strncpy(val,p0,p-p0); val[p-p0]='\0'; return 1; } else return 0;
  
  while(isspace(*p)) ++p;

  // field "2"
  p0=p; while(isdigit(*p)) ++p;
  if(*pref=='2') if(p!=p0) { strncpy(val,p0,p-p0); val[p-p0]='\0'; return 1; } else return 0;

  while(isspace(*p)) ++p;

  // field "3"
  p0=p; while(isgraph(*p)) ++p; 
  if(*pref=='3') if(p!=p0) { strncpy(val,p0,p-p0); val[p-p0]='\0'; return 1; } else return 0;

  while(isspace(*p)) ++p;

  // field "4"
  p0=p; while(isgraph(*p)) ++p;
  if(*pref=='4') if(p!=p0) { strncpy(val,p0,p-p0); val[p-p0]='\0'; return 1; } else return 0;

  while(isspace(*p)) ++p;

  // annotation fields
  do p=strstr(p,pref); while(p!=NULL && *(p-1)!=' ' && *(p-1)!='\t');
  
  if(p==NULL) return 0;
  else
  {
    p+=strlen(pref);
    int len=strcspn(p,FIELD_SEP "\n\r\f\0");
    strncpy(val,p,len);
    val[len]='\0';
    return 1;
  }
}

inline int getfield(wchar_t* seg, const wchar_t* pref, wchar_t* val)
{

  wchar_t* p=seg;
  wchar_t* p0;
  while(iswspace(*p)) ++p;

  // field "1"
  p0=p; while(iswdigit(*p)) ++p;
  if(*pref==L'1') 
    if(p!=p0) 
    { 
      wcsncpy(val,p0,p-p0); 
      val[p-p0]=L'\0'; 
      return 1; 
    } 
    else 
      return 0;
  while(iswspace(*p)) ++p;
  
// field "2"
  p0=p; while(iswdigit(*p)) ++p;
  if(*pref==L'2') if(p!=p0) { wcsncpy(val,p0,p-p0); val[p-p0]=L'\0'; return 1; } else return 0;
  while(iswspace(*p)) ++p;
  // field "3"
  p0=p; while(iswgraph(*p)) ++p; 
  if(*pref==L'3') if(p!=p0) { wcsncpy(val,p0,p-p0); val[p-p0]=L'\0'; return 1; } else return 0;

  while(iswspace(*p)) ++p;
  p0=p; while(iswgraph(*p)) ++p;

  if(*pref==L'4') if(p!=p0) { wcsncpy(val,p0,p-p0); val[p-p0]=L'\0'; return 1; } else return 0;
  while(iswspace(*p)) ++p;

  // annotation fields
  do p=wcsstr(p,pref); while(p!=NULL && *(p-1)!=L' ' && *(p-1)!=L'\t');
  if(p==NULL) return 0;
  else
  {
    p+=wcslen(pref);
    int len=wcscspn(p,FIELD_SEP L"\n\r\f\0");
    wcsncpy(val,p,len);
    val[len]=L'\0';
    return 1;
  }
}

/*
parameters:
        -name - field name, long or short
        +prefix - field name with ':' appended if long name
return value:
        1 if correct field name, 0 otherwise
examples:
name    prefix  r.v.
lem     lem:    1
@       @       1
::      'undef' 0
a,b     'undef' 0
*/
inline
int fieldprefix(char *name, char *prefix)
{
  if (ispunct(name[0]) && name[1]=='\0') // correct short name
  {
    strcpy(prefix, name); return 1;
  }

  int i=0;
  while(name[i]!='\0' && isalnum(name[i])) ++i;
  
  if(name[i]=='\0' && i>0) // correct long name
  {
    sprintf(prefix,"%s:",name); return 1;
  }

  // incorrect
  return 0;
}

inline
bool process_seg(char* seg, gengetopt_args_info& args)
{
  char buf[256];
  char pref[FIELD_PREFIX_MAXLEN];
  bool ret = !args.process_given;
  if(args.process_given)
    {
      getfield(seg,"3",buf);
      for(int i=0; i<args.process_given; ++i)
	if(strcmp(args.process_arg[i],buf)==0)
	  {
	    ret=true;
	    break;
	  }
    }

  if(ret==false) return false; 

  for(int i=0; i<args.select_given; ++i)
    {
      fieldprefix(args.select_arg[i],pref); // !!! ŁATKA - ZOPTYMALIZOWAĆ !!!
      if(! getfield(seg,pref,buf))
	return false;
    }
  for(int i=0; i<args.ignore_given; ++i)
    {
      fieldprefix(args.ignore_arg[i],pref);  // !!! ŁATKA - ZOPTYMALIZOWAĆ !!!
      if(getfield(seg,pref,buf))
	return false;
    }
  
  if(args.input_field_given & !getfield(seg,input_field_prefix,buf))
    return false;

  return true;
}


/*
parameters:
  -+seg - segment
  -pref - prefix of the new field
  -val  - contents of the new field
return value:
  1 - success, 0 - fail (limit on segment length exceeded)
 */
inline
int addfield(char *seg, const char *pref, const char *val)
     // zalozenie, ze seg konczy sie znakiem \n
{
  if(strlen(seg)+strlen(pref)+strlen(val) >= MAX_LINE) return 0; // bezpieczniej, ale wolniej

  int seglen=strlen(seg);
  sprintf(seg+(seglen-1)," %s%s\n",pref,val);
  return 1;
}

/**************************************************/

struct Seg
{
  int filepos, len;
  char* tag;
  char* form;
  char* aux[MAXAUX];
  int auxn;

  bool parse(char* line);
  char* getfield(char* fieldname);
  void print(char* line);
  bool addfield(char* s);
  bool clearfields();
};

/**************************************************/

/* definicja struktury wejscia/wyjscia
 */
struct Segment
{
  int filepos, len;
  char* tag;
  char* form;
  char* aux[MAXAUX];
  int auxn;

  bool parse(char* line);
  char* getfield(char* fieldname);
  void print(char* line);
  bool addfield(char* s);
  bool clearfields();
};

/*
 * Sprawdza czy nalezy przetwarzac dany segment.
 */

inline
bool process_seg(Segment& s, gengetopt_args_info& args)
{
  bool ret = !args.process_given;

  for(int i=0; i<args.process_given; ++i)
    if(strcmp(args.process_arg[i],s.tag)==0)
      {
        ret=true;
        break;
      }

  for(int i=0; i<args.select_given; ++i)
    if(! s.getfield(args.select_arg[i]))
      ret=false;

  for(int i=0; i<args.ignore_given; ++i)
    if(s.getfield(args.ignore_arg[i]))
      ret=false;

  return ret;
}


/*
 * FUNKCJE OBSLUGUJACE WEJSCIE/WYJSCIE
 */
// napisy zostaj na miejscu (w line), tylko wskazniki sa ustawian
// i zara dopisywane zera s dopisywane

inline
bool Segment::parse(char* line)
{
  auxn=0;
  char* field;
  if((field=strtok(line,FIELD_SEP))!=NULL)
    filepos=atoi(field); // nie sprawdzana poprawnosc
  else
    return false;
  if((field=strtok(NULL,FIELD_SEP))!=NULL)
    len=atoi(field); // nie sprawdzana poprawnosc
  else return false;
  if((tag=strtok(NULL,FIELD_SEP))==NULL) return false;
  if((form=strtok(NULL,FIELD_SEP))==NULL)
    return true;
  else
    if(form[0] == EMPTYFORM && form[1] =='\0')
      form=NULL;

  while((aux[auxn]=strtok(NULL,FIELD_SEP))!=NULL) ++auxn;

  return true;
}


inline char* Segment::getfield(char* f)
{
  int flen=strlen(f);
  if(isalnum(*f))
  {
    for(int i=0; i<auxn; ++i)
      if(strncmp(aux[i],f,flen)==0 && aux[i][flen]==INFIELD_SEP)
	return aux[i]+flen+1;
  } else
  {
    for(int i=0; i<auxn; ++i)
    {
      if(*f==*(aux[i]))
	return aux[i]+1;
    }
  }
  return NULL;
}

inline bool Segment::clearfields() {
  for (int i=0; i<auxn; ++i) {
    //    free(aux[i]);
    aux[i] = NULL;
  }
  auxn=0;
  return true;
}

inline // NIEEFEKTYWNE
void Segment::print(char* line)
{
  sprintf(line,"%04d %02d %s", filepos, len, tag);
  if(form)
    {
      strcat(line," ");
      strcat(line,form);
    }
  else
    if(auxn)
      strcat(line," *");

  for(int i=0; i<auxn; ++i)
    {
      strcat(line," ");
      strcat(line,aux[i]);
    }

  strcat(line,"\n");
}


inline
bool Segment::addfield(char* s)
{
  if(auxn<MAXAUX)
    {
      aux[auxn++]=s;
      return true;
    }
  else
    return false;
}

/**************************************************
 * funkcje pomocne w operacjach na plikach        *
 *  konfiguracyjnych                              *
 **************************************************/

// sprawdza istnienie pliku
int file_accessible(const char* path);

// sprawdza istnienie pliku konfiguracyjnego
int config_file(const char* dir, const char* filename);

/**************************************************/

/* Pobiera wejscie
 * parametry:
 * - args - tablica stringow okresnajacych pola wejsciowe
 * - args_len - rozmiar args
 * - seg - segment
 * wartosc - wskaznik do wejscia
 */
inline char*  getInput(char** args, int args_len, Segment seg) {
  char* formp = NULL;
  for (int i=0; i<args_len; ++i) {
    if ('4' == args[i][0])
      return seg.form;
    if ((formp = seg.getfield(args[i])) != NULL) {
      return formp;
    }
  }
  return formp;
}

#endif
