/*
* Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  
*
*  Copyright © 2007-2008 Nokia.  All rights reserved.
*  This material, including documentation and any related computer
*  programs, is protected by copyright controlled by Nokia.  All
*  rights are reserved.  Copying, including reproducing, storing,
*  adapting or translating, any or all of this material requires the
*  prior written consent of Nokia.  This material also contains
*  confidential information which may not be disclosed to others
*  without the prior written consent of Nokia.

*
*
*/


// ===========================================================================
// Included headers


#include <bldvariant.hrh> // for feature definitions

#include <f32file.h>
#include <centralrepository.h>
#include <pathinfo.h>
#include <StringLoader.h>

#include <cameraapp.rsg>
#include <vgacamsettings.rsg>

#include "CamPanic.h"
#include "CamUtility.h"       // PRINT macros
#include "CameraappPrivateCRKeys.h"  
#include "camfolderutility.h"


// ===========================================================================
// Local constants

static const TInt KMultipleFolderNumberChars = 2;
static const TInt KMaxMonthFolders           = 260;
static const TInt KBase10                    = 10;
static const TInt KCamMonthFolderNameLength = 8;


_LIT( KCamMonthFolderFormat, "%F%Y%M" );  
_LIT( KBackslash,            "\\"     );
_LIT( KCharacterOffset,      "A"      );
_LIT( KDigitOffset,          "0"      );
_LIT( KMaxCharacter,         "Z"      );
_LIT( KWildCardCharacter,    "?"      );



// ===========================================================================
// CCamFolderUtility implementation

// ---------------------------------------------------------------------------
// GetBasePathL
// Generates the path where new images/videos are to be saved to.
// ---------------------------------------------------------------------------
//
void
CCamFolderUtility::GetBasePathL( TInt*                 aMonthCounters,
                                 TInt                  aStorage, // TCamMediaStorage
                                 TDes&                 aPath, 
                                 TCamCameraMode aCaptureMode, 
                                 TBool                 aCreateAll,
                                 TInt                  aRequiredFileCount,
                                 TTime                 aTime )
  {
  PRINT( _L("Camera => CCamFolderUtility::GetBasePathL"))

  TInt folderTypeIndex = 0;
  
    if ( ( aStorage == ECamMediaStoragePhone ) ) // Saving to phone memory
       { 
       // phone folders are in the odd indexes
       folderTypeIndex ++;
       PRINT( _L("Camera GetBasePathL saving to phone memory"))
       aPath.Copy( PathInfo::PhoneMemoryRootPath() );
       }
       // with multiple drives, mass storage is the default, like phone memory used to be.
    else if ( aStorage == ECamMediaStorageMassStorage ) // Saving to mass storage memory
       {
       PRINT( _L("Camera GetBasePathL saving to mass storage memory"))
       // Get the root path of the mass storage drive.
       TInt drive;
       TInt err = DriveInfo::GetDefaultDrive( DriveInfo::EDefaultMassStorage, drive );
       TFileName path;
       err = PathInfo::GetRootPath( path, drive );
       aPath.Copy(path);
       }
    else  // Saving to MMC
       {
       PRINT( _L("Camera GetBasePathL saving to memory card"))
       // Get the root path of the mmc.
       TInt drive;
       TInt err = DriveInfo::GetDefaultDrive( DriveInfo::EDefaultRemovableMassStorage, drive );
       TFileName path;
       err = PathInfo::GetRootPath( path, drive );
       aPath.Copy(path);
       }

     // Append the folder to the path
  if ( ECamControllerVideo == aCaptureMode )
      {
      aPath.Append( PathInfo::VideosPath() );
      // video folders are offset to ECamFolderTypeVideoMMC
      folderTypeIndex += ECamFolderTypeVideoMMC;
      }
  else 
      {
      aPath.Append( PathInfo::ImagesPath() );  
      }             
     
  // Add a folder for the current month
  // Use the specified time to determine the year and month.
  // If this is 0 then use the current time.
  TTime now = aTime;
  if ( now.Int64() == TInt64( 0 ) )
      {
      now.HomeTime();
      }
  TBuf<KCamMonthFolderNameLength> monthFolder;
  now.FormatL( monthFolder, KCamMonthFolderFormat );
  // If the month folder name is different to the last used month folder name
  // this indicates that a new month has been started. All the counters will
  // need to be reinitialised
  
  // Get last used folder name
  TBuf<KCamMonthFolderNameLength> previousMonthFolder;
  CRepository* cr = CRepository::NewLC( KCRUidCameraappSettings );
  cr->Get( KCamCrLastUsedMonthFolder, previousMonthFolder );
  
  // Compare to current folder name, if different then reset all counters
  if ( monthFolder.Compare( previousMonthFolder) != 0 )
      {
      ResetCounters( aMonthCounters );
      // Write month folder name to shared data 
      cr->Set( KCamCrLastUsedMonthFolder, monthFolder );
      }  
  CleanupStack::PopAndDestroy( cr );    
  TBuf<KMaxNameBaseLength> cameraFolder;
  StringLoader::Load( cameraFolder, R_CAM_CAMERA_SUBFOLDER );
  aPath.Append( cameraFolder );   
  aPath.Append( KBackslash );          
  aPath.Append( monthFolder );  
  aPath.Append( KBackslash ); 
  
  // Keep track of the month folder (YYYYMM) name length
  // This may be returned e.g.if the month counter destination folder  (YYYYMMXX) is
  // not created
  TInt monthFolderLength = aPath.Length();
  
  RFs rfs;
  User::LeaveIfError( rfs.Connect() );
  CleanupClosePushL( rfs );     
 
  // ensure the path exists
  TInt err = rfs.MkDirAll( aPath );
 
  // If the folder is newly created then set the counter to 0  
  if      ( KErrNone          == err ) aMonthCounters[folderTypeIndex] = 0;
  else if ( KErrAlreadyExists == err ) err = KErrNone;
  else                                 User::Leave( err );

          
  // If the month counter is uninitialised it needs to be set up
  if ( aMonthCounters[folderTypeIndex] < 0 )
      {
      User::LeaveIfError( InitialiseMonthCounter( aMonthCounters,
                                                  aPath,
                                                  monthFolder,
                                                  rfs,
                                                  folderTypeIndex ) );
      }
  aPath.Append( monthFolder ); 
  // Only ensure the folder exists (and has space) if the aCreateAll flag is set
  if ( aCreateAll )
      {
      PRINT( _L("Camera GetBasePathL creating month counter folder") )
      // This adds on the correct counter if completing without error
      User::LeaveIfError( CreateDestinationFolder( aMonthCounters,
                                                   aPath, 
                                                   rfs, 
                                                   folderTypeIndex, 
                                                   aRequiredFileCount ) );
      }
  else
      {
      TInt monthCounter = aMonthCounters[folderTypeIndex];
      aPath.Append( KCharacterOffset()[0] + monthCounter/KBase10 );
      aPath.Append( KDigitOffset()[0] + monthCounter%KBase10 );
      aPath.Append( KBackslash );
      // If the folder does not exist then remove the final folder name from the path
      TEntry entry;
      if ( rfs.Entry( aPath, entry ) == KErrNotFound )
          {
          aPath.SetLength( monthFolderLength );
          }
      }        
  CleanupStack::PopAndDestroy( &rfs );

  PRINT( _L("Camera <= CCamAppController::GetBasePathL returning") )
  }


