/*
   Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.

   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; version 2 of the License.

   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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
*/

#ifndef NdbReceiver_H
#define NdbReceiver_H
#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL  // Not part of public interface

#include <ndb_types.h>


class Ndb;
class NdbImpl;
class NdbTransaction;
class NdbRecord;
class NdbRecAttr;
class NdbQueryOperationImpl;
class NdbReceiverBuffer;

class NdbReceiver
{
  friend class Ndb;
  friend class NdbImpl;
  friend class NdbOperation;
  friend class NdbQueryImpl;
  friend class NdbQueryOperationImpl;
  friend class NdbResultStream;
  friend class NdbScanOperation;
  friend class NdbIndexOperation;
  friend class NdbIndexScanOperation;
  friend class NdbTransaction;
  friend class NdbRootFragment;
  friend int compare_ndbrecord(const NdbReceiver *r1,
                      const NdbReceiver *r2,
                      const NdbRecord *key_record,
                      const NdbRecord *result_record,
                      bool descending,
                      bool read_range_no);
  friend int spjTest(int argc, char** argv);

public:
  enum ReceiverType	{ NDB_UNINITIALIZED,
			  NDB_OPERATION = 1,
			  NDB_SCANRECEIVER = 2,
			  NDB_INDEX_OPERATION = 3,
                          NDB_QUERY_OPERATION = 4
  };
  
  NdbReceiver(Ndb *aNdb);
  int init(ReceiverType type, void* owner);
  void release();
  ~NdbReceiver();
  
  Uint32 getId() const{
    return m_id;
  }

  ReceiverType getType() const {
    return m_type;
  }
 
  inline NdbTransaction * getTransaction(ReceiverType type) const;
  void* getOwner() const {
    return m_owner;
  }
  
  bool checkMagicNumber() const;
  static Uint32 getMagicNumber() { return (Uint32)0x11223344; }
  Uint32 getMagicNumberFromObject() const;

  inline void next(NdbReceiver* next_arg) { m_next = next_arg;}
  inline NdbReceiver* next() { return m_next; }
  
  void setErrorCode(int);

  /**
   * Construct a receive buffer for a batched result set.
   * 'buffer' has to be allocated with size as calculated by
   * result_bufsize, and pointer should be Uint32 aligned.
   */
  static
  NdbReceiverBuffer* initReceiveBuffer(
                           Uint32 *buffer,
                           Uint32 bufSize,      // Size in Uint32 words
                           Uint32 batchRows);

  /**
   * Prepare for receiving of rows into specified buffer.
   * This buffer is later navigated, and retrieved from,
   * by either getNextRow() or setCurrentRow(). The row is
   * then 'unpacked' into 'row_buffer' set by do_setup_ndbrecord().
   */
  void prepareReceive(NdbReceiverBuffer *buf);

private:
  Uint32 theMagicNumber;
  Ndb* const m_ndb;
  Uint32 m_id;
  Uint32 m_tcPtrI;
  ReceiverType m_type;
  void* m_owner;
  NdbReceiver* m_next;

  /**
   * At setup
   */
  class NdbRecAttr * getValue(const class NdbColumnImpl*, char * user_dst_ptr);
  void getValues(const NdbRecord*, char*);
  void prepareSend();

  static
  void calculate_batch_size(const NdbImpl&,
                            Uint32 parallelism,
                            Uint32& batch_size,
                            Uint32& batch_byte_size);

  void calculate_batch_size(Uint32 parallelism,
                            Uint32& batch_size,
                            Uint32& batch_byte_size) const;

  /**
   * Calculate size of result buffer which has to be 
   * allocated for a buffered result set, and later given to 
   * initReceiveBuffer() as 'buffer' argument.
   */
  static
  Uint32 result_bufsize(Uint32 batch_rows,
                        Uint32 batch_bytes,
                        Uint32 fragments,
                        const NdbRecord *result_record,
                        const Uint32* read_mask,
                        const NdbRecAttr *first_rec_attr,
                        Uint32 key_size,
                        bool read_range_no);
  /*
    Set up buffers for receiving TRANSID_AI and KEYINFO20 signals
    during a scan using NdbRecord.
  */
  void do_setup_ndbrecord(const NdbRecord *ndb_record,
                          char *row_buffer,
                          bool read_range_no, bool read_key_info);


  /**
   * Calculate size required for an 'unpacked' result row
   * where the current result row is stored. A buffer of this size is used
   * as 'row_buffer' argument to do_setup_ndbrecord().
   */
  static
  Uint32 ndbrecord_rowsize(const NdbRecord *ndb_record,
                           bool  read_range_no);

  int execKEYINFO20(Uint32 info, const Uint32* ptr, Uint32 len);
  int execTRANSID_AI(const Uint32* ptr, Uint32 len); 
  int execTCOPCONF(Uint32 len);
  int execSCANOPCONF(Uint32 tcPtrI, Uint32 len, Uint32 rows);

