/* Copyright (C) 2003 MySQL AB

   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 2 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, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/* ***************************************************
       TEST OF INTERPRETER IN TUP
       Verify that the interpreter in TUP is able to
       handle and execute all the commands that the
       NdbApi can isssue

       Arguments:

       operationType     1 openScanRead,
                         2 openScanExclusive,
                         3 interpretedUpdateTuple,
                         4 interpretedDirtyUpdate,
                         5 interpretedDeleteTuple
                         6 deleteTuple
                         7 insertTuple
                         8 updateTuple
                         9 writeTuple
                         10 readTuple
                         11 readTupleExclusive
                         12 simpleRead
                         13 dirtyRead
                         14 dirtyUpdate
                         15 dirtyWrite

       tupTest           1 exitMethods
                         2 incValue
                         3 subValue
                         4 readAttr
                         5 writeAttr
                         6 loadConst
                         7 branch
                         8 branchIfNull
                         9 addReg
                         10 subReg
                         11 subroutineWithBranchLabel

        scanTest         Number of the test within each tupTest

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

#include <NdbOut.hpp>
#include <NdbThread.h>
#include <NdbMutex.h>
#include <NdbApi.hpp>
#include <NdbSchemaCon.hpp>
#include <NDBT.hpp>

#define MAXATTR 3
#define MAXTABLES 12
#define MAXSTRLEN 8
#define NUMBEROFRECORDS 1000

typedef enum {
    FAIL = 0,
    NO_FAIL,
	UNDEF
} TTYPE;

inline void  setAttrNames() ;
inline void  setTableNames() ;
void  error_handler(const NdbError & err, TTYPE);
void  create_table(Ndb*);
void  write_rows(Ndb*);
void  update_rows(Ndb*, int, int);
void  delete_rows(Ndb*, int, int);
void  verify_deleted(Ndb*);
void  read_and_verify_rows(Ndb*, bool pre);
void  scan_rows(Ndb*, int, int, int);
TTYPE t_exitMethods(int, NdbOperation*, int);
TTYPE t_incValue(int, NdbOperation*);
TTYPE t_subValue(int, NdbOperation*);
TTYPE t_readAttr(int, NdbOperation*);
TTYPE t_writeAttr(int, NdbOperation*);
TTYPE t_loadConst(int, NdbOperation*, int);
TTYPE t_branch(int, NdbOperation*);
TTYPE t_branchIfNull(int, NdbOperation*);
TTYPE t_addReg(int, NdbOperation*);
TTYPE t_subReg(int, NdbOperation*);
TTYPE t_subroutineWithBranchLabel(int, NdbOperation*);

char        tableName[MAXSTRLEN+1];
char        attrName[MAXATTR][MAXSTRLEN+1];
int         attrValue[NUMBEROFRECORDS] = {0};
int         pkValue[NUMBEROFRECORDS] = {0};
const int   tAttributeSize = 1 ;
const int   nRecords = 20 ;
int         bTestPassed = 0;
  


int main(int argc, const char** argv) {
  ndb_init();

  int operationType = 0;
  int tupTest = 0;
  int scanTest = 0;

  Ndb* pNdb = new Ndb("TEST_DB");
  pNdb->init();

  if (argc != 4  ||  sscanf(argv[1],"%d", &operationType) != 1) {
    operationType = 1 ;
  }
  if (argc != 4  ||  sscanf(argv[2],"%d", &tupTest) != 1) {
    tupTest = 1 ;
  }
  if (argc != 4  ||  sscanf(argv[3],"%d", &scanTest) != 1) {
    scanTest = 1 ;
  }

  ndbout << endl
      << "Test the interpreter in TUP using SimpleTable with\n"
      << nRecords << " records" << endl << endl ;

  if (pNdb->waitUntilReady(30) != 0) {
    ndbout << "NDB is not ready" << endl;
    return -1;
  }

  // Init the pk and attr values.
  for (int i = 0; i < NUMBEROFRECORDS; i ++)
    pkValue[i] = attrValue[i] = i ;

  setAttrNames() ;
  setTableNames() ;
  
  const void * p = NDBT_Table::discoverTableFromDb(pNdb, tableName);
  if (p != 0){
    create_table(pNdb);
  }
  
  write_rows(pNdb);

  ndbout << endl << "Starting interpreter in TUP test." << endl << "Operation type: " ;

  switch(operationType) {
  case 1:
    ndbout << "openScanRead" << endl;
    scan_rows(pNdb,  operationType,  tupTest,  scanTest);
    break;
  case 2:
    ndbout << "openScanExclusive" << endl;
    scan_rows(pNdb,  operationType,  tupTest,  scanTest);
    break;
  case 3:
    ndbout << "interpretedUpdateTuple" << endl;
    update_rows(pNdb,  tupTest,  operationType);
    break;
  case 4:
    ndbout << "interpretedDirtyUpdate" << endl;
    update_rows(pNdb,  tupTest,  operationType);
    break;
  case 5:
    ndbout << "interpretedDeleteTuple" << endl;
    delete_rows(pNdb,  tupTest,  operationType);
    break;
  case 6:
    ndbout << "deleteTuple" << endl;
    break;
  case 7:
    ndbout << "insertTuple" << endl;
    break;
  case 8:
    ndbout << "updateTuple" << endl;
    break;
  case 9:
    ndbout << "writeTuple" << endl;
    break;
  case 10:
    ndbout << "readTuple" << endl;
    break;
  case 11:
    ndbout << "readTupleExclusive" << endl;
    break;
  case 12:
    ndbout << "simpleRead" << endl;
    break;
  case 13:
    ndbout << "dirtyRead" << endl;
    break;
  case 14:
    ndbout << "dirtyUpdate" << endl;
    break;
  case 15:
    ndbout << "dirtyWrite" << endl;
    break;
  default:
    break ;

  }

//  read_and_verify_rows(pNdb, false);
	
//  delete_rows(pNdb, 0, 0) ;
  delete pNdb ;

  if (bTestPassed == 0) {
      ndbout << "OK: test passed" << endl;
      exit(0);
  }else{
      ndbout << "FAIL: test failed" << endl;
      exit(-1);
  }
}

void error_handler(const NdbError & err, TTYPE type_expected) {

  ndbout << err << endl ;

  switch (type_expected){
      case NO_FAIL:
          bTestPassed = -1 ;
          break ;
      case FAIL:
          ndbout << "OK: error is expected" << endl;
          break ;
      case UNDEF:
          ndbout << "assumed OK: expected result undefined" << endl ;
          break ;
      default:
          break ;
  }
}

void  create_table(Ndb* pMyNdb) {

  /****************************************************************
   *    Create SimpleTable and Attributes.
   *
   *    create table SimpleTable1(
   *        col0 int,
   *        col1 int not null,
   *        col2 int not null,
   *        col3 int not null ... 129)
   *
   ***************************************************************/

  int               check = -1 ;
  NdbSchemaOp       *MySchemaOp = NULL ;

  ndbout << endl << "Creating " << tableName << " ... " << endl;

   NdbSchemaCon* MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pMyNdb);
   if(!MySchemaTransaction) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);
   
   MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
   if( !MySchemaOp ) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);
   // Create table
   check = MySchemaOp->createTable( tableName,
                                     8,         // Table size
                                     TupleKey,  // Key Type
                                     40         // Nr of Pages
                                   );

   if( check == -1 ) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);

   ndbout << "Creating attributes ... " << flush;

   // Create first column, primary key
   check = MySchemaOp->createAttribute( attrName[0],
                                        TupleKey,
                                        32,
                                        1/*3, tAttributeSize */,
                                        UnSigned,
                                        MMBased,
                                        NotNullAttribute );

   if( check == -1 ) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);

   // create the 2 .. n columns.
   for ( int i = 1; i < MAXATTR; i++ ){
       check = MySchemaOp->createAttribute( attrName[i],
                                            NoKey,
                                            32,
                                            tAttributeSize,
                                            UnSigned,
                                            MMBased,
                                            NotNullAttribute );

      if( check == -1 ) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);
   }

   ndbout << "OK" << endl;

   if( MySchemaTransaction->execute() == -1 ) {
       ndbout << MySchemaTransaction->getNdbError() << endl;
   }else{
       ndbout << tableName[0] << " created" << endl;
   }


   NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);

   return;

}