// ---------------------------------------------------------------------------
// ResetCounters
// ---------------------------------------------------------------------------
//
void 
CCamFolderUtility::ResetCounters( TInt* aMonthCounters,
                                  TInt  aFrom,
                                  TInt  aTo )
  {
  PRINT( _L("Camera => CCamFolderUtility::ResetCounters") )
    {
    for( TInt i = aFrom; i <= aTo; i++ )
      {
      aMonthCounters[i] = -1;
      }
    }
  }



// ---------------------------------------------------------------------------
// InitialiseMonthCounter
// Sets the value of the folder counter for the current month/media store/mode
// ---------------------------------------------------------------------------
//  
TInt 
CCamFolderUtility::InitialiseMonthCounter( TInt*  aMonthCounters,
                                           TDes&  aPath,
                                           TDesC& aMonthFolder,
                                           RFs&   aFs,
                                           TInt   aFolderType )
  {
  PRINT( _L("Camera => CCamFolderUtility::InitialiseMonthCounter") )

  // start by initialising the appropriate folder counter to 0
  aMonthCounters[aFolderType] = 0; 
      
  TInt monthFolderLength = aPath.Length();
  // The month counter folder starts with the same characters as the parent month folder
  aPath.Append( aMonthFolder );  
  TInt charCount;
  // Add on '??' wildcard characters to get a list of all folders with this 
  // month's format
  for ( charCount = 0; charCount < KMultipleFolderNumberChars; charCount++ )
      {
      aPath.Append( KWildCardCharacter );
      }
  // Get a list of folders for this month, sorted in descending alphabetical order
  // the first entry should be the latest used folder
  CDir* dirList;
  TInt err = KErrNone;
  err =  aFs.GetDir( aPath,
                     KEntryAttMatchExclusive|KEntryAttDir,
                     ESortByName|EDescending,
                     dirList );
  // Prune back to the parent folder path                           
  aPath.SetLength( monthFolderLength );                           
  if ( err == KErrNone )                           
    {
    TInt monthFolderCount = dirList->Count();                
    TInt index = 0;
    TBool done = EFalse;
    // Look through the list of folders in the month for the highest numbered folder
    // with the format YYYYMMAX Where YYYY is the year MM is the month A is an alphabetical
    // character in the range a-z or A-Z and X is a digit 0-9
    while ( index < monthFolderCount && !done )
      {
      done = ETrue;
      // The list is sorted in descending order. Get the last 2 characters from
      // the first directory in the list these indicate the highest folder number
      TPtrC name = ( *dirList )[index].iName; 
      TInt nameLength = name.Length();
      // Check the first character is in the range a-z or A-Z
      TChar firstChar = name[nameLength - KMultipleFolderNumberChars];
      firstChar.UpperCase();
      // If the character is not in the range then disregard this folder
      if ( firstChar < KCharacterOffset()[0] ||
           firstChar > KMaxCharacter()[0] )
        {
        done = EFalse;
        }
      // Check the second character is in the range 0-9                
      TChar secondChar = name[nameLength - 1];
      TInt secondCharVal = secondChar.GetNumericValue();
      if ( secondCharVal < 0 ||
           secondCharVal > KBase10 - 1 )
        {
        done = EFalse;
        }   
      if ( done )                             
        {
        TUint folderNumber = firstChar;
        // 10's part of folder number is represented by characters A-Z
        // convert the character into a decimal value
        folderNumber -= KCharacterOffset()[0];
        folderNumber *= KBase10; 
        // Now add on the units
        folderNumber += secondCharVal;
        aMonthCounters[aFolderType] = folderNumber;
        }
      // TUint folderNumber = name[nameLength - 2];       
      index++;   
      } 
    }
  delete dirList;  
  dirList = NULL;     

  PRINT1( _L("Camera <= CCamAppController::InitialiseMonthCounter returning %d"), err)
  return err;      
  }
  
