/*
*******************************************************************************
*                                                                             *
* COPYRIGHT:                                                                  *
*   (C) Copyright International Business Machines Corporation, 1998, 1999     *
*   Licensed Material - Program-Property of IBM - All Rights Reserved.        *
*   US Government Users Restricted Rights - Use, duplication, or disclosure   *
*   restricted by GSA ADP Schedule Contract with IBM Corp.                    *
*                                                                             *
*******************************************************************************
*
* File rbread.cpp
*
* Modification History:
*
*   Date        Name        Description
*   06/11/99    stephen     Creation.
*******************************************************************************
*/

#include <stdio.h>

#include "rbread.h"
#include "cmemory.h"
#include "cstring.h"
#include "filestrm.h"
#include "ustring.h"
#include "rbdata.h"

#include "unistr.h"
#include "rbdata.h"


/* Protos */
static void read_ustring(FileStream *rb, UnicodeString& val, 
			 UErrorCode& status);
static StringList* read_strlist(FileStream *rb, UnicodeString& listname, 
				UErrorCode& status);
static String2dList* read_strlist2d(FileStream *rb, UnicodeString& listname,
				    UErrorCode& status);
static TaggedList* read_taglist(FileStream *rb, UnicodeString& listname,
				UErrorCode& status);
static void RBHashtable_valueDeleter(void *value);


/* Read a string from a compiled resource file */
#define BUF_SIZE 128
void
read_ustring(FileStream *rb, 
	     UnicodeString& val,
	     UErrorCode& status)
{
  int32_t len = 0, readLen = 0, remain = 0;
  UChar buf [BUF_SIZE];

  if(U_FAILURE(status)) return;

  /* Read the string's length */
  T_FileStream_read(rb, &len, sizeof(len));

  /* Truncate the output string */
  val.remove();

  remain = len;

  /* Successively read the string's data from the file */
  while(remain != 0) {

    /* Read the next chunk of data */
    readLen = icu_min(BUF_SIZE, remain);
    icu_memset(buf, 0, readLen*sizeof(UChar));
    T_FileStream_read(rb, buf, sizeof(UChar) * readLen);

    /* Append the chunk to the string */
    val.append(buf, readLen);
    
    remain -= readLen;
  }

}

/* Read a string list */
StringList*
read_strlist(FileStream *rb,
	     UnicodeString& listname,
	     UErrorCode& status)
{
  int32_t i, count = 0;
  StringList *retval;

  if(U_FAILURE(status)) return 0;

  /* Setup the string list */
  retval = new StringList();
  if(retval == 0) {
    status = U_MEMORY_ALLOCATION_ERROR;
    return 0;
  }

  /* Read the name of this string list */
  read_ustring(rb, listname, status);
  if(U_FAILURE(status)) {
    delete retval;
    return 0;
  }

  /* Read the number of items in this string list */
  T_FileStream_read(rb, &count, sizeof(count));
  retval->fCount = count;

  /* Allocate space for the array of strings */
  retval->fStrings = new UnicodeString [ retval->fCount ];
  if(retval->fStrings == 0) {
    status = U_MEMORY_ALLOCATION_ERROR;
    delete retval;
    return 0;
  }

  /* Successively read strings in the list */
  for(i = 0; i < count; ++i) {
    read_ustring(rb, retval->fStrings[i], status);

    /* handle error */
    if(U_FAILURE(status)) {
      delete [] retval->fStrings;
      delete retval;
      return 0;
    }
  }
  
  return retval;
}

/* Read a 2-d string list */
String2dList*
read_strlist2d(FileStream *rb,
	       UnicodeString& listname,
	       UErrorCode& status)
{
  int32_t i, j;
  int32_t rows, itemcount;
  String2dList *retval;

  if(U_FAILURE(status)) return 0;
  
  /* Setup the 2-d string list */
  retval = new String2dList();
  if(retval == 0) {
    status = U_MEMORY_ALLOCATION_ERROR;
    return 0;
  }

  /* Read the name of this 2-d string list */
  read_ustring(rb, listname, status);
  if(U_FAILURE(status)) {
    delete retval;
    return 0;
  }

  /* Read the number of rows in this 2-d string list */
  T_FileStream_read(rb, &rows, sizeof(rows));
  retval->fRowCount = rows;

  /* Allocate space for the array of strings */
  retval->fStrings = new UnicodeString* [ retval->fRowCount ];
  if(retval->fStrings == 0) {
    status = U_MEMORY_ALLOCATION_ERROR;
    delete retval;
    return 0;
  }

  /* Read each row */
  for(i = 0; i < rows; ++i) {

    /* Read the number of items in this row */
    T_FileStream_read(rb, &itemcount, sizeof(itemcount));

    /* Hack for now - assume all rows are the same length */
    retval->fColCount = itemcount;

    /* Allocate enough space for each item */
    retval->fStrings[i] = new UnicodeString[itemcount];
    if(retval->fStrings[i] == 0) {
      status = U_MEMORY_ALLOCATION_ERROR;
      /* Complicated cleanup later */
      delete retval;
      return 0;
    }

    /* Read each item */
    for(j = 0; j < itemcount; ++j) {
      read_ustring(rb, retval->fStrings[i][j], status);
      
      /* handle error */
      if(U_FAILURE(status)) {
	/* Complicated cleanup later */
	delete retval;
	return 0;
      }
    }
  }

  return retval;
}

