/************************************************************************
    MeOS - Orienteering Software
    Copyright (C) 2009-2017 Melin Software HB

    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 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.  If not, see <http://www.gnu.org/licenses/>.

    Melin Software HB - software@melin.nu - www.melin.nu
    Eksoppsvägen 16, SE-75646 UPPSALA, Sweden

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

#include "stdafx.h"
#include <vector>
#include <math.h>
#include "meos_util.h"
#include "localizer.h"
#include "oFreeImport.h"
#include "meosexception.h"
#include <WinInet.h>

StringCache globalStringCache;

namespace MeOSUtil {
  int useHourFormat = true;
}

string convertSystemTimeN(const SYSTEMTIME &st);
string convertSystemDateN(const SYSTEMTIME &st);
string convertSystemTimeOnlyN(const SYSTEMTIME &st);


DWORD mainThreadId = -1;
StringCache &StringCache::getInstance() {
  DWORD id = GetCurrentThreadId();
  if (mainThreadId == -1)
    mainThreadId = id;
  else if (mainThreadId != id)
    throw std::exception("Thread access error");
  return globalStringCache;
}

string getLocalTimeN() {
  SYSTEMTIME st;
  GetLocalTime(&st);
  return convertSystemTimeN(st);
}

string getLocalDateN()
{
  SYSTEMTIME st;
  GetLocalTime(&st);
  return convertSystemDateN(st);
}

wstring getLocalTime() {
  SYSTEMTIME st;
  GetLocalTime(&st);
  return convertSystemTime(st);
}

wstring getLocalDate() {
  SYSTEMTIME st;
  GetLocalTime(&st);
  return convertSystemDate(st);
}

int getLocalAbsTime() {
  return convertAbsoluteTimeHMS(getLocalTimeOnly(), -1);
}

int getThisYear() {
  static int thisYear = 0;
  if (thisYear == 0) {
    SYSTEMTIME st;
    GetLocalTime(&st);
    thisYear = st.wYear;
  }
  return thisYear;
}


/** Extend a year from 03 -> 2003, 97 -> 1997 etc */
int extendYear(int year) {
  if (year<0)
    return year;

  if (year>=100)
    return year;

  int thisYear = getThisYear();

  int cLast = thisYear%100;

  if (cLast == 0 && year == 0)
    return thisYear;

  if (year > thisYear%100)
    return (thisYear - cLast) - 100 + year;
  else
    return (thisYear - cLast) + year;
}

wstring getLocalTimeFileName()
{
  SYSTEMTIME st;
  GetLocalTime(&st);

  wchar_t bf[32];
  swprintf_s(bf, L"%d%02d%02d_%02d%02d%02d", st.wYear, st.wMonth, st.wDay,
    st.wHour, st.wMinute, st.wSecond);

  return bf;
}

string getLocalTimeOnlyN()
{
  SYSTEMTIME st;
  GetLocalTime(&st);
  return convertSystemTimeOnlyN(st);
}

wstring getLocalTimeOnly()
{
  SYSTEMTIME st;
  GetLocalTime(&st);
  return convertSystemTimeOnly(st);
}

int getRelativeDay() {
  SYSTEMTIME st;
  GetLocalTime(&st);
  FILETIME ft;
  SystemTimeToFileTime(&st, &ft);

  ULARGE_INTEGER u;
  u.HighPart = ft.dwHighDateTime;
  u.LowPart = ft.dwLowDateTime;
  __int64 qp = u.QuadPart;
  qp /= __int64(10) * 1000 * 1000 * 3600 * 24;
  qp -=  400*365;
  return int(qp);
}

__int64 SystemTimeToInt64Second(const SYSTEMTIME &st) {
  FILETIME ft;
  SystemTimeToFileTime(&st, &ft);

  ULARGE_INTEGER u;
  u.HighPart = ft.dwHighDateTime;
  u.LowPart = ft.dwLowDateTime;
  __int64 qp = u.QuadPart;
  qp /= __int64(10) * 1000 * 1000;
  return qp;
}

SYSTEMTIME Int64SecondToSystemTime(__int64 time) {
  SYSTEMTIME st;
  FILETIME ft;

  ULARGE_INTEGER u;
  u.QuadPart = time * __int64(10) * 1000 * 1000;
  ft.dwHighDateTime = u.HighPart;
  ft.dwLowDateTime = u.LowPart;

  FileTimeToSystemTime(&ft, &st);

  return st;
}
//2014-11-03 07:02:00
string convertSystemTimeN(const SYSTEMTIME &st)
{
  char bf[32];
  sprintf_s(bf, "%d-%02d-%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay,
    st.wHour, st.wMinute, st.wSecond);

  return bf;
}

//2014-11-03 07:02:00
wstring convertSystemTime(const SYSTEMTIME &st)
{
  wchar_t bf[32];
  swprintf_s(bf, L"%d-%02d-%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay,
    st.wHour, st.wMinute, st.wSecond);

  return bf;
}

string convertSystemTimeOnlyN(const SYSTEMTIME &st)
{
  char bf[32];
  sprintf_s(bf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);

  return bf;
}

wstring convertSystemTimeOnly(const SYSTEMTIME &st) {
  wchar_t bf[32];
  swprintf_s(bf, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);

  return bf;
}

string convertSystemDateN(const SYSTEMTIME &st)
{
  char bf[32];
  sprintf_s(bf, "%d-%02d-%02d", st.wYear, st.wMonth, st.wDay);

  return bf;
}

wstring convertSystemDate(const SYSTEMTIME &st)
{
  wchar_t bf[32];
  swprintf_s(bf, L"%d-%02d-%02d", st.wYear, st.wMonth, st.wDay);

  return bf;
}

string formatDateN(int m, bool useIsoFormat) {
  char bf[24];
  if (m > 0 && m < 30000101) {
    sprintf_s(bf, 24, "%d-%02d-%02d", m/(100*100), (m/100)%100, m%100);
  }
  else {
    bf[0] = '-';
    bf[1] = 0;
  }
  return bf;
}

wstring formatDate(int m, bool useIsoFormat) {
  wchar_t bf[24];
  if (m > 0 && m < 30000101) {
    swprintf_s(bf, 24, L"%d-%02d-%02d", m/(100*100), (m/100)%100, m%100);
  }
  else {
    bf[0] = '-';
    bf[1] = 0;
  }
  return bf;
}

int convertDateYMS(const wstring &m, SYSTEMTIME &st, bool checkValid) {
  string ms(m.begin(), m.end());
  return convertDateYMS(ms, st, checkValid);
}
//Absolute time string to SYSTEM TIME
int convertDateYMS(const string &m, SYSTEMTIME &st, bool checkValid) {
  memset(&st, 0, sizeof(st));

  if (m.length()==0)
    return -1;

  int len=m.length();
  for (int k=0;k<len;k++) {
    BYTE b=m[k];
    if (b == 'T')
      break;
    if ( !(b=='-' || b==' ' || (b>='0' && b<='9')) )
      return -1;
  }

  int year=atoi(m.c_str());
  if (year<1900 || year>3000)
    return -1;

  int month=0;
  int day=0;
  int kp=m.find_first_of('-');

  if (kp!=string::npos) {
    string mtext=m.substr(kp+1);
    month=atoi(mtext.c_str());

    if (month<1 || month>12) {
      if (checkValid)
        return -1;
      month = 1;
    }

    kp=mtext.find_last_of('-');

    if (kp!=string::npos) {
      day=atoi(mtext.substr(kp+1).c_str());
      if (day<1 || day>31) {
        if (checkValid)
          return -1;
        day = 1;
      }
    }
  }
  st.wYear = year;
  st.wMonth = month;
  st.wDay = day;


  int t = year*100*100+month*100+day;
  if (t<0) return -1;

  return t;
}

