/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation version
 * 2.1 of the License.
 *                                                                                  
 * This library 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
 * Lesser General Public License for more details.
 *                                                                                  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * END COPYRIGHT BLOCK **/

#include "libadminutil/resource.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "unicode/ures.h"
#include "unicode/ustring.h"

/* returns true if the given path is a valid directory, false otherwise */
static int
is_dir_ok(const char *path)
{
	PRFileInfo prinfo;
	int ret = 0;

	if (path && *path &&
		(PR_SUCCESS == PR_GetFileInfo(path, &prinfo)) &&
		prinfo.type == PR_FILE_DIRECTORY) {
		ret = 1;
	}

	return ret;
}

/*
  ----------------------------------------------------------------
  res_find_and_init_resource

  Initializes a property file path.  Looks for the package directory
  in a variety of well known locations, in order, and stops after
  the first successful attempt to stat the directory.
  1) the given path, if any
  2) the current working directory + "/property"
  3) getenv(ADMINUTIL_CONFDIR_ENV_VAR) + "/property"
  It is expected that applications will have their default property
  directory compiled in (via configure ; make) and that's what they
  will pass in as their first argument.  The other path lookup stuff
  is really for legacy apps or apps in which the user wants to change
  the property directory at runtime.
  If package is NULL, then path already is package specific e.g.
  path will usually be something like
  /usr/share/adminutil - the resource files will be in this directory e.g.
  /usr/share/adminutil/root.res,en.res,en_US.res, etc.
  -----------------------------------------------------------------
 */
PR_IMPLEMENT(Resource*)
res_find_and_init_resource(const char *path, const char *package)
{
	char resPath[PATH_MAX];
    char *adminutilConfDir = getenv(ADMINUTIL_CONFDIR_ENV_VAR);
	char *execPath;
	Resource *resource = NULL;

	/* case 1 */
	if (is_dir_ok(path)) {
		return res_init_resource(path, package);
	}

	/* case 2 */
	resPath[0] = '\0';
	execPath = getcwd(resPath, sizeof(resPath));
	if (execPath) {
		PL_strcatn(resPath, sizeof(resPath), "/property");
		if (!is_dir_ok(resPath)) {
			resPath[0] = '\0';
		}
	}

	/* case 3 */
	if (!resPath[0] && adminutilConfDir && *adminutilConfDir) {
		PR_snprintf(resPath, sizeof(resPath), "%s/property", adminutilConfDir);
		if (!is_dir_ok(resPath)) {
			resPath[0] = '\0';
		}
	}

	if (resPath[0]) {
		resource = res_init_resource(resPath, package);
	}

	return resource;
}

/*
  ----------------------------------------------------------------
  res_init_resource

  Initializes a property file path.
  package may be NULL - this means that path is already package specific
  e.g. /usr/share/adminutil
  -----------------------------------------------------------------
*/
PR_IMPLEMENT(Resource*)
res_init_resource(const char* path, const char* package)
{
  Resource *resource;
  char *resPath;
  char path_last_char;

  if (path == NULL) {
      /* both path and package cannot be NULL */
      if (package == NULL || PL_strlen(package) == 0) {
          return NULL;
      }
      path = "./";
  }

  path_last_char = path[PL_strlen(path) - 1];
  if (path_last_char != '/' && path_last_char != '\\') {
    resPath = PR_smprintf("%s%s%s", path,
                          package ? "/" : "",
                          package ? package : "");
  } else {
    resPath = PR_smprintf("%s%s", path,
                          package ? package : "");
  }
  
  resource = (Resource *)PR_Malloc(sizeof(Resource));
  if (NULL == resource) {
    return NULL;
  }
  memset(resource, 0, sizeof(Resource));

  /* ICU on Win does not like forward slashes in paths */
#ifdef XP_WIN32
  {
      char *c;
      for (c=resPath; *c; c++) {
          if (*c == '/') *c = '\\';
      }
  }
#endif

  resource->path = resPath;
  return resource;
}

/*
  -----------------------------------------------------------------------------
  res_getstring

  Gets a string by key from a resource file
  -----------------------------------------------------------------------------
*/
PR_IMPLEMENT(char*)
res_getstring(Resource* resource, char *key, char *locale, char *buffer, size_t bufsize, int *rc)
{
  char* result = NULL;
  int resultcode = -1;
  UResourceBundle *bundle = NULL;
  UErrorCode status = U_ZERO_ERROR;

  if (buffer) {
      *buffer = '\0';
  }

  if (resource == NULL || resource->path == NULL || key == NULL) {
      goto done;
  }

  bundle = ures_open(resource->path, (const char*)locale, &status);
  if (status == U_ILLEGAL_ARGUMENT_ERROR) {
      /* the locale string is bogus - just use the default */
      locale = NULL;
      status = U_ZERO_ERROR;
      bundle = ures_open(resource->path, (const char*)locale, &status);
  }

  if(U_SUCCESS(status) && bundle) {
      int32_t umsglen=0;
      const UChar *umsg = ures_getStringByKey(bundle, key, &umsglen, &status);

      if (U_SUCCESS(status) && umsg) {
         int32_t msglen=-1;
         if (buffer) {
             /* just convert the string into the given buffer - note that
                there may be truncation/overflow - see below */
             msglen = (int32_t)bufsize;
             u_strToUTF8(buffer, msglen, &msglen, umsg, umsglen, &status);
             buffer[bufsize-1] = '\0'; /* ensure null termination */
         } else {
             /* Get first the required buffer size */
             u_strToUTF8(NULL, 0, &msglen, umsg, umsglen, &status);
             if (msglen >0) {				 
                 result = PR_Malloc(msglen+1);
                 if (result) {
                     /* reset status, set to OVERFLOW by the last call to u_strToUTF8 */
                     status = U_ZERO_ERROR; 
                     /* now the real conversion with allocated buffer */
                     u_strToUTF8((char*)result, msglen+1, &msglen, umsg, umsglen, &status);
                     if (!U_SUCCESS(status)) {
                         PR_Free(result);
                         result = NULL;
                     } else {
                         result[msglen] = '\0';
                     }
                 }
             }
         }
         if (status == U_BUFFER_OVERFLOW_ERROR ||
             status == U_STRING_NOT_TERMINATED_WARNING) {
             resultcode = 1;
         } else if (U_SUCCESS(status)) {
             resultcode = 0;
         } else {
             resultcode = -1;
         }
      }

      ures_close(bundle);
  }

done:

  if (buffer) {
      result = buffer;
  } else if (!result) {
      result = PL_strdup("");
  }

  if (rc) {
      *rc = resultcode;
  }

  return result;
}


/*
   ----------------------------------------------------- 
   res_destroy_resource

   Frees resource returned by the init routine
   -----------------------------------------------------
 */

PR_IMPLEMENT(void)
res_destroy_resource(Resource* resource)
{
  if(resource == NULL) {
    return;
  }
  if(resource->path != NULL) {
    PR_Free(resource->path);
  }
  if(resource->package != NULL) {
    PR_Free(resource->package);
  }
  PR_Free(resource);
}
