

/* 
A* -------------------------------------------------------------------
B* This file contains source code for the PyMOL computer program
C* Copyright (c) Schrodinger, LLC. 
D* -------------------------------------------------------------------
E* It is unlawful to modify or remove this copyright notice.
F* -------------------------------------------------------------------
G* Please see the accompanying LICENSE file for further information. 
H* -------------------------------------------------------------------
I* Additional authors of this source file include:
-* 
-* 
-*
Z* -------------------------------------------------------------------
*/

#include"os_predef.h"
#include"os_std.h"

#include"Raw.h"
#include"Version.h"
#include"Base.h"
#include"Feedback.h"
#include"MemoryDebug.h"
#include"OOMac.h"

#define cRaw_file_stream 0

#define cRaw_header_size 16

#define swap_bytes(a) { \
char u; \
u=*(((char*)(a))); \
*(((char*)(a)))=*(((char*)(a))+3); \
*(((char*)(a))+3)=u; \
u=*(((char*)(a))+1); \
*(((char*)(a))+1)=*(((char*)(a))+2); \
*(((char*)(a))+2)=u; }

CRaw *RawOpenRead(PyMOLGlobals * G, char *fname)
{
  int target = 0x04030201;
  int reverse = 0x01020304;
  int actual;
  int ok = true;

  OOAlloc(G, CRaw);
  I->bufVLA = NULL;
  I->G = G;
  I->f = fopen(fname, "rb");
  if(!I->f) {
    ok = false;
  } else {
    if(feof(I->f))
      ok = false;
    else if(fread(&actual, 4, 1, I->f) != 1)
      ok = false;
    else if(actual == target)
      I->swap = false;
    else if(actual == reverse)
      I->swap = true;
    else {
      PRINTFB(G, FB_Raw, FB_Errors)
        "Error-RawOpenRead: Unrecognized byte ordering. This may not a PyMOL file.\n"
        ENDFB(G);
      ok = false;
    }
  }
  if(!ok) {
    if(I->f)
      fclose(I->f);
    OOFreeP(I);
    PRINTFB(G, FB_Raw, FB_Errors)
      "Error-RawOpenRead: Unable to open '%s'.\n", fname ENDFB(G);

  } else {
    I->mode = cRaw_file_stream;
  }
  return (I);
}

CRaw *RawOpenWrite(PyMOLGlobals * G, char *fname)
{
  int target = 0x04030201;
  int ok = true;
  OOAlloc(G, CRaw);
  I->bufVLA = NULL;
  I->G = G;
  I->f = fopen(fname, "wb");
  if(!I->f) {
    ok = false;
  } else {
    fwrite(&target, 4, 1, I->f);
  }
  if(!ok) {
    if(I->f)
      fclose(I->f);
    OOFreeP(I);
  } else {
    I->mode = cRaw_file_stream;
  }
  return (I);
}

CRaw *RawOpenAppend(PyMOLGlobals * G, char *fname)
{
  int target = 0x04030201;
  int ok = true;
  OOAlloc(G, CRaw);
  I->bufVLA = NULL;
  I->G = G;
  I->f = fopen(fname, "wba");
  if(!I->f) {
    ok = false;
  } else {
    if(!ftell(I->f))            /* write magic if this is a new file */
      fwrite(&target, 4, 1, I->f);
  }
  if(!ok) {
    if(I->f)
      fclose(I->f);
    OOFreeP(I);
    PRINTFB(G, FB_Raw, FB_Errors)
      "Error-RawOpenAppend: Unable to open '%s'.\n", fname ENDFB(G);
  } else {
    I->mode = cRaw_file_stream;
  }
  return (I);
}

void RawFree(CRaw * I)
{
  switch (I->mode) {
  case cRaw_file_stream:
    if(I->f) {
      fclose(I->f);
      I->f = NULL;
    }
    break;
  }
  VLAFreeP(I->bufVLA);
  OOFreeP(I);
}

