package code.util

import java.net.URLEncoder
import net.liftweb.common.Full
import net.liftweb.http.{LiftResponse, StreamingResponse}
import code.model.BackupDir
import java.io.{FileInputStream, File}
import javax.servlet.http.HttpServletRequest

/**
 * rdiff-backup-browser内で使用するユーティリティメソッドを提供します。
 */
object Util {

  /**
   * ファイルダウンロード用のレスポンスを返却します。
   */
  def fileDownload(file: File) : Full[LiftResponse] = {
    val length = file.length

    val headers = ("Content-type" -> "application/octeat-stream") ::
                  ("Cache-Control" -> "no-store") ::
                  ("Pragma", "no-cache") ::
                  ("Expires", "0" ) ::
                  ("Content-disposition", "attachment; filename=\"" + getDownloadFileName(file.getName) + "\"") ::
                  ("Content-length" -> String.valueOf(length)) :: Nil
    // 出力
    Full(StreamingResponse(new FileInputStream(file), () => {}, length, headers, Nil, 200) )
  }

  /**
   * 文字列をURLエンコードします。
   */
  def u(value: String) : String = URLEncoder.encode(value, "UTF-8")

  /**
   * リクエストパラメータ"root"のセキュリティチェック。
   * @throws IllegalArgumentException パスが不正な場合
   */
  def checkRoot(root: String): Unit = {
    if(isNullOrEmpty(root)){
      throw new IllegalArgumentException
    }
    for(dir <- BackupDir.findAll()){
      if(root == dir.directory.get){
        return
      }
    }
    throw new IllegalArgumentException
  }

  /**
   * リクエストパラメータ"path"のセキュリティチェック。
   * @throws IllegalArgumentException パスが不正な場合
   */
  def checkPath(path: String): Unit = {
    if(!isNullOrEmpty(path) && path.indexOf("..") >= 0){
      throw new IllegalArgumentException
    }
  }

	/**
	 * バックアップのルートディレクトリと、
	 * ファイルまたはディレクトリのバックアップディレクトリ内でのパスを結合して絶対パスを生成します。
	 *
	 * @param root バックアップのルートディレクトリ
	 * @param path ファイルまたはディレクトリのバックアップディレクトリ内でのパス
	 * @return ファイルまたはディレクトリの絶対パス
	 */
  def buildPath(root: String, path: String) : String = {
    (if(!isNullOrEmpty(root) && !root.endsWith("/")){
      root + "/"
    } else {
      nullToEmpty(root)
    }) + (if(!isNullOrEmpty(path) && path.startsWith("/")){
      path.substring(1)
    } else {
      nullToEmpty(path)
    })
  }

  /**
   * パスから親ディレクトリのパスを取得します。
   *
   * @param path パス
   * @return 親ディレクトリのパス
   */
  def getParentPath(path: String) : String = {
    val fixedPath = fixPath(path)
    val index = fixedPath.lastIndexOf('/')
    if(index > 0){
      fixedPath.substring(0, index)
    } else {
      ""
    }
  }

  /**
   * パスから末尾のファイル名もしくはディレクトリ名部分を取得します。
   *
   * @param path ファイルまたはディレクトリのパス
   * @return ファイル名またはディレクトリ名
   */
  def getFileName(path: String) : String = {
    val fixedPath = fixPath(path)
    val index = fixedPath.lastIndexOf('/')
    if(index >= 0){
      fixedPath.substring(index + 1);
    } else {
      throw new IllegalArgumentException()
    }
  }

  /**
   * ダウンロード用のファイル名を取得します。
   * ブラウザの種類に応じて文字コードの変換を行います。
   *
   * @param fileName ファイル名
   * @return 文字コード変換後のファイル名
   */
  private def getDownloadFileName(fileName: String) : String = {
    val userAgent = RequestUtil.getRequest.getHeader("user-agent")
    if(userAgent != null){
      if(userAgent.indexOf("MSIE") >= 0 && userAgent.indexOf("Opera") < 0){
        return new String(fileName.getBytes("Windows-31J"), "ISO8859_1")
      } else {
        return new String(fileName.getBytes("UTF-8"), "ISO8859_1")
      }
    }
    return fileName
  }

  /**
   * 文字列がnullまたは空文字列だった場合trueを返します。
   * <p/>
   * TODO GuavaのStringsは使えない？？
   */
  def isNullOrEmpty(value: String) : Boolean = (value == null || value.length == 0)

  /**
   * パスの末尾が"/"で終わっていた場合、末尾の"/"を取り除いた文字列を返します。
   * それ以外の場合は元の文字列をそのまま返します。
   */
  private def fixPath(path: String) : String =
    if(path.endsWith("/")) path.substring(0, path.length - 1) else path

  /**
   * 文字列がnullの場合は空文字列、それ以外の場合は元の文字列を返します。
   */
  private def nullToEmpty(value: String) : String = if(value == null) "" else value

  /**
   * ファイルの種類に応じたアイコンのパスを返却します。
   *
   * @param file ファイル
   * @return アイコンのパス
   */
  def getIcon(file: File): String = {
    // ディレクトリの場合
    if (file.isDirectory) return "/images/folder.png"

    // ファイルの場合
    val ext = file.getName.lastIndexOf('.') match {
      case i if i >= 0 => file.getName.substring(i)
      case _ => ""
    }

    "/images/" + (ext match {
      case ".xls" | ".xlsx" => "page_white_excel.png"
      case ".doc" | ".docx" => "page_white_word.png"
      case ".ppt" | ".pptx" => "page_white_powerpoint.png"
      case ".java" => "page_white_cup.png"
      case ".c" => "page_white_c.png"
      case ".cpp" => "page_white_cplusplus.png"
      case ".h" => "page_white_h.png"
      case ".php" => "page_white_php.png"
      case ".exe" | ".bat" | ".sh" => "page_white_gear.png"
      case ".cs" => "page_white_csharp.png"
      case ".html" => "page_white_world.png"
      case ".zip" | ".lzh" | ".gz" | ".tar" => "package.png"
      case ".pdf" => "page_white_acrobat.png"
      case _ => "page_white.png"
    })
  }

  def using[T <: {def close()}] (closeable: T) (func: T => Unit): Unit = {
    try {
      func(closeable)
    } catch {
      case e: Exception => {
        e.printStackTrace()
      }
    } finally {
      closeable.close()
    }
  }

  /**
   * 引数で渡したディレクトリ直下のファイルの配列を返します。
   *
   * @param file ディレクトリ
   * @return 引数で渡したディレクトリ直下のファイルの配列
   */
  def listFiles(file: File): Array[File] = {
    val files = file.listFiles()
    if(files != null)
      files
    else
      new Array[File](0)
  }

}