//Absolute time string to absolute time int
int convertDateYMS(const string &m, bool checkValid)
{
  SYSTEMTIME st;
  return convertDateYMS(m, st, checkValid);
}

int convertDateYMS(const wstring &m, bool checkValid)
{
  SYSTEMTIME st;
  return convertDateYMS(m, st, checkValid);
}

bool myIsSpace(wchar_t b) {
  return iswspace(b) != 0 || b == 0x00A0 || b == 0x2007 || b == 0x202F;
}


//Absolute time string to absolute time int
int convertAbsoluteTimeHMS(const string &m, int daysZeroTime) {
  int len = m.length();

  if (len==0 || m[0]=='-')
    return -1;

  // Support for notation 2D 14:30:00 or 2T14:30:00 for several days
  int tix = -1;
  for (size_t k = 0; k < m.length(); k++) {
    int c = m[k];
    if (c =='D' || c =='d' || c =='T' || c =='t') {
      tix = k;
      break;
    }
  }
  if (tix != -1) {
    if (daysZeroTime < 0)
      return -1; // Not supported
    int tpart = convertAbsoluteTimeHMS(m.substr(tix+1), -1);
    if (tpart != -1) {
      int days = atoi(m.c_str());
      if (days <= 0)
        return -1;
      if (tpart < daysZeroTime)
        days--;
      return days * 3600 * 24 + tpart;
    }
    return -1;
  }

  int plusIndex = -1;
  for (int k=0;k<len;k++) {
    BYTE b=m[k];
    if ( !(isspace(b) || b==':' || (b>='0' && b<='9')) ) {
      if (b=='+' && plusIndex ==-1 && k>0)
        plusIndex = k;
      else
        return -1;
    }
  }

  if (plusIndex>0) {
    int t = convertAbsoluteTimeHMS(m.substr(plusIndex+1), -1);
    int d = atoi(m.c_str());

    if (d>0 && t>=0)
      return d*24*3600 + t;
    else
      return -1;
  }

  int hour=atoi(m.c_str());

  if (hour<0 || hour>23)
    return -1;

  int minute=0;
  int second=0;
  int kp=m.find_first_of(':');

  if (kp!=string::npos) {
    string mtext=m.substr(kp+1);
    minute=atoi(mtext.c_str());

    if (minute<0 || minute>60)
      minute=0;

    kp=mtext.find_last_of(':');

    if (kp!=string::npos) {
      second=atoi(mtext.substr(kp+1).c_str());
      if (second<0 || second>60)
        second=0;
    }
  }
  int t=hour*3600+minute*60+second;
  if (t<0) return 0;

  return t;
}

//Absolute time string to absolute time int
int convertAbsoluteTimeHMS(const wstring &m, int daysZeroTime) {
  string sm(m.begin(), m.end());
  return convertAbsoluteTimeHMS(sm, daysZeroTime);
}

//Absolute time string to absolute time int
int convertAbsoluteTimeISO(const string &m)
{
  int len = m.length();

  if (len==0 || m[0]=='-')
    return -1;

  string hStr, mStr, sStr;

  string tmp =  trim(m);

  if (tmp.length() < 3)
    return -1;

  hStr = tmp.substr(0, 2);
  if (!(tmp[2] >= '0' && tmp[2]<='9'))
    tmp = tmp.substr(3);
  else
    tmp = tmp.substr(2);

  if (tmp.length() < 3)
    return -1;

  mStr = tmp.substr(0, 2);

  if (!(tmp[2] >= '0' && tmp[2]<='9'))
    tmp = tmp.substr(3);
  else
    tmp = tmp.substr(2);

  if (tmp.length() < 2)
    return -1;

  sStr = tmp.substr(0, 2);

  for (int i = 0; i < 2; i++) {
    if (hStr[i] < '0' || hStr[i] > '9')
      return -1;
    if (mStr[i] < '0' || mStr[i] > '9')
      return -1;
    if (sStr[i] < '0' || sStr[i] > '9')
      return -1;
  }

  int hour = atoi(hStr.c_str());
  if (hour<0 || hour>23)
    return -1;

  int minute = atoi(mStr.c_str());

  if (minute<0 || minute>60)
    return -1;

  int second = atoi(sStr.c_str());

  if (second<0 || second>60)
    return -1;

  int t = hour*3600 + minute*60 + second;

  return t;
}

int convertAbsoluteTimeISO(const wstring &m) {
  string mn(m.begin(), m.end());
  return convertAbsoluteTimeISO(mn);
}

// Parse +-MM:SS or +-HH:MM:SS
int convertAbsoluteTimeMS(const string &m)
{
  if (m.length()==0)
    return NOTIME;

  int minute=0;
  int second=0;

  int signpos=m.find_first_of('-');
  string mtext;

  int sign=1;

  if (signpos!=string::npos) {
    sign=-1;
    mtext=m.substr(signpos+1);
  }
  else
    mtext=m;

  minute=atoi(mtext.c_str());

  if (minute<0 || minute>60*24)
    minute=0;

  int kp=mtext.find_first_of(':');

  if (kp!=string::npos) {
    mtext = mtext.substr(kp+1);
    second = atoi(mtext.c_str());
    if (second<0 || second>60)
      second=0;
  }

  int t=minute*60+second;

  kp=mtext.find_first_of(':');
  if (kp!=string::npos) {
    //Allow also for format +-HH:MM:SS
    mtext = mtext.substr(kp+1);
    second=atoi(mtext.c_str());
    if (second<0 || second>60)
      second=0;
    else
      t = t*60 + second;
  }
  return sign*t;
}

int convertAbsoluteTimeMS(const wstring &m) {
  string mn(m.begin(), m.end());
  return convertAbsoluteTimeMS(mn);
}

//Generate +-MM:SS or +-HH:MM:SS
const wstring &getTimeMS(int m) {
  wchar_t bf[32];
  int am = abs(m);
  if (am < 3600 || !MeOSUtil::useHourFormat)
    swprintf_s(bf, L"-%02d:%02d", am/60, am%60);
  else if (am < 3600*48)
    swprintf_s(bf, L"-%02d:%02d:%02d", am/3600, (am/60)%60, am%60);
  else {
    m = 0;
    bf[0] = 0x2013;
    bf[1] = 0;
  }
  wstring &res = StringCache::getInstance().wget();
  if (m<0)
    res = bf; // with minus
  else
    res = bf + 1;

  return res;
}

const wstring &formatTime(int rt) {
  wstring &res = StringCache::getInstance().wget();
  if (rt>0 && rt<3600*999) {
    wchar_t bf[16];
    if (rt>=3600 && MeOSUtil::useHourFormat)
      swprintf_s(bf, 16, L"%d:%02d:%02d", rt/3600,(rt/60)%60, rt%60);
    else
      swprintf_s(bf, 16, L"%d:%02d", (rt/60), rt%60);

    res = bf;
    return res;
  }
  wchar_t ret[2] = {0x2013, 0};
  res = ret;
  return res;
}

const string &formatTimeN(int rt) {
  string &res = StringCache::getInstance().get();
  if (rt>0 && rt<3600*999) {
    char bf[16];
    if (rt>=3600 && MeOSUtil::useHourFormat)
      sprintf_s(bf, 16, "%d:%02d:%02d", rt/3600,(rt/60)%60, rt%60);
    else
      sprintf_s(bf, 16, "%d:%02d", (rt/60), rt%60);

    res = bf;
    return res;
  }
  res = "-";
  return res;
}