/* Read a tagged list */
TaggedList*
read_taglist(FileStream *rb, 
	     UnicodeString& listname,
	     UErrorCode& status)
{
  TaggedList *retval;
  int32_t i, count = 0;
  UnicodeString tag, value;

  if(U_FAILURE(status)) return 0;

  /* Setup the tagged list */
  retval = new TaggedList();
  if(retval == 0) {
    status = U_MEMORY_ALLOCATION_ERROR;
    return 0;
  }

  /* Read the name of this tagged list */
  read_ustring(rb, listname, status);
  if(U_FAILURE(status)) {
    delete retval;
    return 0;
  }

  /* Read the number of items in this tagged list */
  T_FileStream_read(rb, &count, sizeof(count));

  /* Successively read strings in the list */
  for(i = 0; i < count; ++i) {
    read_ustring(rb, tag, status);
    read_ustring(rb, value, status);
    
    /* handle error */
    if(U_FAILURE(status)) {
      delete retval;
      return 0;
    }

    /* put the tag/value in the list */
    retval->put(tag, value);
  }

  return retval;
}

/* Parse a compiled rb file */
UHashtable*
rb_parse(FileStream *f, 
	 UnicodeString& localename,
	 UErrorCode& status)
{
  UHashtable *retval;
  int32_t bom;
  int32_t itemtype;
  UnicodeString listname;
  StringList *strlist;
  String2dList *strlist2d;
  TaggedList *taglist;

  if(U_FAILURE(status)) return 0;

  /* Open the hashtable for saving data */
  retval = uhash_open((UHashFunction)uhash_hashUString, &status);
  if(retval == 0 || U_FAILURE(status)) {
    status = U_MEMORY_ALLOCATION_ERROR;
    return 0;
  }
  uhash_setValueDeleter(retval, RBHashtable_valueDeleter);

  /* Read the byte order mark from the file */
  T_FileStream_read(f, &bom, sizeof(bom));
  
  /* Verify the byte ordering matches */
  if(bom != sBOM) {
    uhash_close(retval);
    status = U_INVALID_FORMAT_ERROR;
    return 0;
  }

  /* Read the locale name from the file */
  read_ustring(f, localename, status);
  if(U_FAILURE(status)) {
    uhash_close(retval);
    return 0;
  }

  /* Successively read each list item */
  for(;;) {

    /* Read the next item type */
    T_FileStream_read(f, &itemtype, sizeof(itemtype));

    /* If we're at EOF, break */
    if(itemtype == sEOF) {
      break;
    }
   
    /* Parse each item and add it to the hashtable */
    switch(itemtype) {
    case sSTRINGLIST:
      strlist = read_strlist(f, listname, status);
      uhash_putKey(retval, listname.hashCode() & 0x7FFFFFFF, 
		   strlist, &status);
      if(U_FAILURE(status)) {
	uhash_close(retval);
	return 0;
      }
      break;

    case sSTRINGLIST2D:
      strlist2d = read_strlist2d(f, listname, status);
      uhash_putKey(retval, listname.hashCode() & 0x7FFFFFFF, 
		   strlist2d, &status);
      if(U_FAILURE(status)) {
	uhash_close(retval);
	return 0;
      }
      break;

    case sTAGGEDLIST:
      taglist = read_taglist(f, listname, status);
      uhash_putKey(retval, listname.hashCode() & 0x7FFFFFFF, 
		   taglist, &status);
      if(U_FAILURE(status)) {
	uhash_close(retval);
	return 0;
      }
      break;
    }
  }

  /* Check if any errors occurred during reading */
  if(T_FileStream_error(f) != 0) {
    status = U_FILE_ACCESS_ERROR;
    delete retval;
    return 0;
  }

  return retval;
}

void 
RBHashtable_valueDeleter(void *value)
{
  delete (ResourceBundleData*)value;
}
