#include        "winplus.h"
#pragma         hdrstop
#include        <io.h>
#include        <sys\stat.h>
#include        <fcntl.h>
#include        <stdio.h>

void COutputBinaryFile::ResetAll()
{
  bLastError=TRUE;
  bAlreadyExists=FALSE;
  lpszFileName=NULL;
  iHandle=-1;
  lpCache=NULL;
  dwCacheSize=0;
  dwCachePos=0;
}

void COutputBinaryFile::ReleaseAll()
{
  if(lpCache) free(lpCache);
}

void COutputBinaryFile::DisplayString(char *lpszString,DWORD dwButtons)
{
  int Response = ::MessageBox(hWnd,lpszString,szAppTitle,
    MB_YESNO|MB_ICONEXCLAMATION);
  if(Response==IDYES) bOverWrite=TRUE; else bOverWrite=FALSE;
}

COutputBinaryFile::COutputBinaryFile(char *lpszFileName,DWORD dwSizeCache)
{
  ResetAll();
  // Проверяем, существует ли уже данный файл
  struct stat st;
  if(::stat(lpszFileName,&st)!=-1)
  {
    // Если он имеет ненулевую длину, то запрашиваем перезапись
    if(st.st_size!=0)
    {
      Report(
        "Файл '%s' уже существует.\n"
        "Перезаписать его ?",lpszFileName);
      if(bOverWrite!=TRUE)
      {
        bAlreadyExists=TRUE;
        return;
      }
    }
    // Удаляем существующий файл
    chmod(lpszFileName,S_IWRITE);
    unlink(lpszFileName);
  }
  this->lpszFileName=lpszFileName;
  dwCacheSize=dwSizeCache;
  // Скорректировать слишком малое | большое значение размера кэша
  if(dwCacheSize<OBF_CASHESIZE)
    dwCacheSize=OBF_CASHESIZE;
  else
    if(dwCacheSize>OBF_MAXCASHESIZE)
      dwCacheSize=OBF_MAXCASHESIZE;
  lpCache=(BYTE*)malloc(dwCacheSize);
  // Если требуемое количество памяти распределить на удалось
  if(lpCache==NULL)
  {
    //  Пробуем распределить хотя бы минимум
    if(dwCacheSize>OBF_CASHESIZE)
    {
      dwCacheSize=OBF_CASHESIZE;
      lpCache=(BYTE*)malloc(dwCacheSize);
      if(lpCache==NULL) return;
    }
    else return;
  }
  // Создаем новый файл нулевой длины
  iHandle=::open(lpszFileName,_O_WRONLY|_O_BINARY|_O_CREAT|_O_TRUNC,
    S_IWRITE);
  if(iHandle==-1) return;
  ::close(iHandle);
  // Если создание прошло успешно то сбрасываем признак ошибки
  bLastError=FALSE;
}

COutputBinaryFile::~COutputBinaryFile()
{
  // Перед уничтожением сбрасываем буфер на диск
  Commit();
  // Полное очищение
  ReleaseAll();
}

void COutputBinaryFile::Commit()
{
  DWORD dwWrote;
  // Если кэш модифицирован то текущая позиция внутри него ненулевая
  if(dwCachePos!=0)
  {
    bLastError=TRUE;
    // Открываем существующий файл для добавления
    iHandle=::open(lpszFileName,_O_WRONLY|_O_BINARY|_O_CREAT|_O_APPEND,
      S_IWRITE);
    // Если открытие невозможно, то код ошибки не сбрасывается
    if(iHandle==-1) return;
    dwWrote=::write(iHandle,lpCache,dwCachePos);
    dwCachePos-=dwWrote;
    ::close(iHandle);
    // Если записано меньше чем требовалось, то код ошибки не сбрасывается
    if(dwCachePos) return;
    bLastError=FALSE;
  }
}

void COutputBinaryFile::write(void *lpBuffer, DWORD dwBufferSize)
{
  bLastError=FALSE;
  // Если запрошенный участок умещается в остаток кэша
  if((dwCachePos+dwBufferSize)<=dwCacheSize)
  {
    // То записываем его в кэш
    memcpy(&lpCache[dwCachePos],lpBuffer,dwBufferSize);
    dwCachePos+=dwBufferSize;
  }
  else
  {
    // Если запрошенный участок по размеру больше кеша
    if(dwBufferSize>=dwCacheSize)
    {
      // Сбрасываем буфер не трогая кэш
      DWORD dwWrote;
      Commit();
      if(bLastError) return;
      iHandle=::open(lpszFileName,_O_WRONLY|_O_BINARY|_O_CREAT|_O_APPEND,
        S_IWRITE);
      if(iHandle==-1)
      {
        bLastError=TRUE;
        return;
      }
      dwWrote=::write(iHandle,lpBuffer,dwBufferSize);
      ::close(iHandle);
      if(dwWrote<dwBufferSize)
      {
        bLastError=TRUE;
        return;
      }
    }
    else
    {
      // Сбрасываем кэш и записываем в него запрошенный буфер
      Commit();
      if(bLastError) return;
      memcpy(lpCache,lpBuffer,dwBufferSize);
      dwCachePos=dwBufferSize;
    }
  }
}


void COutputTextFile::putch(char ch)
{
  write(&ch,1);
}

void COutputTextFile::puts(char *str)
{
  int len=strlen(str);
  write(str,len);
  if((str[len-1] != 0x0D)&&(str[len-1] != 0x0A)) putch(0x0A);
}

void COutputTextFile::printf(const char *fmt, ...)
{
  char *ptr = lpszBuffer;
  va_list marker;
  va_start(marker, fmt);
  vsprintf(lpszBuffer, fmt, marker);
  va_end(marker);
  write(ptr,strlen(ptr));
}