void write_rows (Ndb* pMyNdb) {

  /****************************************************************
   *    Insert rows into SimpleTable
   *
   ***************************************************************/

  int check = -1 ;
  int loop_count_ops = nRecords ;
  NdbOperation      *MyOperation = NULL ;
  NdbConnection     *MyTransaction = NULL ;

  ndbout << endl << "Writing records ..."  << flush;

  for (int count=0 ; count < loop_count_ops ; count++){
      MyTransaction = pMyNdb->startTransaction();
      if (!MyTransaction) {
          error_handler(pMyNdb->getNdbError(), NO_FAIL);
      }//if

      MyOperation = MyTransaction->getNdbOperation(tableName);
      if (!MyOperation) {
        error_handler(pMyNdb->getNdbError(), NO_FAIL);
      }//if

      check = MyOperation->writeTuple();
      if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL);

      check = MyOperation->equal( attrName[0],(char*)&pkValue[count] );
      if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL);

      // Update the columns, index column already ok.
      for (int i = 1 ; i < MAXATTR; i++){
          if ((i == 2) && (count > 4)){
              check = MyOperation->setValue(attrName[i], (char*)&attrValue[count + 1]);
          }else{
              check = MyOperation->setValue(attrName[i], (char*)&attrValue[count]);
          }
        if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL);
      }
      check = MyTransaction->execute( Commit );
      if(check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL);
      
      pMyNdb->closeTransaction(MyTransaction);
    }
   ndbout <<" \tOK" << endl;
   return;
}