const wstring &formatTimeHMS(int rt) {
  wstring &res = StringCache::getInstance().wget();
  if (rt>=0) {
    wchar_t bf[32];
    swprintf_s(bf, 16, L"%02d:%02d:%02d", rt/3600,(rt/60)%60, rt%60);
    res = bf;
    return res;
  }
  wchar_t ret[2] = {0x2013, 0};
  res = ret;
  return res;
}

wstring formatTimeIOF(int rt, int zeroTime)
{
  if (rt>0 && rt<(3600*24*10)) {
    rt+=zeroTime;
    wchar_t bf[16];
    swprintf_s(bf, 16, L"%02d:%02d:%02d", (rt/3600)%24,(rt/60)%60, rt%60);

    return bf;
  }
  return L"--:--:--";
}

size_t find(const wstring &str, const wstring &separator, size_t startpos)
{
  size_t seplen = separator.length();

  for (size_t m = startpos; m<str.length(); m++) {
    for (size_t n = 0; n<seplen; n++)
      if (str[m] == separator[n])
        return m;
  }
  return str.npos;
}

size_t find(const string &str, const string &separator, size_t startpos)
{
  size_t seplen = separator.length();

  for (size_t m = startpos; m<str.length(); m++) {
    for (size_t n = 0; n<seplen; n++)
      if (str[m] == separator[n])
        return m;
  }
  return str.npos;
}

const vector<string> & split(const string &line, const string &separators, vector<string> &split_vector) {
  split_vector.clear();

  if (line.empty())
    return split_vector;

  size_t startpos=0;
  size_t nextp=find(line, separators, startpos);
  split_vector.push_back(line.substr(startpos, nextp-startpos));

  while(nextp!=line.npos) {
    startpos=nextp+1;
    nextp=find(line, separators, startpos);
    split_vector.push_back(line.substr(startpos, nextp-startpos));
  }

  return split_vector;
}

const vector<wstring> & split(const wstring &line, const wstring &separators, vector<wstring> &split_vector) {
  split_vector.clear();

  if (line.empty())
    return split_vector;

  size_t startpos=0;
  size_t nextp=find(line, separators, startpos);
  split_vector.push_back(line.substr(startpos, nextp-startpos));

  while(nextp!=line.npos) {
    startpos=nextp+1;
    nextp=find(line, separators, startpos);
    split_vector.push_back(line.substr(startpos, nextp-startpos));
  }

  return split_vector;
}


const string &unsplit(const vector<string> &split_vector, const string &separators, string &line) {
  size_t s = split_vector.size() * separators.size();
  for (size_t k = 0; k < split_vector.size(); k++) {
    s += split_vector[k].size();
  }
  
  line.clear();
  line.reserve(s);
  for (size_t k = 0; k < split_vector.size(); k++) {
    if (k != 0)
      line += separators;
    line += split_vector[k];
  }
  return line;
}

const wstring &unsplit(const vector<wstring> &split_vector, const wstring &separators, wstring &line) {
  size_t s = split_vector.size() * separators.size();
  for (size_t k = 0; k < split_vector.size(); k++) {
    s += split_vector[k].size();
  }
  
  line.clear();
  line.reserve(s);
  for (size_t k = 0; k < split_vector.size(); k++) {
    if (k != 0)
      line += separators;
    line += split_vector[k];
  }
  return line;
}

const wstring &makeDash(const wstring &t) {
  return makeDash(t.c_str());
}

const wstring &makeDash(const wchar_t *t) {
  wstring &out = StringCache::getInstance().wget();
  out = t;
  for (size_t i=0;i<out.length(); i++) {
    if (t[i]=='-')
      out[i]= 0x2013;
  }
  return out;
}

wstring formatRank(int rank) {
  wchar_t r[16];
  swprintf_s(r, L"(%04d)", rank);
  return r;
}

const wstring &itow(int i) {
  wchar_t bf[32];
  _itow_s(i, bf, 10);
  wstring &res = StringCache::getInstance().wget();
  res = bf;
  return res;
}

wstring itow(unsigned int i) {
  wchar_t bf[32];
  _ultow_s(i, bf, 10);
  return bf;
}

wstring itow(unsigned long i) {
  wchar_t bf[32];
  _ultow_s(i, bf, 10);
  return bf;
}

wstring itow(__int64 i) {
  wchar_t bf[32];
  _i64tow_s(i, bf, 32, 10);
  return bf;
}

const string &itos(int i)
{
  char bf[32];
  _itoa_s(i, bf, 10);
  string &res = StringCache::getInstance().get();
  res = bf;
  return res;
}

string itos(unsigned int i)
{
  char bf[32];
  _ultoa_s(i, bf, 10);
  return bf;
}

string itos(unsigned long i)
{
  char bf[32];
  _ultoa_s(i, bf, 10);
  return bf;
}

string itos(__int64 i)
{
  char bf[32];
  _i64toa_s(i, bf, 32, 10);
  return bf;
}

bool filterMatchString(const string &c, const char *filt_lc)
{
  if (filt_lc[0] == 0)
    return true;
  char key[2048];
  strcpy_s(key, c.c_str());
  CharLowerBuffA(key, c.length());

  return strstr(key, filt_lc)!=0;
}

bool filterMatchString(const wstring &c, const wchar_t *filt_lc) {
  if (filt_lc[0] == 0)
    return true;
  wchar_t key[2048];
  wcscpy_s(key, c.c_str());
  CharLowerBuff(key, c.length());

  return wcsstr(key, filt_lc)!=0;
}


int countWords(const wchar_t *p) {
  int nwords=0;
  const wchar_t *ep=p;
  while (*ep) {
    if (!myIsSpace(*ep)) {
      nwords++;
      while ( *ep && !myIsSpace(*ep) )
        ep++;
    }
    while (*ep && myIsSpace(*ep))
        ep++;
  }
  return nwords;
}

string trim(const string &s)
{
  const char *ptr=s.c_str();
  int len=s.length();

  int i=0;
  while(i<len && (isspace(BYTE(ptr[i])) || BYTE(ptr[i])==BYTE(160))) i++;

  int k=len-1;

  while(k>=0 && (isspace(BYTE(ptr[k])) || BYTE(ptr[i])==BYTE(160))) k--;

  if (i == 0 && k == len-1)
    return s;
  else if (k>=i && i<len)
    return s.substr(i, k-i+1);
  else return "";
}

wstring trim(const wstring &s) {
  const wchar_t *ptr = s.c_str();
  int len = s.length();

  int i=0;
  while(i<len && myIsSpace(ptr[i])) i++;

  int k=len-1;

  while(k>=0 && myIsSpace(ptr[k])) k--;

  if (i == 0 && k == len-1)
    return s;
  else if (k>=i && i<len)
    return s.substr(i, k-i+1);
  else return L"";
}

bool fileExist(const wchar_t *file)
{
  return GetFileAttributes(file) != INVALID_FILE_ATTRIBUTES;
}

bool stringMatch(const wstring &a, const wstring &b) {
  wstring aa = trim(a);
  wstring bb = trim(b);

  return CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, aa.c_str(), aa.length(), bb.c_str(), bb.length())==2;
}

const string &encodeXML(const string &in)
{
  static string out;
  const char *bf = in.c_str();
  int len = in.length();
  bool needEncode = false;
  for (int k=0;k<len ;k++)
    needEncode |=  (bf[k]=='&') | (bf[k]=='>') | (bf[k]=='<') | (bf[k]=='"') | (bf[k]==0) | (bf[k]=='\n') | (bf[k]=='\r');

  if (!needEncode)
    return in;
  out.clear();
  for (int k=0;k<len ;k++) {
    if (bf[k]=='&')
      out+="&amp;";
    else if (bf[k]=='<')
      out+="&lt;";
    else if (bf[k]=='>')
      out+="&gt;";
    else if (bf[k]=='"')
      out+="&quot;";
    else if (bf[k]=='\n')
      out+="&#10;";
    else if (bf[k]=='\r')
      out+="&#13;";
    else if (bf[k] == 0)
      out+=' ';
    else
      out+=bf[k];
  }
  return out;
}