int RawGetNext(CRaw * I, int *size, int *version)
{
  PyMOLGlobals *G = I->G;
  int result = cRaw_EOF;
  switch (I->mode) {
  case cRaw_file_stream:
    if(I->f) {
      if(!feof(I->f)) {
        if(fread((char *) I->header, cRaw_header_size, 1, I->f) != 1) {
          PRINTFD(G, FB_Raw)
            " RawGetNextType-Debug: Couldn't read header.\n" ENDFD;
        } else {
          if(I->swap) {
            swap_bytes(I->header);
            swap_bytes(I->header + 1);
            swap_bytes(I->header + 2);
            swap_bytes(I->header + 3);
          }
          fseek(I->f, -cRaw_header_size, SEEK_CUR);
          *size = I->header[0];
          result = I->header[1];
          *version = I->header[2];
        }
      }
    }
  }
  return (result);
}

int RawReadSkip(CRaw * I)
{
  PyMOLGlobals *G = I->G;
  int result = false;
  switch (I->mode) {
  case cRaw_file_stream:
    if(I->f) {
      if(!feof(I->f)) {
        if(fread((char *) I->header, cRaw_header_size, 1, I->f) != 1) {
          PRINTFB(G, FB_Raw, FB_Errors)
            "Error-Raw: Error reading header.\n" ENDFB(G);
        } else {
          if(I->swap) {
            swap_bytes(I->header);
            swap_bytes(I->header + 1);
            swap_bytes(I->header + 2);
            swap_bytes(I->header + 3);
          }
          fseek(I->f, I->header[0], SEEK_CUR);
          result = true;
        }
      }
    }
    break;
  }
  return (result);
}

char *RawRead(CRaw * I, int *type, unsigned int *size, int *serial)
{
  PyMOLGlobals *G = I->G;
  char *result = NULL;
  switch (I->mode) {
  case cRaw_file_stream:
    if(I->f) {
      if(!feof(I->f)) {
        if(fread((char *) I->header, cRaw_header_size, 1, I->f) != 1) {
          PRINTFB(G, FB_Raw, FB_Errors)
            "Error-Raw: Error reading header.\n" ENDFB(G);
        } else {
          if(I->swap) {
            swap_bytes(I->header);
            swap_bytes(I->header + 1);
            swap_bytes(I->header + 2);
            swap_bytes(I->header + 3);
          }
          VLACheck(I->bufVLA, char, I->header[0]);
          if(fread(I->bufVLA, I->header[0], 1, I->f) == 1) {
            result = I->bufVLA;
            *size = I->header[0];
            *type = I->header[1];       /* record type */
            *serial = I->header[3];
          } else {
            PRINTFB(G, FB_Raw, FB_Errors)
              "Error-RawRead: Data read error.\n" ENDFB(G);

          }
        }
      } else {
        *type = cRaw_EOF;
      }
    }
    break;
  }
  return (result);
}

char *RawReadPtr(CRaw * I, int type, int *size)
{
  PyMOLGlobals *G = I->G;
  char *result = NULL;
  switch (I->mode) {
  case cRaw_file_stream:
    if(I->f) {
      if(!feof(I->f)) {
        if(fread((char *) I->header, cRaw_header_size, 1, I->f) != 1) {
          PRINTFB(G, FB_Raw, FB_Errors)
            "Error-Raw: Error reading header.\n" ENDFB(G);
        } else {
          if(I->swap) {
            swap_bytes(I->header);
            swap_bytes(I->header + 1);
            swap_bytes(I->header + 2);
            swap_bytes(I->header + 3);
          }
          if((I->header[1]) != type) {
            fseek(I->f, -cRaw_header_size, SEEK_CUR);
            PRINTFD(G, FB_Raw)
              " RawReadPtr-Debug: Type mismatch.\n" ENDFD;
          } else {
            result = (char*) mmalloc(I->header[0]);
            if(fread(result, I->header[0], 1, I->f) != 1) {
              FreeP(result);
              PRINTFB(G, FB_Raw, FB_Errors)
                "Error-RawReadVLA: Data read error.\n" ENDFB(G);

            } else {
              *size = I->header[0];
            }
          }
        }
      }
    }
    break;
  }
  return (result);
}