void verify_deleted(Ndb* pMyNdb) {

  int               check = -1 ;
  int               loop_count_ops = nRecords;
  NdbOperation*     pMyOperation = NULL ;

  ndbout << "Verifying deleted records..."<< flush;

  for (int count=0 ; count < loop_count_ops ; count++){

      NdbConnection*  pMyTransaction = pMyNdb->startTransaction();
      if (!pMyTransaction) error_handler(pMyNdb->getNdbError(), NO_FAIL);
      
      pMyOperation = pMyTransaction->getNdbOperation(tableName);
      if (!pMyOperation) error_handler(pMyNdb->getNdbError(), NO_FAIL);
      
      check = pMyOperation->readTuple();
      if( check == -1 ) error_handler( pMyTransaction->getNdbError(), NO_FAIL);

      check = pMyOperation->equal( attrName[0],(char*)&pkValue[count] );
      if( check == -1 ) error_handler( pMyTransaction->getNdbError(), NO_FAIL);

     // Exepect to receive an error
     if(pMyTransaction->execute(Commit) != -1)
         if( 626 == pMyTransaction->getNdbError().code){
             ndbout << pMyTransaction->getNdbError() << endl ;
             ndbout << "OK" << endl ;
         }else{
             error_handler(pMyTransaction->getNdbError(), NO_FAIL) ;
         }

     pMyNdb->closeTransaction(pMyTransaction);
    }

  ndbout << "OK" << endl;
  return;
};

void read_and_verify_rows(Ndb* pMyNdb, bool pre) {

  int               check = -1 ;
  int               loop_count_ops = nRecords;
  char              expectedCOL1[NUMBEROFRECORDS] = {0} ;
  char              expectedCOL2[NUMBEROFRECORDS] = {0} ;
  NdbConnection     *pMyTransaction = NULL ;
  NdbOperation      *MyOp = NULL ;
  NdbRecAttr*       tTmp = NULL ;
  int               readValue[MAXATTR] = {0} ;

  ndbout << "Verifying records...\n"<< endl;

  for (int count=0 ; count < loop_count_ops ; count++){
      
      pMyTransaction = pMyNdb->startTransaction();
      if (!pMyTransaction) error_handler(pMyNdb->getNdbError(), NO_FAIL);

      MyOp = pMyTransaction->getNdbOperation(tableName);
      if (!MyOp) error_handler( pMyTransaction->getNdbError(), NO_FAIL);


      check = MyOp->readTuple();
      if( check == -1 ) error_handler( MyOp->getNdbError(), NO_FAIL);
      
      check = MyOp->equal( attrName[0],(char*)&pkValue[count] );
      if( check == -1 ) error_handler( MyOp->getNdbError(), NO_FAIL);

      for (int count_attributes = 1; count_attributes < MAXATTR; count_attributes++){
          
          tTmp = MyOp->getValue( (char*)attrName[count_attributes], (char*)&readValue[count_attributes] );
          if(!tTmp) error_handler( MyOp->getNdbError(), NO_FAIL);
      }

      if( pMyTransaction->execute( Commit ) == -1 ) {
         error_handler(pMyTransaction->getNdbError(), NO_FAIL);
      } else {
        if (pre) {
          expectedCOL1[count] = readValue[1];
          expectedCOL2[count] = readValue[2];
        }
        
        ndbout << attrName[1] << "\t " << readValue[1] << "\t "
             << attrName[2] << "\t " << readValue[2] << endl;
      }

      pMyNdb->closeTransaction(pMyTransaction);

  }

  ndbout << "\nOK\n" << endl;

  return;

};

TTYPE t_exitMethods(int nCalls, NdbOperation * pOp, int opType) {

  ndbout << "Defining exitMethods test " << nCalls << ": " << endl ;;
  TTYPE ret_val = NO_FAIL ;

  switch(nCalls){
  case 1: // exit_nok if attr value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 14);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_ok() ;
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break;
  case 2: // exit_ok if attr value doesn't match
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 14);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_nok() ;
    pOp->def_label(0);
    if (opType == 3) {
      // For update transactions use incValue to update the tuple
      Uint32 val32 = 5;
      pOp->incValue("COL2", val32);
    }
    pOp->interpret_exit_ok();
    break ;
  case 3: // Non-existent value (128): exit_ok if if the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 128);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    pOp->interpret_exit_ok();
	ret_val = FAIL ;
    break;
  case 4: // Non-existent value (128): exit_nok if the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 128);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
	ret_val = FAIL ;
    break;
  case 5: // exit_nok of the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 2);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break ;
  case 6: // exit_ok of the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 2);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    if (opType == 3) {
      // For update transactions use incValue to update the tuple
      Uint32 val32 = 5;
      pOp->incValue("COL2", val32);
    }
    pOp->interpret_exit_ok();
    break;
  case 7: // exit_nok if the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 6);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break;
  case 8: // exit_ok if the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 6);
    pOp->branch_ne(1, 2, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    if (opType == 3) {
      // For update transactions use incValue to update the tuple
      Uint32 val32 = 5;
      pOp->incValue("COL2", val32);
    }
    pOp->interpret_exit_ok();
    break ;
  case 9: // exit_nok if the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 8);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break;
  case 10: // exit_ok if the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 8);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    if (opType == 3) {
      // For update transactions use incValue to update the tuple
      Uint32 val32 = 5;
      pOp->incValue("COL2", val32);
    }
    pOp->interpret_exit_ok();
    break;
  case 11: // exit_nok if the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 10);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break;
  case 12:
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 10);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    if (opType == 3) {
      // For update transactions use incValue to update the tuple
      Uint32 val32 = 5;
      pOp->incValue("COL2", val32);
    }
    pOp->interpret_exit_ok();
    break;
  case 13:
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 10);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break;
  case 14: // exit_nok if the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 12);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break;
  case 15: // exit_ok if the value matches
    pOp->read_attr("COL1", 1);
    pOp->load_const_u32(2, 12);
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    if (opType == 3) {
      // For update transactions use incValue to update the tuple
      Uint32 val32 = 5;
      pOp->incValue("COL2", val32);
    }
    pOp->interpret_exit_ok();
  case 16:
    pOp->interpret_exit_nok();
    ret_val = FAIL ;
    break;
  case 17:
    pOp->interpret_exit_ok();
    break ;
  case 18:
    pOp->interpret_exit_nok();
    ret_val = FAIL ;
    break ;
  default:
    break ;
  }
  return ret_val;
}