const wstring &encodeXML(const wstring &in)
{
  static wstring out;//WCS
  const wchar_t *bf = in.c_str();
  int len = in.length();
  bool needEncode = false;
  for (int k=0;k<len ;k++)
    needEncode |=  (bf[k]=='&') | (bf[k]=='>') | (bf[k]=='<') | (bf[k]=='"') | (bf[k]==0) | (bf[k]=='\n') | (bf[k]=='\r');

  if (!needEncode)
    return in;
  out.clear();
  for (int k=0;k<len ;k++) {
    if (bf[k]=='&')
      out+=L"&amp;";
    else if (bf[k]=='<')
      out+=L"&lt;";
    else if (bf[k]=='>')
      out+=L"&gt;";
    else if (bf[k]=='"')
      out+=L"&quot;";
    else if (bf[k]=='\n')
      out+=L"&#10;";
    else if (bf[k]=='\r')
      out+=L"&#13;";
    else if (bf[k] == 0)
      out+=' ';
    else
      out+=bf[k];
  }
  return out;
}

const wstring &encodeHTML(const wstring &in)
{
  static wstring out;//WCS
  const wchar_t *bf = in.c_str();
  int len = in.length();
  bool needEncode = false;
  for (int k=0;k<len ;k++)
    needEncode |=  (bf[k]==' ') | (bf[k]=='&') | (bf[k]=='>') | (bf[k]=='<') | (bf[k]=='"') | (bf[k]==0) | (bf[k]=='\n') | (bf[k]=='\r') | (bf[k]==0x2013);

  if (!needEncode)
    return in;
  out.clear();
  for (int k=0;k<len ;k++) {
    if (bf[k]=='&')
      out+=L"&amp;";
    else if (bf[k]=='<')
      out+=L"&lt;";
    else if (bf[k]=='>')
      out+=L"&gt;";
    else if (bf[k]=='"')
      out+=L"&quot;";
    else if (bf[k]=='\n')
      out+=L"&#10;";
    else if (bf[k]=='\r')
      out+=L"&#13;";
    else if (bf[k] == 0)
      out+=' ';
    else if (bf[k] == 0x2013) {
      out+=L"&ndash;";
    }
    else if (bf[k] == ' ') {
      out+=L"&nbsp;";
    }
    else
      out+=bf[k];
  }
  return out;
}

const string &decodeXML(const string &in)
{
  const char *bf = in.c_str();
  int len = in.length();
  bool needDecode = false;
  for (int k=0;k<len ;k++)
    needDecode |=  (bf[k]=='&');

  if (!needDecode)
    return in;

  static string out;
  out.clear();

  if (len < 50) {
    for (int k=0;k<len ;k++) {
      if (bf[k]=='&') {
        if ( memcmp(&bf[k], "&amp;", 5)==0 )
          out+="&", k+=4;
        else if  ( memcmp(&bf[k], "&lt;", 4)==0 )
          out+="<", k+=3;
        else if  ( memcmp(&bf[k], "&gt;", 4)==0 )
          out+=">", k+=3;
        else if  ( memcmp(&bf[k], "&quot;", 6)==0 )
          out+="\"", k+=5;
        else if  ( memcmp(&bf[k], "&#10;", 5)==0 )
          out+="\n", k+=4;
        else if  ( memcmp(&bf[k], "&#13;", 5)==0 )
          out+="\r", k+=4;
        else
          out+=bf[k];
      }
      else
        out+=bf[k];
    }
  }
  else {
    ostringstream str;
    for (int k=0;k<len ;k++) {
      if (bf[k]=='&') {
        if ( memcmp(&bf[k], "&amp;", 5)==0 )
          str << '&', k+=4;
        else if  ( memcmp(&bf[k], "&lt;", 4)==0 )
          str << '<', k+=3;
        else if  ( memcmp(&bf[k], "&gt;", 4)==0 )
          str << '>', k+=3;
        else if  ( memcmp(&bf[k], "&quot;", 6)==0 )
          str << '\"', k+=5;
        else if  ( memcmp(&bf[k], "&nbsp;", 6)==0 )
          str << ' ', k+=5;
        else if  ( memcmp(&bf[k], "&#10;", 5)==0 )
          str << '\n', k+=4;
        else if  ( memcmp(&bf[k], "&#13;", 5)==0 )
          str << '\r', k+=4;
        else
          str << bf[k];
      }
      else
        str << bf[k];
    }
    out = str.str();
  }
  return out;
}


void inplaceDecodeXML(char *in)
{
  char *bf = in;
  int outp = 0;

  for (int k=0;bf[k] ;k++) {
    if (bf[k] != '&')
      bf[outp++] = bf[k];
    else {
      if ( memcmp(&bf[k], "&amp;", 5)==0 )
        bf[outp++] = '&', k+=4;
      else if  ( memcmp(&bf[k], "&lt;", 4)==0 )
        bf[outp++] = '<', k+=3;
      else if  ( memcmp(&bf[k], "&gt;", 4)==0 )
        bf[outp++] = '>', k+=3;
      else if  ( memcmp(&bf[k], "&quot;", 6)==0 )
        bf[outp++] = '"', k+=5;
      else if  ( memcmp(&bf[k], "&#10;", 5)==0 )
        bf[outp++] = '\n', k+=4;
      else if  ( memcmp(&bf[k], "&#13;", 5)==0 )
        bf[outp++] = '\r', k+=4;
      else
        bf[outp++] = bf[k];
    }
  }
  bf[outp] = 0;
}

const char *decodeXML(const char *in)
{
  const char *bf = in;
  bool needDecode = false;
  for (int k=0; bf[k] ;k++)
    needDecode |=  (bf[k]=='&');

  if (!needDecode)
    return in;

  static string out;
  out.clear();
  for (int k=0;bf[k] ;k++) {
    if (bf[k]=='&') {
      if ( memcmp(&bf[k], "&amp;", 5)==0 )
        out+="&", k+=4;
      else if  ( memcmp(&bf[k], "&lt;", 4)==0 )
        out+="<", k+=3;
      else if  ( memcmp(&bf[k], "&gt;", 4)==0 )
        out+=">", k+=3;
      else if  ( memcmp(&bf[k], "&quot;", 6)==0 )
        out+="\"", k+=5;
      else if  ( memcmp(&bf[k], "&#10;", 5)==0 )
        out+="\n", k+=4;
      else if  ( memcmp(&bf[k], "&#13;", 5)==0 )
        out+="\r", k+=4;
      else
        out+=bf[k];
    }
    else
      out+=bf[k];
  }

  return out.c_str();
}

void static setChar(wchar_t *map, wchar_t pos, wchar_t value)
{
  map[pos] = value;
}

