/*------------------------------------------------------------------------------
 src/lib/dj/str.cxx
 
 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.
------------------------------------------------------------------------------*/

//#define DJ_DBG
#include "djStr.h"
#include <djMem.h>
#include <djDbg.h>
#include <ctype.h>
#include <fnmatch.h>
#include <stdio.h>
#include <stdlib.h>

namespace dj {

const Str List<char>::arg(const Str & in, int width, char fill) const
{
  DJ_DBG_FNC("dj::List<char>::arg");
  DJ_DBG_PRN("this = " << *this);
  DJ_DBG_PRN("in = " << in);
  DJ_DBG_PRN("width = " << width);
  DJ_DBG_PRN("fill = " << fill);
  
  // Find lowest occurrence of "%#".
  //
  int min = -1, index = -1, digits = -1;
  ConstStrIt it = start();
  int i = 0;
  while (it.isValid()) {
    if ('%' == it()) {
      ++it;
      ++i;
      const int start = i;
      for (; isDigit(it()) && it.isValid(); ++it, ++i) ;
      if (i != start) {
        const int size = i - start;
        const int tmp = toInt(cut(*this, start, size));
        if (-1 == min || tmp < min) {
          min = tmp;
          index = start - 1;
          digits = size;
        }
      }
    } else {
      ++it;
      ++i;
    }
  }
  if (-1 == index) return *this;
  DJ_DBG_PRN("index = " << index);
  DJ_DBG_PRN("digits = " << digits);
  
  // Pad input.
  //
  Str tmp = in;
  const int w = (width < 0 ? -width : width) - in.size();
  if (w > 0) {
    const int w2 = w < DJ_CSTR_SIZE ? w : DJ_CSTR_SIZE;
    char ascii [DJ_CSTR_SIZE];
    memSet(fill, ascii, w2);
    if (width < 0) {
      tmp.add(ascii, w2);
      for (int i = 0; i < (w - w2); ++i) tmp += ' ';
    } else {
      tmp.insert(ascii, 0, w2);
      for (int i = 0; i < (w - w2); ++i) tmp.push(' ');
    }
  }

  // Replace with input.
  //
  Str out(*this);
  out.replace(tmp, index, 1 + digits);
  DJ_DBG_PRN("out = " << out);
  return out;
}

const Str lowercase(const Str & in)
{
  Str out(in);
  for (StrIt it = out; it.isValid(); ++it)
    it(tolower(it()));
  return out;
}

const Str uppercase(const Str & in)
{
  Str out(in);
  for (StrIt it = out; it.isValid(); ++it)
    it(toupper(it()));
  return out;
}

static char _compareSortFnc(char in) { return in; }
static char _compareCaseSortFnc(char in)
  { return (in >= 97 && in <= 122) ? (in - 32) : in; }

inline static int _compareSort(
  const Str & a, const Str & b, int size, char fnc(char)
) {
  if (size < 0) size = a.size() > b.size() ? a.size() : b.size();
  ConstStrIt itA = a, itB = b;
  for (
    ;
    fnc(itA()) == fnc(itB()) &&
      itA.isValid() && itB.isValid() &&
      itA.count() < size - 1;
    ++itA, ++itB
  ) ;
  if (! itA.isValid() && ! itB.isValid()) return 0;
  else if (! itA.isValid()) return -1;
  else if (! itB.isValid()) return 1;
  else return fnc(itA()) - fnc(itB());
}

int compareSort(const Str & a, const Str & b, int size)
  { return _compareSort(a, b, size, _compareSortFnc); }
int compareCaseSort(const Str & a, const Str & b, int size)
  { return _compareSort(a, b, size, _compareCaseSortFnc); }

bool match(const Str & in, const Str & pattern)
{
  return ::fnmatch(pattern.toAscii(), in.toAscii(), 0) == 0;
}

bool isDigit(char in)
{
  return in >= '0' && in <= '9';
}

int toInt(const Str & in) { return ::atoi(in.toAscii()); }
uint64_t toLong(const Str & in) { return ::atol(in.toAscii()); }
double toFloat(const Str & in) { return ::atof(in.toAscii()); }

const Str toStr(int in)
{
  char tmp [DJ_CSTR_SIZE];
  const int size = ::snprintf(tmp, DJ_CSTR_SIZE, "%d", in);
  return Str(tmp, size);
}

const Str toStr(uint64_t in)
{
  char tmp [DJ_CSTR_SIZE];
  const int size = ::snprintf(tmp, DJ_CSTR_SIZE, "%lld", in);
  return Str(tmp, size);
}

const Str toStr(double in, int precision)
{
  char format [] = "%0. f";
  format[3] = '0' + precision;
  char tmp [DJ_CSTR_SIZE];
  const int size = ::snprintf(tmp, DJ_CSTR_SIZE, format, in);
  return Str(tmp, size);
}

const Str toStr(const char * in, int size)
{
  return Str(in, size);
}

}