TTYPE t_incValue(int nCalls, NdbOperation* pOp) {

  ndbout << "Defining incValue test " << nCalls << ": ";
  TTYPE ret_val = NO_FAIL;

  Uint32 val32 = 5;
  Uint64 val64 = 5;
  Uint32 attr0 = 0;
  Uint32 attr1 = 1;
  Uint32 attr20 = 20;

  switch(nCalls) {
  case 1:
    pOp->incValue(attrName[1], val32);
    break;
  case 2:
    pOp->incValue(attr1, val32);
    break;
  case 3:
    pOp->incValue(attrName[1], val64);
    break;
  case 4:
    pOp->incValue(attr1, val64);
    break;
  case 5:
    pOp->incValue(attrName[0], val32);
    ret_val = FAIL ;
    break;
  case 6:
    pOp->incValue(attrName[0], val64);
    ret_val = FAIL ;
    break;
  case 7:
    pOp->incValue(attr0, val32);
    ret_val = FAIL ;
    break;
  case 8:
    pOp->incValue(attr0, val64);
    ret_val = FAIL ;
    break;
  case 9:
    pOp->incValue("COL20", val32);
    ret_val = FAIL ;
    break;
  case 10:
    pOp->incValue("COL20", val64);
    ret_val = FAIL ;
    break;
  case 11:
    pOp->incValue(attr20, val32);
    ret_val = FAIL ;
    break;
  case 12:
    pOp->incValue(attr20, val64);
    ret_val = FAIL ;
    break;
  default:
      break ;
  }
  return ret_val;
}

TTYPE t_subValue(int nCalls, NdbOperation* pOp) {

  ndbout << "Defining subValue test " << nCalls << ": ";

  Uint32 val32 = 5;
  Uint64 val64 = 5;
  Uint32 attr0 = 0;
  Uint32 attr1 = 1;
  Uint32 attr20 = 20;

  TTYPE ret_val = NO_FAIL;

  switch(nCalls) {
  case 1:
    pOp->subValue("COL2", val32);
    break;
  case 2:
    pOp->subValue(attr1, val32);
    break;
  case 3:
    pOp->subValue("COL0", val32);
    ret_val = FAIL ;
    break;
  case 4:
    pOp->subValue(attr0, val32);
    ret_val = FAIL ;
    break;
  case 5:
    pOp->subValue("COL20", val32);
    ret_val = FAIL ;
    break;
  case 6:
    pOp->subValue(attr20, val32);
    ret_val = FAIL ;
    break;
  case 7:
    // Missing implementation
    //pOp->subValue("COL20", val64);
    ndbout << "Missing implementation" << endl;
    break;
  case 8:
    // Missing implementation
    //pOp->subValue("COL2", val64);
    ndbout << "Missing implementation" << endl;
    break;
  case 9:
    // Missing implementation
    //pOp->subValue("COL0", val64);
    ndbout << "Missing implementation" << endl;
    break;
  case 10:
    // Missing implementation
    //pOp->subValue(attr1, val64);
    ndbout << "Missing implementation" << endl;
    break;
  case 11:
    // Missing implementation
    //pOp->subValue(attr0, val64);
    ndbout << "Missing implementation" << endl;
    break;
  case 12:
    // Missing implementation
    //pOp->subValue(attr20, val64);
    ndbout << "Missing implementation" << endl;
    break;
  default:
      break ;
  }
  return ret_val ;
}

TTYPE t_readAttr(int nCalls, NdbOperation* pOp) {

  ndbout << "Defining readAttr test " << nCalls << ": ";

  Uint32 attr0 = 0;
  Uint32 attr1 = 1;
  Uint32 attr20 = 20;
  TTYPE ret_val = NO_FAIL;

  switch(nCalls) {
  case 1:
    pOp->read_attr("COL1", 1);
    break;
  case 2:
    pOp->read_attr(attr1, 1);
    ret_val = NO_FAIL ;
    break;
  case 3:
    pOp->read_attr("COL0", 1);
    ret_val = NO_FAIL ;
    break;
  case 4:
    pOp->read_attr(attr0, 1);
    ret_val = NO_FAIL ;
    break;
  case 5:
    pOp->read_attr("COL20", 1);
    ret_val = FAIL ;
    break;
  case 6:
    pOp->read_attr(20, 1);
    ret_val = FAIL ;
    break;
  case 7:
    pOp->read_attr("COL1", 8);
    ret_val = FAIL ;
    break;
  case 8:
    pOp->read_attr(attr1, 8);
    ret_val = FAIL ;
    break;
  default:
      break ;
  }
  return ret_val;
}