int toLowerStripped(wchar_t c) {
  if (c>='A' && c<='Z')
    return c + ('a' - 'A');
  else if (c<128)
    return c;

  static wchar_t *map = 0;
  if (map == 0) {
    map = new wchar_t[65536];
    for (int i = 0; i < 65536; i++)
      map[i] = i;

    setChar(map, L'Å', L'å');
    setChar(map, L'Ä', L'ä');
    setChar(map, L'Ö', L'ö');

    setChar(map, L'É', L'e');
    setChar(map, L'é', L'e');
    setChar(map, L'è', L'e');
    setChar(map, L'È', L'e');
    setChar(map, L'ë', L'e');
    setChar(map, L'Ë', L'e');
    setChar(map, L'ê', L'e');
    setChar(map, L'Ê', L'e');

    setChar(map, L'û', L'u');
    setChar(map, L'Û', L'u');
    setChar(map, L'ü', L'u');
    setChar(map, L'Ü', L'u');
    setChar(map, L'ú', L'u');
    setChar(map, L'Ú', L'u');
    setChar(map, L'ù', L'u');
    setChar(map, L'Ù', L'u');

    setChar(map, L'ñ', L'n');
    setChar(map, L'Ñ', L'n');

    setChar(map, L'á', L'a');
    setChar(map, L'Á', L'a');
    setChar(map, L'à', L'a');
    setChar(map, L'À', L'a');
    setChar(map, L'â', L'a');
    setChar(map, L'Â', L'a');
    setChar(map, L'ã', L'a');
    setChar(map, L'Ã', L'a');

    setChar(map, L'ï', L'i');
    setChar(map, L'Ï', L'i');
    setChar(map, L'î', L'i');
    setChar(map, L'Î', L'i');
    setChar(map, L'í', L'i');
    setChar(map, L'Í', L'i');
    setChar(map, L'ì', L'i');
    setChar(map, L'Ì', L'i');

    setChar(map, L'ó', L'o');
    setChar(map, L'Ó', L'o');
    setChar(map, L'ò', L'o');
    setChar(map, L'Ò', L'o');
    setChar(map, L'õ', L'o');
    setChar(map, L'Õ', L'o');
    setChar(map, L'ô', L'o');
    setChar(map, L'Ô', L'o');

    setChar(map, L'ý', L'y');
    setChar(map, L'Ý', L'Y');
    setChar(map, L'ÿ', L'y');

    setChar(map, L'Æ', L'ä');
    setChar(map, L'æ', L'ä');

    setChar(map, L'Ø', L'ö');
    setChar(map, L'ø', L'ö');

    setChar(map, L'Ç', L'c');
    setChar(map, L'ç', L'c');
  }
  int a = map[c];
  return a;
}

const wchar_t *canonizeName(const wchar_t *name)
{
  static wchar_t out[70];
  static wchar_t tbf[70];
  
  for (int i = 0; i<63 && name[i]; i++) {
    if (name[i] == ',') {
      int tout = 0;
      for (int j = i+1; j < 63 && name[j]; j++) {
        tbf[tout++] = name[j];
      }      
      tbf[tout++] = ' ';
      for (int j = 0; j < i; j++) {
        tbf[tout++] = name[j];
      }
      tbf[tout] = 0;
      name = tbf;
      break;
    }
  }

  int outp = 0;
  int k = 0;
  for (k=0; k<63 && name[k]; k++) {
    if (name[k] != ' ')
      break;
  }

  bool first = true;
  while (k<63 && name[k]) {
    if (!first)
      out[outp++] = ' ';

    while(name[k]!= ' ' && k<63 && name[k]) {
      if (name[k] == '-')
        out[outp++] = ' ';
      else
        out[outp++] = toLowerStripped(name[k]);
      k++;
    }

    first = false;
    while(name[k] == ' ' && k<64)
      k++;
  }

  out[outp] = 0;
  return out;
}

const int notFound = 1000000;

int charDist(const wchar_t *b, int len, int origin, wchar_t c)
{
  int i;
  int bound = max(1, min(len/2, 4));
  for (int k = 0;k<bound; k++) {
    i = origin - k;
    if (i>0 && b[i] == c)
      return -k;
    i = origin + k;
    if (i<len && b[i] == c)
      return k;
  }
  return notFound;
}

static double stringDistance(const wchar_t *a, int al, const wchar_t *b, int bl)
{
  al = min (al, 256);
  int d1[256];
  int avg = 0;
  int missing = 0;
  int ndiff = 1; // Do not allow zero
  for (int k=0; k<al; k++) {
    int d = charDist(b, bl, k, a[k]);
    if (d == notFound) {
      missing++;
      d1[k] = 0;
    }
    else {
      d1[k] = d;
      avg += d;
      ndiff ++;
    }
  }
  if (missing>min(3, max(1, al/3)))
    return 1;

  double mfactor = double(missing*0.8);
  double center = double(avg)/double(ndiff);

  double dist = 0;
  for (int k=0; k<al; k++) {
    double ld = min<double>(fabs(d1[k] - center), abs(d1[k]));
    dist += ld*ld;
  }

  return (sqrt(dist)+mfactor*mfactor)/double(al);
}

double stringDistance(const wchar_t *a, const wchar_t *b)
{
  int al = wcslen(a);
  int bl = wcslen(b);

  double d1 = stringDistance(a, al, b, bl);
  if (d1 >= 1)
    return 1.0;

  double d2 = stringDistance(b, bl, a, al);
  if (d2 >= 1)
    return 1.0;

  return (max(d1, d2) + d1 + d2)/3.0;
}

int getNumberSuffix(const string &str)
{
  int pos = str.length();

  while (pos>1 && (str[pos-1] & (~127)) == 0  && (isspace(str[pos-1]) || isdigit(str[pos-1]))) {
    pos--;
  }

  if (pos == str.length())
    return 0;
  return atoi(str.c_str() + pos);
}

int getNumberSuffix(const wstring &str)
{
  int pos = str.length();

  while (pos>1 && (str[pos-1] & (~127)) == 0  && (isspace(str[pos-1]) || isdigit(str[pos-1]))) {
    pos--;
  }

  if (pos == str.length())
    return 0;
  return _wtoi(str.c_str() + pos);
}

int extractAnyNumber(const wstring &str, wstring &prefix, wstring &suffix)
{
  const wchar_t *ptr = (const wchar_t*)str.c_str();
  for (size_t k = 0; k<str.length(); k++) {
    if (isdigit(ptr[k])) {
      prefix = str.substr(0, k);
      int num = _wtoi(str.c_str() + k);
      while(k<str.length() && (str[++k] & ~0x7F) == 0 && isdigit(str[k]));
        suffix = str.substr(k);

      return num;
    }
  }
  return -1;
}

static void decomposeClassName(const wstring &name, vector<wstring> &dec) {
  if (name.empty())
    return;

  dec.push_back(wstring());
  
  for (size_t i = 0; i < name.size(); i++) {
    int bchar = toLowerStripped(name[i]);
    if (myIsSpace(bchar) || bchar == '-' || bchar == 160) {
      if (!dec.back().empty())
        dec.push_back(wstring());
      continue;
    }
    if (!dec.back().empty()) {
      int last = *dec.back().rbegin();
      bool lastNum = last >= '0' && last <= '9';
      bool isNum = bchar >= '0' && bchar <= '9';

      if (lastNum^isNum)
        dec.push_back(wstring()); // Change num/ non-num
    }
    dec.back().push_back(bchar);
  }
  if (dec.back().empty())
    dec.pop_back();

  sort(dec.begin(), dec.end());
}

/** Matches H21 L with H21 Lång and H21L 
 but not Violet with Violet Court, which is obviously wrong.
 */
bool compareClassName(const wstring &a, const wstring &b)
{
  if (a == b)
    return true;

	vector<wstring> acanon;
	vector<wstring> bcanon;

  decomposeClassName(a, acanon);
  decomposeClassName(b, bcanon);

  if (acanon.size() != bcanon.size())
    return false;

  for (size_t k = 0; k < acanon.size(); k++) {
    if (acanon[k] == bcanon[k])
      continue;

    int sample = acanon[k][0];
    if (sample >= '0' && sample <= '9')
      return false; // Numbers must match

    if (acanon[k][0] == bcanon[k][0] && (acanon[k].length() == 1 || bcanon[k].length() == 1))
      continue; // Abbrevation W <-> Women etc

    return false; // No match
  }

  return true; // All parts matched
}

