/********************************************************************
 * COPYRIGHT: 
 * Copyright (c) 1997-2009, International Business Machines Corporation and
 * others. All Rights Reserved.
 ********************************************************************/

/**
 * IntlTestCollator is the medium level test class for everything in the directory "collate".
 */

/***********************************************************************
* Modification history
* Date        Name        Description
* 02/14/2001  synwee      Compare with cintltst and commented away tests 
*                         that are not run.
***********************************************************************/

#include "unicode/utypes.h"

#if !UCONFIG_NO_COLLATION && !UCONFIG_NO_FILE_IO

#include "unicode/uchar.h"
#include "unicode/tstdtmod.h"
#include "cstring.h"
#include "ucol_tok.h"
#include "tscoll.h"
#include "dadrcoll.h"

U_CDECL_BEGIN
static void U_CALLCONV deleteSeqElement(void *elem) {
  delete((SeqElement *)elem);
}
U_CDECL_END

DataDrivenCollatorTest::DataDrivenCollatorTest() 
: seq(StringCharacterIterator("")),
status(U_ZERO_ERROR),
sequences(status)
{
  driver = TestDataModule::getTestDataModule("DataDrivenCollationTest", *this, status);
  sequences.setDeleter(deleteSeqElement);
  UCA = (RuleBasedCollator*)Collator::createInstance("root", status);
}

DataDrivenCollatorTest::~DataDrivenCollatorTest() 
{
  delete driver;
  delete UCA;
}

void DataDrivenCollatorTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par */)
{
  if(driver != NULL) {
    if (exec)
    {
        logln("TestSuite Collator: ");
    }
    const DataMap *info = NULL;
    TestData *testData = driver->createTestData(index, status);
    if(U_SUCCESS(status)) {
      name = testData->getName();
      if(testData->getInfo(info, status)) {
        log(info->getString("Description", status));
      }
      if(exec) {
        log(name);
          logln("---");
          logln("");
          processTest(testData);
      }
      delete testData;
    } else {
      name = "";
    }
  } else {
    dataerrln("collate/DataDrivenTest data not initialized!");
    name = "";
  }


}

UBool
DataDrivenCollatorTest::setTestSequence(const UnicodeString &setSequence, SeqElement &el) {
  seq.setText(setSequence);
  return getNextInSequence(el);
}

// Parses the sequence to be tested
UBool 
DataDrivenCollatorTest::getNextInSequence(SeqElement &el) {
  el.source.truncate(0);
  UBool quoted = FALSE;
  UBool quotedsingle = FALSE;
  UChar32 currChar = 0;

  while(currChar != CharacterIterator::DONE) {
    currChar= seq.next32PostInc();
    if(!quoted) {
      if(u_isWhitespace(currChar)) {
        continue;
      }
      switch(currChar) {
      case CharacterIterator::DONE:
        break;
      case 0x003C /* < */:
        el.relation = Collator::LESS;
        currChar = CharacterIterator::DONE;
        break;
      case 0x003D /* = */:
        el.relation = Collator::EQUAL;
        currChar = CharacterIterator::DONE;
        break;
      case 0x003E /* > */:
        el.relation = Collator::GREATER;
        currChar = CharacterIterator::DONE;
        break;
      case 0x0027 /* ' */: /* very basic quoting */
        quoted = TRUE;
        quotedsingle = FALSE;
        break;
      case 0x005c /* \ */: /* single quote */
        quoted = TRUE;
        quotedsingle = TRUE;
        break;
      default:
        el.source.append(currChar);
      }
    } else {
      if(currChar == CharacterIterator::DONE) {
        status = U_ILLEGAL_ARGUMENT_ERROR;
        errln("Quote in sequence not closed!");
        return FALSE;
      } else if(currChar == 0x0027) {
        quoted = FALSE;
      } else {
        el.source.append(currChar);
      }
      if(quotedsingle) {
        quoted = FALSE;
      }
    }
  }
  return seq.hasNext();
}

// Reads the options string and sets appropriate attributes in collator
void 
DataDrivenCollatorTest::processArguments(Collator *col, const UChar *start, int32_t optLen) {
  const UChar *end = start+optLen;
  UColAttribute attrib;
  UColAttributeValue value;

  if(optLen == 0) {
    return;
  }

  start = ucol_tok_getNextArgument(start, end, &attrib, &value, &status);
  while(start != NULL) {
    if(U_SUCCESS(status)) {
      col->setAttribute(attrib, value, status);
    }
    start = ucol_tok_getNextArgument(start, end, &attrib, &value, &status);
  }
}