  /* Assist functions to execTRANSID_AI */
  const Uint32 * handle_extra_get_values(Uint32 & save_pos,
                                         Uint32 * aLength,
                                         const Uint32 *aDataPtr,
                                         Uint32 attrSize,
                                         bool isScan,
                                         Uint32 attrId,
                                         Uint32 origLength,
                                         bool & ndbrecord_part_done);
  const Uint32 * handle_attached_rec_attrs(Uint32 attrId,
                                           const Uint32 *aDataPtr,
                                           Uint32 origLength,
                                           Uint32 attrSize,
                                           Uint32 * aLength);

  /* Convert from packed transporter to NdbRecord / RecAttr format. */
  int unpackRow(const Uint32* ptr, Uint32 len, char* row); 

  /* NdbRecord describing row layout expected by API */
  const NdbRecord *m_ndb_record;

  /* The (single) current row in 'unpacked' NdbRecord format */
  char *m_row_buffer;

  /* Block of memory used to buffer all rows in a batch during scan. */
  NdbReceiverBuffer *m_recv_buffer;

  /**
   * m_read_range_no & m_read_key_info is true if we are reading 
   * range / keyinfo as part of scans.
   */
  bool m_read_range_no;
  bool m_read_key_info;

  /**
   * Holds the list of RecAttr defined by getValue()
   * which to retrieve data into when a row is unpacked.
   * These RecAttr's are owner by this NdbReceiver and
   * terminated by ::release()
   */
  class NdbRecAttr* m_firstRecAttr;
  class NdbRecAttr* m_lastRecAttr; // A helper for getValue()

  /* Savepoint for unprocessed RecAttr data from current row. */
  const Uint32* m_rec_attr_data;
  Uint32        m_rec_attr_len;
  
  /*
    When an NdbReceiver is sitting in the NdbScanOperation::m_sent_receivers
    array, waiting to receive TRANSID_AI data from the kernel, its index into
    m_sent_receivers is stored in m_list_index, so that we can remove it when
    done without having to search for it.
  */
  Uint32 m_list_index;
  /*
    m_current_row holds the next row / key to be delivered to 
    the application.
  */
  Uint32 m_current_row;

  /*
    m_expected_result_length: Total number of 32-bit words of TRANSID_AI and
    KEYINFO20 data to receive. This is set to zero until SCAN_TABCONF has
    been received.
   */
  Uint32 m_expected_result_length;
  Uint32 m_received_result_length;

  /**
   * Unpack a packed stream of field values, whose presence and nullness
   * is indicated by a leading bitmap into a list of NdbRecAttr objects
   * Return the number of words read from the input stream.
   */
  static
  Uint32 unpackRecAttr(NdbRecAttr**, Uint32 bmlen, 
                       const Uint32* aDataPtr, Uint32 aLength);

  /**
   * Unpack a stream of field values, whose presence and nullness
   * is indicated by a leading bitmap, into an NdbRecord row.
   * Return the number of words consumed.
   */
  static
  Uint32 unpackNdbRecord(const NdbRecord *record, Uint32 bmlen,
                         const Uint32* aDataPtr,
                         char* row);

  /**
   * Handle a stream of field values, both 'READ_PACKED' and plain
   * unpacked fields, into a list of NdbRecAttr objects.
   * Return 0 on success, or -1 on error
   */
  static
  int handle_rec_attrs(NdbRecAttr* rec_attr_list,
                       const Uint32* aDataPtr,
                       Uint32 aLength);

  
  /**
   * Unpack data for the specified 'row' previously stored into
   * the 'buffer'. Handles both the row in NdbRecord format, and
   * the key recieved as KEYINFO, if present.
   */
  const char *unpackBuffer(const NdbReceiverBuffer *buffer, Uint32 row);

  /**
   * Result set is navigated either sequentialy or randomly to a 
   * specific row. The NdbRecord contents is then unpacked into 
   * 'm_row_buffer' and returned. KeyInfo, Range no and RecAttr
   * values may be retrieved by specific calls below.
   */
  const char *getRow(const NdbReceiverBuffer* buffer, Uint32 row);
  const char *getNextRow();

  /* Fetch the NdbRecord part of current row */
  const char *getCurrentRow() const { return m_row_buffer; }

  /* get_range_no() returns the range_no for current row. */
  int get_range_no() const;

  /* Fetch keyinfo from KEYINFO20 signal for current row. */
  int get_keyinfo20(Uint32 & scaninfo, Uint32 & length,
                    const char * & data_ptr) const;

  /** Fetch RecAttr values for current row. */
  int get_AttrValues(NdbRecAttr* rec_attr_list) const;
};

#ifdef NDB_NO_DROPPED_SIGNAL
#include <stdlib.h>
#endif

inline
bool 
NdbReceiver::checkMagicNumber() const {
  bool retVal = (theMagicNumber == getMagicNumber());
#ifdef NDB_NO_DROPPED_SIGNAL
  if(!retVal){
    abort();
  }
#endif
  return retVal;
}

inline
Uint32
NdbReceiver::getMagicNumberFromObject() const
{
  return theMagicNumber;
}

inline
int
NdbReceiver::execTCOPCONF(Uint32 len){
  const Uint32 tmp = m_received_result_length;
  m_expected_result_length = len;
#ifdef assert
  assert(!(tmp && !len));
#endif
  return ((bool)len ^ (bool)tmp ? 0 : 1);
}

#endif // DOXYGEN_SHOULD_SKIP_INTERNAL
#endif
