/*
   Copyright (c) 2003, 2011, 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 TRIX_H
#define TRIX_H

#include <SimulatedBlock.hpp>
#include <trigger_definitions.h>
#include <DataBuffer.hpp>
#include <SimpleProperties.hpp>
#include <signaldata/DictTabInfo.hpp>
#include <signaldata/CreateTrig.hpp>
#include <signaldata/BuildIndx.hpp>
#include <signaldata/IndexStatSignal.hpp>
#include <signaldata/GetTabInfo.hpp>
#include <signaldata/TuxBound.hpp>
#define ZNOT_FOUND 626

// Error codes
#define INTERNAL_ERROR_ILLEGAL_CALL 4344
#define INTERNAL_ERROR_TRIX_BUSY 4345

/**
 * TRIX - This block manages triggers and index (in coop with DICT)
 */
class Trix : public SimulatedBlock
{
public:
  Trix(Block_context&);
  virtual ~Trix();

public:
  // Subscription data, when communicating with SUMA

  enum RequestType {
    REORG_COPY = 0
    ,REORG_DELETE = 1
    ,INDEX_BUILD = 2
    ,STAT_UTIL = 3  // PK op of HEAD table directly via DBUTIL
    ,STAT_CLEAN = 4
    ,STAT_SCAN = 5
    //ALTER_TABLE
  };
  typedef DataBuffer<11> AttrOrderBuffer;

private:
  // Private attributes
  
  BLOCK_DEFINES(Trix);

  // Declared but not defined
  //DBtrix(const Trix &obj);
  //void operator = (const Trix &);

  // Block state
  enum BlockState {
    NOT_STARTED,
    STARTED,
    NODE_FAILURE,
    IDLE,
    BUSY
  };

  BlockState c_blockState;

  // Node data needed when communicating with remote TRIX:es
  struct NodeRecord {
    NodeRecord() {}
    bool alive;
    BlockReference trixRef;
    union {
      Uint32 nextPool;
      Uint32 nextList;
    };
    Uint32 prevList;
  };
  
  typedef Ptr<NodeRecord> NodeRecPtr;

  /**
   * The pool of node records
   */
  ArrayPool<NodeRecord> c_theNodeRecPool;

  /**
   * The list of other NDB nodes
   */  
  DLList<NodeRecord> c_theNodes;

  Uint32 c_masterNodeId;
  BlockReference c_masterTrixRef;
  Uint16 c_noNodesFailed;
  Uint16 c_noActiveNodes;

  AttrOrderBuffer::DataBufferPool c_theAttrOrderBufferPool;

  struct SubscriptionRecord {
    SubscriptionRecord(AttrOrderBuffer::DataBufferPool & aop):
      attributeOrder(aop)
    {}
    enum RequestFlags {
      RF_WAIT_GCP = 0x1
      ,RF_NO_DISK = 0x2
      ,RF_TUP_ORDER = 0x4
    };
    Uint32 m_flags;
    RequestType requestType;
    BlockReference userReference; // For user
    Uint32 connectionPtr; // For user
    Uint32 subscriptionId; // For Suma
    Uint32 schemaTransId;
    Uint32 subscriptionKey; // For Suma
    Uint32 prepareId; // For DbUtil
    Uint32 indexType;
    Uint32 sourceTableId;
    Uint32 targetTableId;
    AttrOrderBuffer attributeOrder;
    Uint32 noOfIndexColumns;
    Uint32 noOfKeyColumns;
    Uint32 parallelism;
    Uint32 fragCount;
    Uint32 fragId;
    Uint32 syncPtr;
    BuildIndxRef::ErrorCode errorCode;
    bool subscriptionCreated;
    bool pendingSubSyncContinueConf;
    Uint32 expectedConf; // Count in n UTIL_EXECUTE_CONF + 1 SUB_SYNC_CONF
    Uint64 m_rows_processed;
    Uint64 m_gci;
    Uint32 m_statPtrI;
    union {
      Uint32 nextPool;
      Uint32 nextList;
    };
    Uint32 prevList;
  };
  
  typedef Ptr<SubscriptionRecord> SubscriptionRecPtr;

  /**
   * The pool of node records
   */
  ArrayPool<SubscriptionRecord> c_theSubscriptionRecPool;
  RSS_AP_SNAPSHOT(c_theSubscriptionRecPool);

  /**
   * The list of other subscriptions
   */  
  DLList<SubscriptionRecord> c_theSubscriptions;

  /*
   * Ordered index stats.  Implements sub-ops of DBDICT index stat
   * schema op.  Each sub-op is a simple REQ which seizes and releases
   * a stat op here before returning CONF or REF.  A stat op always has
   * an associated SubscriptionRecord.  It is used for SUMA index scans
   * and as proxy for PK ops to DBUTIL.
   */

