/********************************************************************

Module: TiInstaller

Author: Vince Harron

Description:

Routines for mirroring file system directory trees.

Copyright 2006 Sony Online Entertainment.  All rights reserved.

*********************************************************************/

#include "TiInstaller.h"
#include "SyDebug.h"
#include "SyFile.h"
#include "SyDirectory.h"
#include "SyNew.h"
#include "SyStr.h"
#include "SyMath.h"
#include "SyTime.h"

int TiInstaller::CopyFile( const char* srcPath, const char* dstPath )
{
  SyOutputString( "copy: %s->%s\n", srcPath, dstPath );
  SyFile src;
  SyFile dst;

  if ( src.Open( srcPath, SYFILE_RONLY | SYFILE_BINARY ) < 0 )
  {
    SyOutputString( "open failed: %s\n", srcPath );
    return -1;
  }

  if ( src.Seek( 0, SyFile::End ) < 0 )
  {
    SyOutputString( "seek failed: %s\n", srcPath );
    return -1;
  }

  int64 size = src.Tell();
  if ( size < 0 )
  {
    SyOutputString( "tell failed: %s\n", srcPath );
    return -1;
  }

  if ( src.Seek( 0, SyFile::Set ) < 0 )
  {
    SyOutputString( "seek failed: %s\n", srcPath );
    return -1;
  }

  if ( dst.Open( dstPath, SYFILE_WONLY | SYFILE_CREATE | SYFILE_BINARY ) < 0 )
  {
    SyOutputString( "open failed: %s\n", srcPath );
    return -1;
  }

  int bufferLen = 1024*1024;
  void* temp = malloc(bufferLen);
  int64 totalWritten = 0;
  while ( totalWritten < size )
  {
    int chunkSize = SY_MIN( size-totalWritten, bufferLen );
    if ( src.Read( temp, chunkSize ) < 0 )
    {
      SyOutputString( "read failed: %s\n", srcPath );
      free(temp);
      return -1;
    }

    if ( dst.Write( temp, chunkSize ) )
    {
      SyOutputString( "write failed: %s\n", dstPath );
      free(temp);
      return -1;
    }
    totalWritten += chunkSize;
  }
  free(temp);
  return 0;
}

int TiInstaller::CopyTree( const char* srcPath, const char* dstPath )
{
  int result;

  SyDirectory::FindFile findFile;

  if ( findFile.Open( srcPath ) < 0 )
  {
    SyOutputString( "invalid directory %s\n", srcPath );
    return -1;
  }

  while (findFile.GetFile())
  {
    if (
      strcmp(".",findFile.GetFilename())==0 ||
      strcmp("..",findFile.GetFilename())==0
      )
    {
      continue;
    }
    const char* srcFile = findFile.GetFullPath();
    char dstFile[1024];
    sprintf( dstFile, "%s/%s", dstPath, findFile.GetFilename() );

    switch ( findFile.GetType() )
    {
    case SyDirectory::DIRECTORY:
      // create dstFile if it doesn't exist already
      if ( (result=SyDirectory::Mkdir( dstFile )) < 0 )
      {
        SyOutputString( "Mkdir failed: %s (%i)\n", dstFile, result );
        //return -1;
      }
      if ( CopyTree( srcFile, dstFile ) < 0 )
      {
        return -1;
      }
      break;
    case SyDirectory::REGULAR:
      if ( CopyFile( srcFile, dstFile ) < 0 )
      {
        return -1;
      }
      break;
    default: break; // suppress warning
    }
  }

  return 0;
}

int TiInstaller::ListTree( const char* directory )
{
  SyDirectory::FindFile findFile;

  if ( findFile.Open( directory ) < 0 )
  {
    SyOutputString( "invalid directory %s\n", directory );
    return -1;
  }

  while (findFile.GetFile())
  {
    if (
      strcmp(".",findFile.GetFilename())==0 ||
      strcmp("..",findFile.GetFilename())==0
      )
    {
      continue;
    }
    const char* fullpath = findFile.GetFullPath();

    int64 size;
    size = SyFile::GetFileSize(fullpath);
    SyDebug( "list: %s (%i bytes)\n", fullpath, size );

    if ( findFile.GetType() == SyDirectory::DIRECTORY )
    {
      if ( ListTree( fullpath ) < 0 )
      {
        return -1;
      }
    }
  }

  return 0;
}

int TiInstaller::DeleteTree( const char* directory )
{
  int result;

  SyDirectory::FindFile findFile;

  if ( findFile.Open( directory ) < 0 )
  {
    SyOutputString( "invalid directory %s\n", directory );
    return -1;
  }

  while (findFile.GetFile())
  {
    if (
      strcmp(".",findFile.GetFilename())==0 ||
      strcmp("..",findFile.GetFilename())==0
      )
    {
      continue;
    }
    const char* fullpath = findFile.GetFullPath();

    SyOutputString( "delete: %s\n", fullpath );

    switch ( findFile.GetType() )
    {
    case SyDirectory::DIRECTORY:
      if ( DeleteTree( fullpath ) < 0 )
      {
        return -1;
      }
      if ( (result=SyDirectory::Rmdir( fullpath )) < 0 )
      {
        SyOutputString( "Rmdir failed: %s (%i)\n", fullpath, result );
        //return -1;
      }
      break;
    case SyDirectory::REGULAR:
      if ( (result=SyDirectory::Unlink( fullpath )) < 0 )
      {
        SyOutputString( "Unlink failed: %s (%i)\n", fullpath, result );
        return -1;
      }
      break;
    default: break; // suppress warning
    }
  }

  return 0;
}

int TiInstaller::Install( const char* srcPath, const char* dstPath )
{
  if ( !SyDirectory::IsMounted( dstPath ) )
  {
    SyOutputString( "%s is not mounted, install failed", dstPath );
    return -1;
  }

  SyTime start;
  SyTime end;
#if 0
  start = SyTimer::GetTime();
  SyOutputString( "deleting files from %s\n", dstPath );
  if ( DeleteTree( dstPath ) < 0 )
  {
    SyOutputString( "install: delete failed\n" );
    return -1;
  }
  end = SyTimer::GetTime();

  SyOutputString( "delete time: %f minutes\n", ((float)(end-start))/60000.0f );
#endif

  start = SyTimer::GetTime();
  SyOutputString( "installing to %s\n", dstPath );
  if ( CopyTree( srcPath, dstPath ) < 0 )
  {
    SyOutputString( "install: copy failed\n" );
    return -1;
  }
  end = SyTimer::GetTime();

  SyOutputString( "install time: %f minutes\n", ((float)(end-start))/60000.0f );

  return 0;
}
