package com.manning.aip.mymoviesdatabase.util;

import android.util.Log;

import com.manning.aip.mymoviesdatabase.Constants;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.channels.FileChannel;

/**
 * Metody klasy FileUtil. 
 * 
 * @author ccollins
 *
 */
public final class FileUtil {

   // Obiekty do tworzenia blokad (według dokumentacji tablica o długości 0 
   // jest "lżejsza" od zwykłego obiektu klasy Object)
   public static final Object[] DATA_LOCK = new Object[0];

   private FileUtil() {
   }

   // Korzystamy też z interfejsu nio.
   /**
    * Kopiuje plik.
    * 
    * @param src
    * @param dst
    * @return
    */
   public static boolean copyFile(final File src, final File dst) {
      boolean result = false;
      FileChannel inChannel = null;
      FileChannel outChannel = null;
      synchronized (FileUtil.DATA_LOCK) {
         try {
            inChannel = new FileInputStream(src).getChannel();
            outChannel = new FileOutputStream(dst).getChannel();
            inChannel.transferTo(0, inChannel.size(), outChannel);
            result = true;
         } catch (IOException e) {

         } finally {
            if (inChannel != null && inChannel.isOpen()) {
               try {
                  inChannel.close();
               } catch (IOException e) {
                  // Ignorowanie wyjątku
               }
            }
            if (outChannel != null && outChannel.isOpen()) {
               try {
                  outChannel.close();
               } catch (IOException e) {
                  // Ignorowanie wyjątku
               }
            }
         }
      }
      return result;
   }

   /**
    * Zastępuje cały plik łańcuchem znaków.
    * 
    * @param fileContents
    * @param file
    * @return
    */
   public static boolean writeStringAsFile(final String fileContents, final File file) {
      boolean result = false;
      try {
         synchronized (FileUtil.DATA_LOCK) {
            if (file != null) {
               file.createNewFile();
               // Klasa FileWriter stosuje kodowanie domyślne. Można też użyć klasy
               // FileOutputStream, aby określić kodowanie i zagwarantować 
               // opróżnienie bufora, ale tu nie jest to konieczne.
               Writer out = new BufferedWriter(new FileWriter(file), 1024);
               out.write(fileContents);
               out.close(); // Metoda close powoduje opróżnienie bufora i zamknięcie pliku 
               // (nie ma jednak gwarancji synchronizacji).
               result = true;
            }
         }
      } catch (IOException e) {
         Log.e(Constants.LOG_TAG, "Błąd zapisu tekstu do pliku " + e.getMessage(), e);
      }
      return result;
   }

   /**
    * Dodawanie tekstu na koniec pliku.
    * 
    * @param appendContents
    * @param file
    * @return
    */
   public static boolean appendStringToFile(final String appendContents, final File file) {
      boolean result = false;
      try {
         synchronized (FileUtil.DATA_LOCK) {
            if ((file != null) && file.canWrite()) {
               file.createNewFile(); 
               // Klasa FileWriter używa domyślnego kodowania i umożliwia łatwe dodawanie tekstu.
               Writer out = new BufferedWriter(new FileWriter(file, true), 1024);
               out.write(appendContents);
               out.close();
               result = true;
            }
         }
      } catch (IOException e) {
         Log.e(Constants.LOG_TAG, "Błąd przy dodawaniu tekstu do pliku " + e.getMessage(), e);
      }
      return result;
   }

   /**
    * Wywołanie metody sync klasy FileOutputStream gwarantuje natychmiastowy zapis danych na dysku. 
    * Metody write, flush, close itd. nie gwarantują fizycznego zapisu w systemach plików z buforami.
    * 
    * @param stream
    * @return
    */
   public static boolean syncStream(FileOutputStream fos) {
      try {
         if (fos != null) {
            fos.getFD().sync();
         }
         return true;
      } catch (IOException e) {
         Log.e(Constants.LOG_TAG, "Błąd synchronizacji strumienia " + e.getMessage(), e);
      }
      return false;
   }
}