  bool c_statGetMetaDone;
  struct SysColumn {
    Uint32 pos;
    const char* name;
    bool keyFlag;
  };
  struct SysTable {
    const char* name;
    mutable Uint32 tableId;
    const Uint32 columnCount;
    const SysColumn* columnList;
  };
  struct SysIndex {
    const char* name;
    mutable Uint32 tableId;
    mutable Uint32 indexId;
  };
  static const SysColumn g_statMetaHead_column[];
  static const SysColumn g_statMetaSample_column[];
  static const SysTable g_statMetaHead;
  static const SysTable g_statMetaSample;
  static const SysIndex g_statMetaSampleX1;

  struct StatOp {
    struct Meta {
      GetTabInfoConf m_conf;
      Callback m_cb;
    };
    struct Data {
      Int32 m_head_found;
      Uint32 m_indexId;
      Uint32 m_indexVersion;
      Uint32 m_tableId;
      Uint32 m_fragCount;
      Uint32 m_valueFormat;
      Uint32 m_sampleVersion;
      Uint32 m_loadTime;
      Uint32 m_sampleCount;
      Uint32 m_keyBytes;
      Uint32* m_statKey;
      Uint32* m_statValue;
      Data() {
        m_head_found = -1;
        m_sampleVersion = 0;
      }
    };
    struct Attr {
      Uint32* m_attr;
      Uint32 m_attrMax;
      Uint32 m_attrSize;
      Uint32* m_data;
      Uint32 m_dataMax;
      Uint32 m_dataSize;
      Attr() {}
    };
    struct Util {
      Uint32 m_prepareId;
      bool m_not_found;
      Callback m_cb;
      Util() {
        m_prepareId = RNIL;
        m_not_found = false; // read + ZNOT_FOUND
      };
    };
    struct Clean {
      Uint32 m_cleanCount;
      // bounds on index_id, index_version, sample_version
      Uint32 m_bound[3 * 3];
      Uint32 m_boundCount;
      Uint32 m_boundSize;
      Clean() {}
    };
    struct Scan {
      Uint32 m_sampleCount;
      Uint32 m_keyBytes;
      Scan() {}
    };
    struct Drop {
    };
    struct Send {
      const SysTable* m_sysTable;
      Uint32 m_operationType;     // UtilPrepareReq::OperationTypeValue
      Uint32 m_prepareId;
      Send() {}
    };
    IndexStatImplReq m_req;
    Uint32 m_requestType;
    const char* m_requestName;
    Uint32 m_subRecPtrI;
    Meta m_meta;
    Data m_data;
    Attr m_attr;
    Util m_util;
    Clean m_clean;
    Scan m_scan;
    Drop m_drop;
    Send m_send;
    Uint32 m_errorCode;
    Uint32 m_errorLine;
    union {
      Uint32 m_ownPtrI;
      Uint32 nextPool;
    };
    StatOp() {
      m_subRecPtrI = RNIL;
      m_errorCode = 0;
      m_errorLine = 0;
    };
  };
  typedef Ptr<StatOp> StatOpPtr;
  ArrayPool<StatOp> c_statOpPool;
  RSS_AP_SNAPSHOT(c_statOpPool);

  // System start
  void execREAD_CONFIG_REQ(Signal* signal);
  void execSTTOR(Signal* signal);
  void execNDB_STTOR(Signal* signal);

  // Node management
  void execREAD_NODESCONF(Signal* signal);
  void execREAD_NODESREF(Signal* signal);
  void execNODE_FAILREP(Signal* signal);
  void execINCL_NODEREQ(Signal* signal);
  // Debugging
  void execDUMP_STATE_ORD(Signal* signal);

  void execDBINFO_SCANREQ(Signal* signal);

  // Build index
  void execBUILD_INDX_IMPL_REQ(Signal* signal);
  void execBUILD_INDX_IMPL_CONF(Signal* signal);
  void execBUILD_INDX_IMPL_REF(Signal* signal);

  void execCOPY_DATA_IMPL_REQ(Signal* signal);

  void execUTIL_PREPARE_CONF(Signal* signal);
  void execUTIL_PREPARE_REF(Signal* signal);
  void execUTIL_EXECUTE_CONF(Signal* signal);
  void execUTIL_EXECUTE_REF(Signal* signal);
  void execUTIL_RELEASE_CONF(Signal* signal);
  void execUTIL_RELEASE_REF(Signal* signal);

  // Suma signals
  void execSUB_CREATE_CONF(Signal* signal);
  void execSUB_CREATE_REF(Signal* signal);
  void execSUB_REMOVE_CONF(Signal* signal);
  void execSUB_REMOVE_REF(Signal* signal);
  void execSUB_SYNC_CONF(Signal* signal);
  void execSUB_SYNC_REF(Signal* signal);
  void execSUB_SYNC_CONTINUE_REQ(Signal* signal);
  void execSUB_TABLE_DATA(Signal* signal);

  // GCP
  void execWAIT_GCP_REF(Signal*);
  void execWAIT_GCP_CONF(Signal*);