TTYPE t_writeAttr(int nCalls, NdbOperation* pOp) {

  ndbout << "Defining writeAttr test " << nCalls << ": ";

  pOp->load_const_u32(1, 5);

  Uint32 attr0 = 0;
  Uint32 attr1 = 1;
  Uint32 attr20 = 20;
  TTYPE ret_val = NO_FAIL ;

  switch(nCalls) {
  case 1:
    pOp->write_attr("COL1", 1);
    break;
  case 2:
    pOp->write_attr(attr1, 1);
    break;
  case 3:
    pOp->write_attr("COL0", 1);
    ret_val = FAIL ;
    break;
  case 4:
    pOp->write_attr(attr0, 1);
    ret_val = FAIL ;
    break;
  case 5:
    pOp->write_attr("COL20", 1);
    ret_val = FAIL ;
    break;
  case 6:
    pOp->write_attr(20, 1);
    ret_val = FAIL ;
    break;
  case 7:
    pOp->write_attr("COL1", 2);
    ret_val = FAIL ;
    break;
  case 8:
    pOp->write_attr(attr1, 2);
    ret_val = FAIL ;
    break;
  case 9:
    pOp->write_attr("COL1", 8);
    ret_val = FAIL ;
    break;
  case 10:
    pOp->write_attr(attr1, 8);
    ret_val = FAIL ;
    break;
  default:
      break ;
  }
  return ret_val ;
}

TTYPE t_loadConst(int nCalls, NdbOperation* pOp, int opType) {

  ndbout << "Defining loadConst test " << nCalls << " : ";
  TTYPE ret_val = NO_FAIL ;

  switch(nCalls) {
  case 1:
    // Loading null into a valid register
    pOp->load_const_null(1);
    break;
  case 2:
    // Loading null into an invalid register
    pOp->load_const_null(8);
    ret_val = FAIL ;
    break;
  case 3:
    // Test loading a 32-bit value (>65536)
    pOp->load_const_u32(1, 65600);
    break;
  case 4:
    // Test loading a 16-bit value (<65536)
    pOp->load_const_u32(1, 65500);
    break;
  case 5:
    // Test by loading to a non-valid register
    pOp->load_const_u32(8, 2);
    ret_val = FAIL ;
    break;
  case 6:
    // Test by loading a 64-bit value
    pOp->load_const_u64(1, 65600);
    break;
  case 7:
    // Test by loading a non-valid register
    pOp->load_const_u64(8, 2);
    ret_val = FAIL ;
    break;
  case 8:
    // Test by loading a valid register with -1
    pOp->load_const_u64(1, -1);
    ret_val = FAIL ;
    break;

  default:
      break ;
  }

  if (opType == 3 && FAIL != ret_val)
    pOp->write_attr("COL1", 1);
  return ret_val;
}