wstring getErrorMessage(int code) {
  LPVOID msg;

  if (code == ERROR_INTERNET_TIMEOUT) {
    return L"Timeout";
  }

  int s = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                code,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR) &msg, 0, NULL);
  if (s==0 || !msg) {
    if (code != 0) {
      wchar_t ch[128];
      swprintf_s(ch, L"Error code: %d", code);
      return ch;
    }
    return L"";
  }

  wstring str = LPCTSTR(msg);
  if (str.empty() && code>0) {
    wchar_t ch[128];
    swprintf_s(ch, L"Error code: %d", code);
    str = ch;
  }

  LocalFree(msg);
  return str;
}

#define  HLSMAX   252 /* H,L, and S vary over 0-HLSMAX */
#define  RGBMAX   255   /* R,G, and B vary over 0-RGBMAX */
                        /* HLSMAX BEST IF DIVISIBLE BY 6 */
                        /* RGBMAX, HLSMAX must each fit in a byte. */

/* Hue is undefined if Saturation is 0 (grey-scale) */
#define UNDEFINED (HLSMAX*2/3)

HLS &HLS::RGBtoHLS(DWORD lRGBColor)
{
  WORD R,G,B;          /* input RGB values */
  BYTE cMax,cMin;      /* max and min RGB values */
  WORD  Rdelta,Gdelta,Bdelta; /* intermediate value: % of spread from max
                              */
  /* get R, G, and B out of DWORD */
  R = GetRValue(lRGBColor);
  G = GetGValue(lRGBColor);
  B = GetBValue(lRGBColor);
  WORD &L = lightness;
  WORD &H = hue;
  WORD &S = saturation;

  /* calculate lightness */
  cMax = (BYTE)max( max(R,G), B);
  cMin = (BYTE)min( min(R,G), B);
  L = ( ((cMax+cMin)*HLSMAX) + RGBMAX )/(2*RGBMAX);

  if (cMax == cMin) {           /* r=g=b --> achromatic case */
    S = 0;                     /* saturation */
    H = UNDEFINED;             /* hue */
  }
  else {                        /* chromatic case */
    /* saturation */
    if (L <= (HLSMAX/2))
      S = ( ((cMax-cMin)*HLSMAX) + ((cMax+cMin)/2) ) / (cMax+cMin);
    else
      S = ( ((cMax-cMin)*HLSMAX) + ((2*RGBMAX-cMax-cMin)/2) )
      / (2*RGBMAX-cMax-cMin);

    /* hue */
    Rdelta = ( ((cMax-R)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
    Gdelta = ( ((cMax-G)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
    Bdelta = ( ((cMax-B)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);

    if (R == cMax)
      H = Bdelta - Gdelta;
    else if (G == cMax)
      H = (HLSMAX/3) + Rdelta - Bdelta;
    else /* B == cMax */
      H = ((2*HLSMAX)/3) + Gdelta - Rdelta;

    if (H < 0)
      H += HLSMAX;
    if (H > HLSMAX)
      H -= HLSMAX;
  }
  return *this;
}


/* utility routine for HLStoRGB */
WORD HLS::HueToRGB(WORD n1, WORD n2, WORD hue) const
{
  /* range check: note values passed add/subtract thirds of range */
  if (hue < 0)
    hue += HLSMAX;

  if (hue > HLSMAX)
    hue -= HLSMAX;

  /* return r,g, or b value from this tridrant */
  if (hue < (HLSMAX/6))
    return ( n1 + (((n2-n1)*hue+(HLSMAX/12))/(HLSMAX/6)) );
  if (hue < (HLSMAX/2))
    return ( n2 );
  if (hue < ((HLSMAX*2)/3))
    return ( n1 +    (((n2-n1)*(((HLSMAX*2)/3)-hue)+(HLSMAX/12))/(HLSMAX/6))
    );
  else
    return ( n1 );
}

DWORD HLS::HLStoRGB() const
{
  const WORD &lum = lightness;
  const WORD &sat = saturation;

  WORD R,G,B;                /* RGB component values */
  WORD  Magic1,Magic2;       /* calculated magic numbers (really!) */

  if (sat == 0) {            /* achromatic case */
    R=G=B=(lum*RGBMAX)/HLSMAX;
    if (hue != UNDEFINED) {
      /* ERROR */
    }
  }
  else  {                    /* chromatic case */
    /* set up magic numbers */
    if (lum <= (HLSMAX/2))
      Magic2 = (lum*(HLSMAX + sat) + (HLSMAX/2))/HLSMAX;
    else
      Magic2 = lum + sat - ((lum*sat) + (HLSMAX/2))/HLSMAX;
    Magic1 = 2*lum-Magic2;

    /* get RGB, change units from HLSMAX to RGBMAX */
    R = (HueToRGB(Magic1,Magic2,hue+(HLSMAX/3))*RGBMAX +
      (HLSMAX/2))/HLSMAX;
    G = (HueToRGB(Magic1,Magic2,hue)*RGBMAX + (HLSMAX/2)) / HLSMAX;
    B = (HueToRGB(Magic1,Magic2,hue-(HLSMAX/3))*RGBMAX +
      (HLSMAX/2))/HLSMAX;
  }
  return(RGB(R,G,B));
}

void HLS::lighten(double f) {
  lightness = min<WORD>(HLSMAX, WORD(f*lightness));
}

void HLS::saturate(double s) {
  saturation = min<WORD>(HLSMAX, WORD(s*saturation));
}

void HLS::colorDegree(double d) {

}

bool isAscii(const string &s) {
  for (size_t k = 0; k<s.length(); k++)
    if (!isascii(s[k]))
      return false;
  return true;
}

bool isNumber(const string &s) {
  int len = s.length();
  for (int k = 0; k < len; k++) {
    if (!isdigit(s[k]))
      return false;
  }
  return len > 0;
}

bool isAscii(const wstring &s) {
  for (size_t k = 0; k<s.length(); k++)
    if (!iswascii(s[k]))
      return false;
  return true;
}

bool isNumber(const wstring &s) {
  int len = s.length();
  for (int k = 0; k < len; k++) {
    if ( (s[k]&127)!=s[k] || !isdigit(s[k]))
      return false;
  }
  return len > 0;
}

int convertDynamicBase(const wstring &s, long long &out) {
  out = 0;
  if (s.empty())
    return 0;

  bool alpha = false;
  bool general = false;
  int len = s.length();
  for (int k = 0; k < len; k++) {
    unsigned c = s[k];
    if (c >= '0' && c <= '9')
      continue;
    if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
      alpha = true;
      continue;
    }
    general = true;
    if (c<32)
      return 0; // Not a supported character
  }

  int base = general ? 256-32 : (alpha ? 36 : 10);
  long long factor = 1;
  for (int k = len-1; k >= 0; k--) {
    unsigned c = s[k]&0xFF;
    if (general)
      c -= 32;
    else {
      if (c >= '0' && c <= '9')
        c -= '0';
      else if (c >= 'A' && c <= 'Z')
        c -= 'A'-10;
      else if (c >= 'a' && c <= 'z')
        c -= 'a'-10;
    }
    out += factor * c;
    factor *= base;
  }

  return base;
}

void convertDynamicBase(long long val, int base, wchar_t out[16]) {
  int len = 0;
  while (val != 0) {
    unsigned int c = val % base;
    val = val / base;
    char cc;
    if (base == 10)
      cc = '0' + c;
    else if (base == 36) {
      if (c < 10)
        cc = '0' + c;
      else
        cc = 'A' + c - 10;
    }
    else {
      cc = c+32;
    }
    out[len++] = cc;
  }
  out[len] = 0;
  reverse(out, out+len);
}

bool expandDirectory(const wchar_t *file, const wchar_t *filetype, vector<wstring> &res)
{
  WIN32_FIND_DATA fd;

  wchar_t dir[MAX_PATH];
  wchar_t fullPath[MAX_PATH];

  if (file[0] == '.') {
    GetCurrentDirectory(MAX_PATH, dir);
    wcscat_s(dir, file+1);
  }
  else
    wcscpy_s(dir, MAX_PATH, file);

  if (dir[wcslen(dir)-1]!='\\')
    wcscat_s(dir, MAX_PATH, L"\\");

  wcscpy_s(fullPath, MAX_PATH, dir);
  wcscat_s(dir, MAX_PATH, filetype);

  HANDLE h=FindFirstFile(dir, &fd);

  if (h == INVALID_HANDLE_VALUE)
    return false;

  bool more = true;

  while (more) {
    if (fd.cFileName[0] != '.') {
      //Avoid .. and .
      wchar_t fullPathFile[MAX_PATH];
      wcscpy_s(fullPathFile, MAX_PATH, fullPath);
      wcscat_s(fullPathFile, MAX_PATH, fd.cFileName);
      res.push_back(fullPathFile);
    }
    more=FindNextFile(h, &fd)!=0;
  }

  FindClose(h);
  return true;
}

wstring encodeSex(PersonSex sex) {
  if (sex == sFemale)
    return L"F";
  else if (sex == sMale)
    return L"M";
  else if (sex == sBoth)
    return L"B";
  else
    return L"";
}

PersonSex interpretSex(const wstring &sex) {
  if (sex == L"F" || sex == L"K" || sex == L"W")
    return sFemale;
  else if (sex == L"M" || sex == L"H")
    return sMale;
  else if (sex == L"B")
    return sBoth;
  else
    return sUnknown;
}

bool matchNumber(int a, const wchar_t *b) {
  if (a == 0 && b[0])
    return false;

  wchar_t bf[32];
  _itow_s(a, bf, 10);

  // Check matching substring
  for (int k = 0; k < 12; k++) {
    if (b[k] == 0)
      return true;
    if (bf[k] != b[k])
      return false;
  }

  return false;
}

wstring makeValidFileName(const wstring &input, bool strict) {
  wstring out;
  out.reserve(input.size());

  if (strict) {
    for (size_t k = 0; k < input.length(); k++) {
      wchar_t b = input[k];
      if ( (b>='0' && b<='9') || (b>='a' && b<='z') || (b>='A' && b<='Z') || b == '_' || b=='.' )
        out.push_back(b);
      else if (b == ' ' ||  b == ',')
        out.push_back('_');
      else {
        b = toLowerStripped(b);
        
        if (b >= 'a' && b <= 'z')
          b = b;
        else if ( b == L'ö')
          b = 'o';
        else if (b == L'ä' || b == L'å' || b== L'à' || b == L'á' || b == L'â' || b == L'ã' || b == L'æ')
          b = 'a';
        else if (b == L'ç')
          b = 'c';
        else if (b == L'è' || b == L'é' || b == L'ê' || b == L'ë')
          b = 'e';
        else if (b == L'ð')
          b = 't';
        else if (b == L'ï' || b == L'ì' || b == L'ï' || b == L'î' || b == L'í')
          b = 'i';
        else if (b == L'ò' || b == L'ó' || b == L'ô' || b == L'õ' || b == L'ø')
          b = 'o';
        else if (b == L'ù' || b == L'ú' || b == L'û' || b == L'ü')
          b = 'u';
        else if (b == L'ý')
          b = 'y';
        else
          b = '-';

        out.push_back(b);
      }
    }
  }
  else {
     for (size_t k = 0; k < input.length(); k++) {
      wchar_t b = input[k];
      if (b < 32 || b == '*' || b == '?' || b==':' || b=='/' || b == '\\')
        b = '_';
      out.push_back(b);
     }
  }
  return out;
}

void capitalize(wstring &str) {
  if (str.length() > 0) {
    auto bf = str.c_str();
    CharUpperBuff(const_cast<LPWSTR>(bf), 1);
  }
}

/** Return bias in seconds. UTC = local time + bias. */
int getTimeZoneInfo(const wstring &date) {
  static wchar_t lastDate[16] = {0};
  static int lastValue = -1;
  // Local caching
  if (lastValue != -1 && lastDate == date) {
    return lastValue;
  }
  wcscpy_s(lastDate, 16, date.c_str());
//  TIME_ZONE_INFORMATION tzi;
  SYSTEMTIME st;
  convertDateYMS(date, st, false);
  st.wHour = 12;
  SYSTEMTIME utc;
  TzSpecificLocalTimeToSystemTime(0, &st, &utc);

  int datecode = ((st.wYear * 12 + st.wMonth) * 31) + st.wDay;
  int datecodeUTC = ((utc.wYear * 12 + utc.wMonth) * 31) + utc.wDay;

  int daydiff = 0;
  if (datecodeUTC > datecode)
    daydiff = 1;
  else if (datecodeUTC < datecode)
    daydiff = -1;

  int t = st.wHour * 3600;
  int tUTC = daydiff * 24 * 3600 + utc.wHour * 3600 + utc.wMinute * 60 + utc.wSecond;

  lastValue = tUTC - t;
  return lastValue;
}

wstring getTimeZoneString(const wstring &date) {
  int a = getTimeZoneInfo(date);
  if (a == 0)
    return L"+00:00";
  else if (a>0) {
    wchar_t bf[12];
    swprintf_s(bf, L"-%02d:%02d", a/3600, (a/60)%60);
    return bf;
  }
  else {
    wchar_t bf[12];
    swprintf_s(bf, L"+%02d:%02d", a/-3600, (a/-60)%60);
    return bf;
  }
}

bool compareBib(const wstring &b1, const wstring &b2) {
  int l1 = b1.length();
  int l2 = b2.length();
  if (l1 != l2)
    return l1 < l2;

  wchar_t maxc = 0, minc = numeric_limits<wchar_t>::max(); 
  for (int k = 0; k < l1; k++) {
    wchar_t b = b1[k];
    maxc = max(maxc, b);
    minc = min(minc, b);
  }
  for (int k = 0; k < l2; k++) {
    wchar_t b = b2[k];
    maxc = max(maxc, b);
    minc = min(minc, b);
  }

  unsigned coeff = maxc-minc + 1;

  unsigned z1 = 0;
  for (int k = 0; k < l1; k++) {
    wchar_t b = b1[k]-minc;
    z1 = coeff * z1 + b;
  }

  unsigned z2 = 0;
  for (int k = 0; k < l2; k++) {
    wchar_t b = b2[k]-minc;
    z2 = coeff * z2 + b;
  }

  return z1 < z2;
}

/// Split a name into first name and last name
int getNameCommaSplitPoint(const wstring &name) {
  int commaSplit = -1;

  for (unsigned k = 1; k + 1 < name.size(); k++) {
    if (name[k] == ',') {
      commaSplit = k;
      break;
    }
  }

  if (commaSplit >= 0) {
    commaSplit += 2;
  }
  
  return commaSplit;
}

/// Split a name into first name and last name
int getNameSplitPoint(const wstring &name) {
  int split[10];
  int nSplit = 0;

  for (unsigned k = 1; k + 1<name.size(); k++) {
    if (iswspace(name[k])) {
      split[nSplit++] = k;
      if ( nSplit>=9 )
        break;
    }
  }
  if (nSplit == 1)
    return split[0] + 1;
  else if (nSplit == 0)
    return -1;
  else {
    const oWordList &givenDB = lang.get().getGivenNames();
    int sp_ix = 0;
    for (int k = 1; k<nSplit; k++) {
      wstring sn = name.substr(split[k-1]+1, split[k] - split[k-1]-1);
      if (!givenDB.lookup(sn.c_str()))
        break;
      sp_ix = k;
    }
    return split[sp_ix]+1;
  }
}

wstring getGivenName(const wstring &name) {
  int sp = getNameCommaSplitPoint(name);
  if (sp != -1) {
    return trim(name.substr(sp));
  }

  sp = getNameSplitPoint(name);
  if (sp == -1)
    return trim(name);
  else
    return trim(name.substr(0, sp));
}

wstring getFamilyName(const wstring &name) {
  int sp = getNameCommaSplitPoint(name);
  if (sp != -1) {
    return trim(name.substr(0, sp - 2));
  }

  sp = getNameSplitPoint(name);
  if (sp == -1)
    return _EmptyWString;
  else
    return trim(name.substr(sp));
}

static bool noCapitalize(const wstring &str, size_t pos) {
  string word;
  while (pos < str.length() && !myIsSpace(str[pos])) {
    word.push_back(char(str[pos++]));
  }

  if (word == "of" || word == "for" || word == "at" || word == "by")
    return true;

  if (word == "and" || word == "or" || word == "from" || word == "as" || word == "in")
    return true;

  if (word == "with")
    return true;

  if (word == "to" || word == "next" || word == "a" || word == "an" || word == "the" || word == "but")
    return true;

  return false;
}

void capitalizeWords(wstring &str) {
  bool init = true;
  for (size_t i = 0; i < str.length(); i++) {
    wchar_t c = str[i];
    if (init && c >= 'a' && c <= 'z' && !noCapitalize(str, i))
      str[i] = c + ('A' - 'a');
    init = iswspace(c) != 0;
  }
}

void MeOSFileLock::unlockFile() {
  if (lockedFile != INVALID_HANDLE_VALUE)
    CloseHandle(lockedFile);

  lockedFile = INVALID_HANDLE_VALUE;
}

void MeOSFileLock::lockFile(const wstring &file) {
  unlockFile();
  lockedFile = CreateFile(file.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 
                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  //lockedFile = _open(file.c_str(), _O_RDWR);
  if (lockedFile == INVALID_HANDLE_VALUE) {
    int err = GetLastError();
    if (err == ERROR_SHARING_VIOLATION)
      throw meosException("open_error_locked");
    else {
      TCHAR buff[256];
      FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, buff, sizeof(buff), 0);
      throw meosException(L"open_error#" + file + L"#" + buff);
    }
  }
}

void processGeneralTime(const wstring &generalTime, wstring &meosTime, wstring &meosDate) {
  meosTime = L"";
  meosDate = L"";
  vector<wstring> parts;
  split(generalTime, L":-,. /\t", parts);
  
  // Indices into parts
  int year = -2;
  int month = -2;
  int day = -2;

  int hour = -2;
  int minute = -2;
  int second = -2;
  int subsecond = -2;
  
  int found = 0, base = -1, iter = 0;
  bool pm = wcsstr(generalTime.c_str(), L"PM") != 0 || 
            wcsstr(generalTime.c_str(), L"pm") != 0;
          
  while (iter < 2 && second==-2) {
    if (base == found)
      iter++;

    base = found;
    for (size_t k = 0; k < parts.size(); k++) {
      if (parts[k].empty())
        continue;
      int number = _wtoi(parts[k].c_str());
      if (number == 0 && parts[k][0] != '0')
        number = -1; // Not a number

      if (iter == 0) {
        // Date
        if (number > 1900 && number < 3000 && year < 0) {
          found++;
          year = k;
        }
        else if (number >= 1 && number <= 12 && month < 0 && (year == k+1 || year == k-1) ) {
          month = k;
          found++;
        }
        else if (number >= 1 && number <=31 && day < 0 && (month == k+1 || month == k-1)) {
          day = k;
          found++;
          iter++;
          break;
        }
        else if (number > 1011900 && number < 30000101 && year < 0 && month < 0 && day < 0) {
          day = k; //Date with format 20160906 or 06092016
          month = k;
          year = k;
          found++;
          break;
        }
      }
      else if (iter == 1) {
        
        // Time
        if (number >= 0 && number <= 24 && year != k && day != k && month != k && hour < 0) {
          hour = k;
          found++;
        }
        else if (number >= 0 && number <= 59 && minute < 0 && hour == k-1) {
          minute = k;
          found++;
        }
        else if (number >= 0 && number <= 59 && second < 0 && minute == k-1) {
          second = k;
          found++;
        }
        else if (number >= 0 && number < 1000 && subsecond < 0 && second == k-1 && k != year) {
          subsecond = k;
          found++;
          iter++;
          break;
        }
      }
    }
  }

  if (second >= 0 && minute >= 0 && hour>= 0) {
    if (!pm)
      meosTime = parts[hour] + L":" + parts[minute] + L":" + parts[second];
    else {
      int rawHour = _wtoi(parts[hour].c_str());
      if (rawHour < 12)
        rawHour+=12;
      meosTime = itow(rawHour) + L":" + parts[minute] + L":" + parts[second];
    }
  }

  if (year >= 0 && month >= 0 && day >= 0) {
    int y = -1, m = -1, d = -1;
    if (year != month) {
      y = _wtoi(parts[year].c_str());
      m = _wtoi(parts[month].c_str());
      d = _wtoi(parts[day].c_str());

      //meosDate = parts[year] + "-" + parts[month] + "-" + parts[day];
    } 
    else {
      int td = _wtoi(parts[year].c_str());
      int y1 = td / 10000;
      int m1 = (td / 100) % 100;
      int d1 = td % 100;
      bool ok = y1 > 2000 && y1 < 3000 && m1>=1 && m1<=12 && d1 >= 1 && d1 <= 31;
      if (!ok) {
        y1 = td % 10000;
        m1 = (td / 10000) % 100;
        d1 = (td / 1000000);

        ok = y1 > 2000 && y1 < 3000 && m1>=1 && m1<=12 && d1 >= 1 && d1 <= 31;
      }
      if (ok) {
        y = y1;
        m = m1;
        d = d1;
      }
        meosDate = itow(y1) + L"-" + itow(m1) + L"-" + itow(d1);
    }
    if (y > 0) {
      wchar_t bf[24];
      swprintf_s(bf, 24, L"%d-%02d-%02d", y, m, d);
      meosDate = bf;
    }
  }

}

void string2Wide(const string &in, wstring &out) {
  int cp = 1252;
  if (in.empty()) {
    out = L"";
    return;
  }
  out.reserve(in.size() + 1);
  out.resize(in.size(), 0);
  MultiByteToWideChar(cp, MB_PRECOMPOSED, in.c_str(), in.size(), &out[0], out.size() * sizeof(wchar_t));
}

void wide2String(const wstring &in, string &out) {
  out.clear();
  out.insert(out.begin(), in.begin(), in.end());// XXX Simple extend
}

void checkWriteAccess(const wstring &file) {
  if (_waccess(file.c_str(), 4) == 0)
    return;

  auto h = CreateFile(file.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
  if (h == INVALID_HANDLE_VALUE) {
    wchar_t absPath[260];
    _wfullpath(absPath, file.c_str(), 260);

    throw meosException(wstring(L"Du saknar behörighet att skriva till 'X'.#") + absPath);
  }
  CloseHandle(h);
}