// ---------------------------------------------------------------------------
// CreateDestinationFolder
// Creates the folder where new images/videos are to be saved to.
// ---------------------------------------------------------------------------
//
TInt 
CCamFolderUtility::CreateDestinationFolder( TInt* aMonthCounters,
                                            TDes& aPath,
                                            RFs&  aFs, 
                                            TInt  aFolderType,
                                            TInt  aRequiredFileCount )    
  {
  PRINT( _L("Camera => CCamFolderUtility::CreateDestinationFolder ") ) 
  __ASSERT_DEBUG( aFolderType < ECamFolderTypeLast, CamPanic( ECamPanicBadIndex ) );

  TInt folderCreated = EFalse;
  // This error value will only be retained if the counter is outside
  // the allowed range 
  TInt err = KErrArgument; 
  TInt monthCounter = aMonthCounters[aFolderType];
  while ( !folderCreated && monthCounter < KMaxMonthFolders )
    {
    aMonthCounters[aFolderType] = monthCounter;
    err = KErrNone;   
    // Add on the new counter
    aPath.Append( KCharacterOffset()[0] + monthCounter/KBase10 );
    aPath.Append( KDigitOffset()[0] + monthCounter%KBase10 );
    aPath.Append( KBackslash );
    err = aFs.MkDirAll( aPath );
    PRINT1( _L("Camera <> MkDirAll returned %d "), err ) 
    // If the folder already exists then check there is enough space for the required file count
    if ( err == KErrAlreadyExists )
      {
      PRINT( _L("Camera <> MkDirAll KErrALreadyExists ") ) 
      // if this is the final folder (Z9) there is no need to check for available space
      // this folder will be used anyway
      if ( monthCounter >= KMaxMonthFolders - 1 ) 
        {
        PRINT( _L("Camera <> MkDirAll KErrALreadyExists Z9") ) 
        folderCreated = ETrue;
        // sanity check to ensure the counter is not too high
        aMonthCounters[aFolderType] = KMaxMonthFolders - 1;
        err = KErrNone;
        }
      // if this is not the final folder (Z9) check for space and try the next one if necessary
      else
        {  
        PRINT( _L("Camera <> MkDirAll KErrALreadyExists not Z9, retry") )                        
        CDir* fileList;  
        TInt dirErr = ( aFs.GetDir( aPath,
                                    KEntryAttMaskSupported,
                                    ESortNone,
                                    fileList ) );  
        TBool spaceRemaining = EFalse;    
        if ( !dirErr )
          {
          spaceRemaining = ( fileList->Count() + aRequiredFileCount <= KMaxFilesPerFolder );   
          }
        delete fileList;  
        fileList = NULL;                      
        if ( dirErr )        
          {
          PRINT1( _L("Camera <= CCamFolderUtility::DoCreateDestinationFolderL returning %d"), err)   
          return dirErr;    
          }             
                
        // If there is insufficient space then try the next folder                                       
        if ( !spaceRemaining )  
          {
          monthCounter++;
          // Remove the previous counter characters and the trailing backslash then try again                                        
          aPath.SetLength( aPath.Length() - ( KMultipleFolderNumberChars + 1 ) );
          }
        else // This folder has enough space for the capture
          {
          folderCreated = ETrue;
          err = KErrNone;
          }     
        }
      }
    // There is a problem creating folders - report error         
    else if ( err )   
      {
      PRINT1( _L("Camera <= CCamFolderUtility::DoCreateDestinationFolderL returning %d"), err)
      return err;    
      }
    // A new folder has been created. There is no need to check the space
    else 
      {
      folderCreated = ETrue;
      }            
    }

  PRINT1( _L("Camera <= CCamFolderUtility::DoCreateDestinationFolderL returning %d"), err)
  return err;        
  }

// ===========================================================================

// end of file