TTYPE t_branch(int nCalls, NdbOperation* pOp) {

  ndbout << "Defining branch test " << nCalls << ": " ;
  
  TTYPE ret_val = NO_FAIL ;
  Uint32 val32=5;

  pOp->read_attr("COL1", 1);
  pOp->load_const_u32(2, val32);

  switch(nCalls) {
  case 1:
    pOp->branch_eq(1, 2, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    pOp->interpret_exit_ok();
    break;
  case 2:
    pOp->branch_eq(2, 1, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    pOp->interpret_exit_ok();
    break;
  case 3:
    pOp->branch_eq(1, 1, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    pOp->interpret_exit_ok();
    break;
  default:
      //ndbout << "t_branch: default case (no test case)" << endl ;
      //return ret_val = NO_FAIL ;
      break ;
  }
  return ret_val;
}

TTYPE t_branchIfNull(int nCalls, NdbOperation* pOp) {

  TTYPE ret_val = NO_FAIL;
  ndbout << "Defining branchIfNull test " << nCalls << ": " << endl ;

  switch(nCalls) {
  case 1:
    pOp->load_const_u32(1, 1);
    pOp->branch_ne_null(1, 0);
    pOp->interpret_exit_nok();
    pOp->def_label(0);
    pOp->interpret_exit_ok();
    break;
  case 2:
    pOp->load_const_null(1);
    pOp->branch_ne_null(1, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break;
  case 3:
    pOp->load_const_u32(1, 1);
    pOp->branch_eq_null(1, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break;
  case 4:
    pOp->load_const_null(1);
    pOp->branch_ne_null(1, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    break;
  case 5:
    // Test with a non-initialized register
    pOp->branch_ne_null(3, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    ret_val = FAIL ;
    break;
  case 6:
    // Test with a non-existing register
    pOp->branch_ne_null(8, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    ret_val = FAIL ;
    break;
  case 7:
    // Test with a non-initialized register
    pOp->branch_eq_null(3, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    ret_val = FAIL ;
    break;
  case 8:
    // Test with a non-existing register
    pOp->branch_ne_null(8, 0);
    pOp->interpret_exit_ok();
    pOp->def_label(0);
    pOp->interpret_exit_nok();
    ret_val = FAIL ;
    break;
  default:
      break ;
  }

  return ret_val;
}

TTYPE t_addReg(int nCalls, NdbOperation* pOp) {

  TTYPE ret_val = NO_FAIL ;
  
  ndbout << "Defining addReg test " << nCalls << ": ";

  pOp->load_const_u32(1, 65500);
  pOp->load_const_u32(2, 500);
  pOp->load_const_u64(3, 65600);
  pOp->load_const_u64(4, 95600);

  switch(nCalls) {
  case 1:
    pOp->add_reg(1, 2, 5);
    break;
  case 2:
    pOp->add_reg(1, 3, 5);
    break;
  case 3:
    pOp->add_reg(3, 1, 5);
    break;
  case 4:
    pOp->add_reg(3, 4, 5);
    break;
  case 5:
    pOp->add_reg(1, 6, 5);
    break;
  case 6:
    pOp->add_reg(6, 1, 5);
    break;
  case 7: // illegal register
    pOp->add_reg(1, 8, 5);
    ret_val = FAIL ;
    break;
  case 8: // another illegal register
    pOp->add_reg(8, 1, 5); 
    ret_val = FAIL ;
    break;
  case 9: // and another one
    pOp->add_reg(1, 2, 8);
    ret_val = FAIL ;
    break;
  default:
    break ;
  }
  pOp->load_const_u32(7, 65000);
  pOp->branch_eq(5, 7, 0);
  pOp->interpret_exit_nok();
  pOp->def_label(0);
  pOp->interpret_exit_ok();

  return ret_val ;
}

TTYPE t_subReg(int nCalls, NdbOperation* pOp) {

  TTYPE ret_val = NO_FAIL ;
  ndbout << "Defining subReg test: " << nCalls << endl;

  pOp->load_const_u32(1, 65500);
  pOp->load_const_u32(2, 500);
  pOp->load_const_u64(3, 65600);
  pOp->load_const_u64(4, 95600);

  switch(nCalls) {
  case 1:
    pOp->sub_reg(1, 2, 5);
    pOp->load_const_u32(7, 65000);
    break;
  case 2:
    pOp->sub_reg(1, 3, 5);
    pOp->load_const_u64(7, (Uint64)-100);
    break;
  case 3:
    pOp->sub_reg(3, 1, 5);
    pOp->load_const_u64(7, (Uint64)100);
    break;
  case 4:
    pOp->sub_reg(3, 4, 5);
    pOp->load_const_u64(7, (Uint64)-30000);
    break;
  case 5:
    pOp->sub_reg(1, 6, 5);
    pOp->load_const_u64(7, 0);
    ret_val = FAIL ;
    break;
  case 6:
    pOp->sub_reg(6, 1, 5);
    pOp->load_const_u64(7, 0);
    ret_val = FAIL ;
    break;
  case 7:
    pOp->sub_reg(1, 8, 5);
    pOp->load_const_u64(7, 0);
    ret_val = FAIL ;
    break;
  case 8:
    pOp->sub_reg(8, 1, 5);
    pOp->load_const_u64(7, 0);
    ret_val = FAIL ;
    break;
  case 9:
    pOp->sub_reg(1, 2, 8);
    pOp->load_const_u32(7, (Uint32)65000);
    ret_val = FAIL;
    break;
  default:
      //ndbout << "t_subReg: default case (no test case)" << endl ;
      //return ret_val = NO_FAIL ;
      break ;
  }
  pOp->branch_eq(5, 7, 0);
  pOp->interpret_exit_nok() ;
  pOp->def_label(0);
  pOp->interpret_exit_ok() ;

  return ret_val;
}

TTYPE t_subroutineWithBranchLabel(int nCalls, NdbOperation* pOp) {

  TTYPE ret_val = NO_FAIL ;
  ndbout << "Defining subroutineWithBranchLabel test:" << nCalls << endl;

  pOp->load_const_u32(1, 65500);
  pOp->load_const_u32(2, 500);
  pOp->load_const_u64(3, 65600);
  pOp->load_const_u64(4, 95600);
  pOp->load_const_u32(7, 65000);
  pOp->call_sub(0) ;

  switch(nCalls) {
  case 1:
    pOp->def_subroutine(0) ;
    pOp->add_reg(1, 2, 5);
    break;
  case 2:
    pOp->def_subroutine(0) ;
    pOp->add_reg(1, 3, 5);
    break;
  case 3:
    pOp->def_subroutine(0) ;
    pOp->add_reg(3, 1, 5);
    break;
  case 4:
    pOp->def_subroutine(0) ;
    pOp->add_reg(3, 4, 5);
    break;
  case 5:
    pOp->def_subroutine(0) ;
    pOp->add_reg(1, 6, 5);
    break;
  case 6:
    pOp->def_subroutine(0) ;
    pOp->add_reg(6, 1, 5);
    break;
  case 7: // illegal register
    pOp->def_subroutine(0) ;
    pOp->add_reg(1, 8, 5);
    ret_val = FAIL ;
    break;
  case 8: // another illegal register
    pOp->def_subroutine(0) ;
    pOp->add_reg(8, 1, 5); 
    ret_val = FAIL ;
    break;
  case 9: // and another one
    pOp->def_subroutine(0) ;
    pOp->add_reg(1, 2, 8);
    ret_val = FAIL ;
    break;
  case 10: // test subroutine nesting
      for(int sub = 0; sub < 25 ; ++sub){
          pOp->call_sub(sub) ;
          pOp->def_subroutine(sub + 1) ;
          pOp->interpret_exit_ok() ;
          pOp->ret_sub() ;
      }
    ret_val = FAIL ;
  default:
    break ;
  }
  
  pOp->branch_label(0) ;
  pOp->interpret_exit_nok() ;
  pOp->def_label(0) ;
  pOp->interpret_exit_ok() ;
  pOp->ret_sub() ;

  return ret_val ;
}


void scan_rows(Ndb* pMyNdb, int opType, int tupleType, int scanType) {

  int           check = -1 ; 
  int           loop_count_ops = nRecords ;
  int           eOf = -1 ;
  int           readValue = 0 ;
  int           readValue2 = 0 ;
  int           scanCount = 0 ;
  TTYPE         fail = NO_FAIL ;

  for (int count=0 ; count < loop_count_ops ; count++)    {
  NdbConnection* MyTransaction = pMyNdb->startTransaction();
  if (!MyTransaction) error_handler(pMyNdb->getNdbError(), NO_FAIL);

  NdbOperation*  MyOperation = MyTransaction->getNdbOperation(tableName);
  if (!MyOperation) error_handler(pMyNdb->getNdbError(), NO_FAIL);

  if (opType == 1)
    // Open for scan read, Creates the SCAN_TABREQ and if needed
    // SCAN_TABINFO signals.
    check = MyOperation->openScanRead(1);
  else if (opType == 2)
    // Open for scan with update of rows.
    check = MyOperation->openScanExclusive(1);

  // Create ATTRINFO signal(s) for interpreted program used for
  // defining search criteria and column values that should be returned.

  scanCount = count+1 ;

  switch(tupleType) {
  case 1:
    fail = t_exitMethods(scanCount, MyOperation,  opType);
    break;
  case 2:
    fail = t_incValue(scanCount, MyOperation);
    break;
  case 3:
    fail = t_subValue(scanCount, MyOperation);
    break;
  case 4:
    fail = t_readAttr(scanCount, MyOperation);
    break;
  case 5:
    fail = t_writeAttr(scanCount, MyOperation);
    break;
  case 6:
    fail = t_loadConst(scanCount, MyOperation,  opType);
    break;
  case 7:
    fail = t_branch(scanCount, MyOperation);
    break;
  case 8:
    fail = t_branchIfNull(scanCount, MyOperation);
    break;
  case 9:
    fail = t_addReg(scanCount, MyOperation);
    break;
  case 10:
    fail = t_subReg(scanCount, MyOperation);
    break;
  case 11:
    fail = t_subroutineWithBranchLabel(scanCount, MyOperation);
    break;
  default:
    break ;
  }

  if(11 != tupleType) MyOperation->getValue(attrName[1], (char*)&readValue);

  // Sends the SCAN_TABREQ, (SCAN_TABINFO) and ATTRINFO signals and then
  // reads the answer in TRANSID_AI. Confirmation is received through SCAN_TABCONF or
  // SCAN_TABREF if failure.
  check = MyTransaction->executeScan();
  if (check == -1) {
    //ndbout << endl << "executeScan returned: " << MyTransaction->getNdbError() << endl;
      error_handler(MyTransaction->getNdbError(), fail) ;
	  pMyNdb->closeTransaction(MyTransaction);
  }else{
    // Sends the SCAN_NEXTREQ signal(s) and reads the answer in TRANS_ID signals.
    // SCAN_TABCONF or SCAN_TABREF is the confirmation.
    while ((eOf = MyTransaction->nextScanResult()) == 0) {
      ndbout << readValue <<"; ";
      // Here we call takeOverScanOp for update of the tuple.
    }
    ndbout << endl ;

    pMyNdb->closeTransaction(MyTransaction);
    if (eOf == -1) {
      ndbout << endl << "nextScanResult returned: "<< MyTransaction->getNdbError() << endl;
    } else {
      ndbout << "OK" << endl;
    }
  }
  }
  return;

};


void  update_rows(Ndb* pMyNdb, int tupleType, int opType) {
   /****************************************************************
   *    Update rows in SimpleTable
   *    Only updates the first 3 cols.
   ***************************************************************/

  int check = -1 ;
  int loop_count_ops = nRecords ;
  int readValue[MAXATTR] = {0} ;
  TTYPE ret_val = NO_FAIL ;
  NdbConnection     *MyTransaction = NULL ;
  NdbOperation      *MyOperation = NULL ;

  ndbout << "Updating records ..." << endl << endl;

  for (int count=0 ; count < loop_count_ops ; count++){

    MyTransaction = pMyNdb->startTransaction();
    if (!MyTransaction) {
      error_handler(pMyNdb->getNdbError(), NO_FAIL);
      return;
    }//if

    MyOperation = MyTransaction->getNdbOperation(tableName);
    if (MyOperation == NULL) {
      error_handler(pMyNdb->getNdbError(), NO_FAIL);
      return;
    }//if

    //   if (operationType == 3)
    check = MyOperation->interpretedUpdateTuple();
    // else if (operationType == 4)
    // check = MyOperation->interpretedDirtyUpdate();
    if( check == -1 )
      error_handler(MyTransaction->getNdbError(), NO_FAIL);

    check = MyOperation->equal( attrName[0], (char*)&pkValue[count] );
    if( check == -1 )
      error_handler(MyTransaction->getNdbError(), NO_FAIL);

    switch(tupleType) {
    case 1:
      ret_val = t_exitMethods(count+1, MyOperation,  opType);
      break;
    case 2:
      ret_val = t_incValue(count+1, MyOperation);
      break;
    case 3:
      ret_val = t_subValue(count+1, MyOperation);
      break;
    case 4:
      ret_val = t_readAttr(count+1, MyOperation);
      break;
    case 5:
      ret_val = t_writeAttr(count+1, MyOperation);
      break;
    case 6:
      ret_val = t_loadConst(count+1, MyOperation,  opType);
      break;
    case 7:
      ret_val = t_branch(count+1, MyOperation);
      break;
    case 8:
      ret_val = t_branchIfNull(count+1, MyOperation);
      break;
    case 9:
      ret_val = t_addReg(count+1, MyOperation);
      break;
    case 10:
      ret_val = t_subReg(count+1, MyOperation);
      break;
    case 11:
      ret_val = t_subroutineWithBranchLabel(count+1, MyOperation);
      break;
    default:
        break ;
    }

    MyOperation->getValue("COL1", (char*)&readValue);

    if (MyTransaction->execute( Commit ) == -1 ) {
       error_handler(MyTransaction->getNdbError(), ret_val);
    }else if (NO_FAIL == ret_val ) {
      ndbout << "OK" << endl;
    } else {
      bTestPassed = -1;
      ndbout << "Test passed when expected to fail" << endl;
    }//if
    pMyNdb->closeTransaction(MyTransaction);

  }

  ndbout << "Finished updating records" << endl;
  return;
};

void delete_rows(Ndb* pMyNdb, int tupleTest, int opType) {

  /****************************************************************
   *    Delete rows from SimpleTable
   *
   ***************************************************************/

  int check = 1 ;
  int loop_count_ops = nRecords ;
  int readValue[MAXATTR] = {0};
  NdbConnection     *MyTransaction = NULL ;
  NdbOperation      *MyOperation = NULL ;
  TTYPE ret_val = NO_FAIL ;
  
  ndbout << "Deleting records ..."<< endl << endl;
 
  for (int count=0 ; count < loop_count_ops ; count++)    {

    MyTransaction = pMyNdb->startTransaction();
    if (!MyTransaction) error_handler(pMyNdb->getNdbError(), NO_FAIL) ;
    
    MyOperation = MyTransaction->getNdbOperation(tableName);
    if (!MyOperation) error_handler(pMyNdb->getNdbError(), NO_FAIL) ;

    check = MyOperation->interpretedDeleteTuple();
    if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL) ;

    check = MyOperation->equal( attrName[0], (char*)&pkValue[count] );
    if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL) ;

      switch(tupleTest) {
      case 1:
        ret_val = t_exitMethods(count+1, MyOperation,  opType);
        break;
      case 2:
        ret_val = t_incValue(count+1, MyOperation);
        break;
      case 3:
        ret_val = t_subValue(count+1, MyOperation);
        break;
      case 4:
        ret_val = t_readAttr(count+1, MyOperation);
        break;
      case 5:
        ret_val = t_writeAttr(count+1, MyOperation);
        break;
      case 6:
        ret_val = t_loadConst(count+1, MyOperation, opType);
        break;
      case 7:
        ret_val = t_branch(count+1, MyOperation);
        break;
      case 8:
        ret_val = t_branchIfNull(count+1, MyOperation);
        break;
      case 9:
        ret_val = t_addReg(count+1, MyOperation);
        break;
      case 10:
        ret_val = t_subReg(count+1, MyOperation);
        break;
      case 11:
        ret_val = t_subroutineWithBranchLabel(count+1, MyOperation);
        break;
      default:
        break ;
      }

      if(11 != tupleTest)MyOperation->getValue(attrName[1], (char*)&readValue) ;

      if (MyTransaction->execute( Commit ) == -1 ) {
         error_handler(MyTransaction->getNdbError(), ret_val);
      } else if (NO_FAIL == ret_val /*|| UNDEF == ret_val*/ ) {
        ndbout << "OK" << endl;
      } else {
        bTestPassed = -1;
        ndbout << "Test passed when expected to fail" << endl;
      }//if
      ndbout << endl;
      pMyNdb->closeTransaction(MyTransaction);
   }

   ndbout << "Finished deleting records" << endl;
   return;

};


inline void setAttrNames(){
  for (int i = 0; i < MAXATTR; i++){
      snprintf(attrName[i], MAXSTRLEN, "COL%d", i);
    }
}


inline void setTableNames(){
  snprintf(tableName, MAXSTRLEN, "TAB1");
}

