/* 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 */

#ifndef AsyncFile_H
#define AsyncFile_H

//===========================================================================
//
// .DESCRIPTION
//    Asynchronous file, All actions are executed concurrently with other 
//    activity of the process. 
//    Because all action are performed in a seperated thread the result of 
//    of a action is send back tru a memory channel. 
//    For the asyncronise notivication of a finished request all the calls
//    have a request as paramater, the user can use the userData pointer
//    to add information it needs when the request is send back.
//      
//
// .TYPICAL USE:
//      Writing or reading data to/from disk concurrently to other activities.
//
//===========================================================================
//=============================================================================
//
// .PUBLIC
//
//=============================================================================
///////////////////////////////////////////////////////////////////////////////
//
// AsyncFile( );
// Description:
//   Initialisation of the class. 
// Parameters:
//      -
///////////////////////////////////////////////////////////////////////////////
//
// ~AsyncFile( );
// Description:
//   Tell the thread to stop and wait for it to return
// Parameters:
//      -
///////////////////////////////////////////////////////////////////////////////
// 
// doStart( );
// Description: 
//   Spawns the new  thread.
// Parameters:
//  Base path of filesystem
//
///////////////////////////////////////////////////////////////////////////////
//
// void execute(Request *request);
// Description:
//   performens the requered action.
// Parameters:
//    request: request to be called when open is finished.
//       action= open|close|read|write|sync
//          if action is open then:
//             par.open.flags= UNIX open flags, see man open
//             par.open.name= name of the file to open
//          if action is read or write then:
//             par.readWrite.buf= user provided buffer to read/write 
//             the data from/to
//             par.readWrite.size= how many bytes must be read/written
//             par.readWrite.offset= absolute offset in file in bytes
// return:
//    return values are stored in the request error field:
//       error= return state of the action, UNIX error see man open/errno
//       userData= is untouched can be used be user.
//      
///////////////////////////////////////////////////////////////////////////////
//
// void reportTo( MemoryChannel<Request> *reportTo );
// Description:
//   set the channel where the file must report the result of the 
//    actions back to.
// Parameters:
//    reportTo: the memory channel to use use MemoryChannelMultipleWriter 
//              if more 
//              than one file uses this channel to report back.
//      
///////////////////////////////////////////////////////////////////////////////

#include <kernel_types.h>
#include "MemoryChannel.hpp"
#include "Filename.hpp"

const int ERR_ReadUnderflow = 1000;

const int WRITECHUNK = 262144;

class AsyncFile;

class Request
{
public:
  enum Action {
    open,
    close,
    closeRemove,
    read,   // Allways leave readv directly after 
            // read because SimblockAsyncFileSystem depends on it
    readv,
    write,// Allways leave writev directly after 
	        // write because SimblockAsyncFileSystem depends on it
    writev,
    writeSync,// Allways leave writevSync directly after 
    // writeSync because SimblockAsyncFileSystem depends on it
    writevSync,
    sync,
    end,
    append,
    rmrf
  };
  Action action;
  union {
    struct {
      Uint32 flags;
    } open;
    struct {
      int numberOfPages;
      struct{
	char *buf;
	size_t size;
	off_t offset;
      } pages[16];
    } readWrite;
    struct {
      const char * buf;
      size_t size;
    } append;
    struct {
      bool directory;
      bool own_directory;
    } rmrf;
  } par;
  int error;
  
  void set(BlockReference userReference, 
	   Uint32 userPointer,
	   Uint16 filePointer);
  BlockReference theUserReference;
  Uint32 theUserPointer;
  Uint16 theFilePointer;
   // Information for open, needed if the first open action fails.
  AsyncFile* file;
  Uint32 theTrace;
};


inline
void 
Request::set(BlockReference userReference, 
	     Uint32 userPointer, Uint16 filePointer) 
{
  theUserReference= userReference;
  theUserPointer= userPointer;
  theFilePointer= filePointer;
}

class AsyncFile
{
public:
  AsyncFile();
  ~AsyncFile();
  
  void reportTo( MemoryChannel<Request> *reportTo );
  
  void execute( Request* request );
  
  void doStart(Uint32 nodeId, const char * fspath, const char * backup_path);
  // its a thread so its always running
  void run();   

  bool isOpen();

  Filename theFileName;
private:
  
  void openReq(Request *request);
  void readReq(Request *request);
  void readvReq(Request *request);
  void writeReq(Request *request);
  void writevReq(Request *request);
  
  void closeReq(Request *request);
  void syncReq(Request *request);
  void removeReq(Request *request);
  void appendReq(Request *request);
  void rmrfReq(Request *request, char * path, bool removePath);
  void endReq();
  
  int readBuffer(char * buf, size_t size, off_t offset);
  int writeBuffer(const char * buf, size_t size, off_t offset,
		  size_t chunk_size = WRITECHUNK);
  
  int extendfile(Request* request);
  void createDirectories();
    
#ifdef NDB_WIN32
  HANDLE hFile;
#else
  int theFd;
#endif
  
  MemoryChannel<Request> *theReportTo;
  MemoryChannel<Request>* theMemoryChannelPtr;
  
  struct NdbThread* theThreadPtr;
  NdbMutex* theStartMutexPtr;
  NdbCondition* theStartConditionPtr;
  bool   theStartFlag;
  int theWriteBufferSize;
  char* theWriteBuffer;
  
  bool m_openedWithSync;
  Uint32 m_syncCount;
  Uint32 m_syncFrequency;
};

#endif