char *RawReadVLA(CRaw * I, int type, unsigned int rec_size, int grow_factor,
                 int auto_zero)
{
  PyMOLGlobals *G = I->G;
  char *result = NULL;
  switch (I->mode) {
  case cRaw_file_stream:
    if(I->f) {
      if(!feof(I->f)) {
        if(fread((char *) I->header, cRaw_header_size, 1, I->f) != 1) {
          PRINTFB(G, FB_Raw, FB_Errors)
            "Error-Raw: Error reading header.\n" ENDFB(G);
        } else {
          if(I->swap) {
            swap_bytes(I->header);
            swap_bytes(I->header + 1);
            swap_bytes(I->header + 2);
            swap_bytes(I->header + 3);
          }
          if((I->header[1]) != type) {
            fseek(I->f, -cRaw_header_size, SEEK_CUR);
            PRINTFD(G, FB_Raw)
              " RawReadVLA-Debug: Type mismatch %d != %d.\n", I->header[1], type ENDFD;

          } else {
            result = (char*)
              VLAMalloc((I->header[0] / rec_size), rec_size, grow_factor, auto_zero);
            if(fread(result, I->header[0], 1, I->f) != 1) {
              VLAFreeP(result);
              PRINTFB(G, FB_Raw, FB_Errors)
                "Error-RawReadVLA: Data read error.\n" ENDFB(G);

            } else {
              result = (char *) VLASetSize(result, I->header[0] / rec_size);
            }
          }
        }
      }
    }
    break;
  }
  return (result);
}

int RawReadInto(CRaw * I, int type, unsigned int size, char *buffer)
{
  PyMOLGlobals *G = I->G;
  int ok = false;
  switch (I->mode) {
  case cRaw_file_stream:
    if(I->f) {
      if(!feof(I->f)) {
        if(fread((char *) I->header, cRaw_header_size, 1, I->f) != 1) {
          PRINTFB(G, FB_Raw, FB_Errors)
            "Error-RawReadInfo: Error reading header.\n" ENDFB(G);
        } else {
          if(I->swap) {
            swap_bytes(I->header);
            swap_bytes(I->header + 1);
            swap_bytes(I->header + 2);
            swap_bytes(I->header + 3);
          }
          if((I->header[1]) != type) {
            fseek(I->f, -cRaw_header_size, SEEK_CUR);
            PRINTFD(G, FB_Raw)
              " RawReadPtr-Debug: Type mismatch.\n" ENDFD;
          } else if(I->header[0] != (signed) size) {
            PRINTFB(G, FB_Raw, FB_Errors)
              "Error-RawReadInfo: Size mismatch %d!=%d (disk/RAM).\n", I->header[0], size
              ENDFB(G);
          } else {
            if(fread(buffer, size, 1, I->f) != 1) {
              PRINTFB(G, FB_Raw, FB_Errors)
                "Error-RawReadInfo: Data read error.\n" ENDFB(G);
            } else {
              ok = true;
            }
          }
        }
      }
    }
    break;
  }
  return (ok);
}

int RawWrite(CRaw * I, int type, unsigned int size, int serial, char *bytes)
{
  PyMOLGlobals *G = I->G;
  int header[4];
  int ok = false;
  PRINTFD(G, FB_Raw)
    " RawWrite-Debug: type %d size %d %p\n", type, size, bytes ENDFD;
  switch (I->mode) {
  case cRaw_file_stream:
    if(I->f) {
      header[0] = size;
      header[1] = type;
      header[2] = _PyMOL_VERSION_int;
      header[3] = serial;
      if(fwrite((char *) header, cRaw_header_size, 1, I->f) != 1) {
        PRINTFB(G, FB_Raw, FB_Errors)
          "Error-RawWrite: can't write header.\n" ENDFB(G);
      } else if(fwrite((char *) bytes, size, 1, I->f) != 1) {
        PRINTFB(G, FB_Raw, FB_Errors)
          "Error-RawWrite: can't write data.\n" ENDFB(G);
      } else {
        ok = true;
      }
    }
  }
  PRINTFD(G, FB_Raw)
    " RawWrite-Debug: leaving... %d\n", ok ENDFD;

  return (ok);
}