  // Utility functions
  void setupSubscription(Signal* signal, SubscriptionRecPtr subRecPtr);
  void startTableScan(Signal* signal, SubscriptionRecPtr subRecPtr);
  void prepareInsertTransactions(Signal* signal, SubscriptionRecPtr subRecPtr);
  void executeBuildInsertTransaction(Signal* signal, SubscriptionRecPtr);
  void executeReorgTransaction(Signal*, SubscriptionRecPtr, Uint32);
  void buildComplete(Signal* signal, SubscriptionRecPtr subRecPtr);
  void wait_gcp(Signal*, SubscriptionRecPtr subRecPtr, Uint32 delay = 0);
  void buildFailed(Signal* signal, 
		   SubscriptionRecPtr subRecPtr, 
		   BuildIndxRef::ErrorCode);
  void checkParallelism(Signal* signal, SubscriptionRecord* subRec);

  // index stats
  StatOp& statOpGetPtr(Uint32 statPtrI);
  bool statOpSeize(Uint32& statPtrI);
  void statOpRelease(StatOp&);
  void execINDEX_STAT_IMPL_REQ(Signal*);
  // sys tables metadata
  void statMetaGetHead(Signal*, StatOp&);
  void statMetaGetHeadCB(Signal*, Uint32 statPtrI, Uint32 ret);
  void statMetaGetSample(Signal*, StatOp&);
  void statMetaGetSampleCB(Signal*, Uint32 statPtrI, Uint32 ret);
  void statMetaGetSampleX1(Signal*, StatOp&);
  void statMetaGetSampleX1CB(Signal*, Uint32 statPtrI, Uint32 ret);
  void sendGetTabInfoReq(Signal*, StatOp&, const char* name);
  void execGET_TABINFO_CONF(Signal*);
  void execGET_TABINFO_REF(Signal*);
  // continue
  void statGetMetaDone(Signal*, StatOp&);
  // head table ops
  void statHeadRead(Signal*, StatOp&);
  void statHeadReadCB(Signal*, Uint32 statPtrI, Uint32 ret);
  void statHeadInsert(Signal*, StatOp&);
  void statHeadInsertCB(Signal*, Uint32 statPtrI, Uint32 ret);
  void statHeadUpdate(Signal*, StatOp&);
  void statHeadUpdateCB(Signal*, Uint32 statPtrI, Uint32 ret);
  void statHeadDelete(Signal*, StatOp&);
  void statHeadDeleteCB(Signal*, Uint32 statPtrI, Uint32 ret);
  // util
  void statUtilPrepare(Signal*, StatOp&);
  void statUtilPrepareConf(Signal*, Uint32 statPtrI);
  void statUtilPrepareRef(Signal*, Uint32 statPtrI);
  void statUtilExecute(Signal*, StatOp&);
  void statUtilExecuteConf(Signal*, Uint32 statPtrI);
  void statUtilExecuteRef(Signal*, Uint32 statPtrI);
  void statUtilRelease(Signal*, StatOp&);
  void statUtilReleaseConf(Signal*, Uint32 statPtrI);
  // continue
  void statReadHeadDone(Signal*, StatOp&);
  void statInsertHeadDone(Signal*, StatOp&);
  void statUpdateHeadDone(Signal*, StatOp&);
  void statDeleteHeadDone(Signal*, StatOp&);
  // clean
  void statCleanBegin(Signal*, StatOp&);
  void statCleanPrepare(Signal*, StatOp&);
  void statCleanExecute(Signal*, StatOp&);
  void statCleanRelease(Signal*, StatOp&);
  void statCleanEnd(Signal*, StatOp&);
  // scan
  void statScanBegin(Signal*, StatOp&);
  void statScanPrepare(Signal*, StatOp&);
  void statScanExecute(Signal*, StatOp&);
  void statScanRelease(Signal*, StatOp&);
  void statScanEnd(Signal*, StatOp&);
  // drop
  void statDropBegin(Signal*, StatOp&);
  void statDropEnd(Signal*, StatOp&);
  // send
  void statSendPrepare(Signal*, StatOp&);
  void statSendExecute(Signal*, StatOp&);
  void statSendRelease(Signal*, StatOp&);
  // data
  void statDataPtr(StatOp&, Uint32 i, Uint32*& dptr, Uint32& bytes);
  void statDataOut(StatOp&, Uint32 i);
  void statDataIn(StatOp&, Uint32 i);
  // abort ongoing
  void statAbortUtil(Signal*, StatOp&);
  void statAbortUtilCB(Signal*, Uint32 statPtrI, Uint32 ret);
  // conf and ref
  void statOpSuccess(Signal*, StatOp&);
  void statOpConf(Signal*, StatOp&);
  void statOpError(Signal*, StatOp&, Uint32 errorCode, Uint32 errorLine,
                   const Uint32 * supress = 0);
  void statOpAbort(Signal*, StatOp&);
  void statOpRef(Signal*, StatOp&);
  void statOpRef(Signal*, const IndexStatImplReq*, Uint32 errorCode, Uint32 errorLine);
  void statOpEvent(StatOp&, const char* level, const char* msg, ...);
  // debug
  friend class NdbOut& operator<<(NdbOut&, const StatOp& stat);
};

#endif