void
DataDrivenCollatorTest::processTest(TestData *testData) {
  Collator *col = NULL;
  const UChar *arguments = NULL;
  int32_t argLen = 0;
  const DataMap *settings = NULL;
  const DataMap *currentCase = NULL;
  UErrorCode intStatus = U_ZERO_ERROR;
  UnicodeString testSetting;
  while(testData->nextSettings(settings, status)) {
    intStatus = U_ZERO_ERROR;
    // try to get a locale
    testSetting = settings->getString("TestLocale", intStatus);
    if(U_SUCCESS(intStatus)) {
      char localeName[256];
      testSetting.extract(0, testSetting.length(), localeName, "");
      col = Collator::createInstance(localeName, status);
      if(U_SUCCESS(status)) {
        logln("Testing collator for locale "+testSetting);
      } else {
        errln("Unable to instantiate collator for locale "+testSetting);
        return;
      }
    } else {
      // if no locale, try from rules
      intStatus = U_ZERO_ERROR;
      testSetting = settings->getString("Rules", intStatus);
      if(U_SUCCESS(intStatus)) {
        col = new RuleBasedCollator(testSetting, status);
        if(U_SUCCESS(status)) {
          logln("Testing collator for rules "+testSetting);
        } else {
          errln("Unable to instantiate collator for rules "+testSetting);
          return;
        }
      } else {
        errln("No collator definition!");
      }
    }
    
    int32_t cloneSize = 0;
    uint8_t* cloneBuf = NULL;
    RuleBasedCollator* clone = NULL;
    if(col != NULL){
      RuleBasedCollator* rbc = (RuleBasedCollator*)col;
      cloneSize = rbc->cloneBinary(NULL, 0, intStatus);
      intStatus = U_ZERO_ERROR;
      cloneBuf = (uint8_t*) malloc(cloneSize);
      cloneSize = rbc->cloneBinary(cloneBuf, cloneSize, intStatus);
      clone = new RuleBasedCollator(cloneBuf, cloneSize, UCA, intStatus);
      if(U_FAILURE(intStatus)){
          errln("Could not clone the RuleBasedCollator. Error: %s", u_errorName(intStatus));
          intStatus= U_ZERO_ERROR;
      }
      // get attributes
      testSetting = settings->getString("Arguments", intStatus);
      if(U_SUCCESS(intStatus)) {
        logln("Arguments: "+testSetting);
        argLen = testSetting.length();
        arguments = testSetting.getBuffer();
        processArguments(col, arguments, argLen);
        if(clone != NULL){
            processArguments(clone, arguments, argLen);
        }
        if(U_FAILURE(status)) {
          errln("Couldn't process arguments");
          break;
        }
      } else {
        intStatus = U_ZERO_ERROR;
      }
      // Start the processing
      while(testData->nextCase(currentCase, status)) {
        UnicodeString sequence = currentCase->getString("sequence", status);
        if(U_SUCCESS(status)) {
            processSequence(col, sequence);
            if(clone != NULL){
                processSequence(clone, sequence);
            }
        }
      }
    } else {
      errln("Couldn't instantiate a collator!");
    }
    delete clone;
    free(cloneBuf);
    delete col;
    col = NULL;
  }
}


void 
DataDrivenCollatorTest::processSequence(Collator* col, const UnicodeString &sequence) {
  Collator::EComparisonResult relation = Collator::EQUAL;
  UBool hasNext;
  SeqElement *source = NULL;
  SeqElement *target = NULL;
  int32_t j = 0;

  sequences.removeAllElements();

  target = new SeqElement(); 

  setTestSequence(sequence, *target);
  sequences.addElement(target, status);

  do {
    relation = Collator::EQUAL;
    target = new SeqElement(); 
    hasNext = getNextInSequence(*target);
    for(j = sequences.size(); j > 0; j--) {
      source = (SeqElement *)sequences.elementAt(j-1);
      if(relation == Collator::EQUAL && source->relation != Collator::EQUAL) {
        relation = source->relation;
      }
      doTest(col, source->source, target->source, relation);     
    }
    sequences.addElement(target, status);
    source = target;
  } while(hasNext);
}

#endif /* #if !UCONFIG_NO_COLLATION */
