/*
 * $Id: TemplateLoader.java 220 2007-07-16 10:32:15Z sugimotokenichi $
 * Copyright (C) 2005 SUGIMOTO Ken-ichi
 * 作成日: 2005/04/03
 */
package feat2;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import feat2.config.BasicFileLocator;
import feat2.config.FileLocator;
import feat2.template.HTMLTemplate;
import feat2.template.HTMLTemplateParser;
import feat2.template.impl.HTMLParser15TemplateParser;

/**
 * Templateをロードするためのクラス。
 * @author SUGIMOTO Ken-ichi
 */
public class TemplateLoader {

    private static HTMLTemplateParser parser = new HTMLParser15TemplateParser();
    private static Map templateCache = new HashMap();
    //private static Map templateCache2 = new HashMap();

    /**
     * 現在のロケールのテンプレートをロードする簡易メソッド。
     * @param file
     * @param encoding 文字コード。nullを指定するとHTMLから判断する
     * @return
     * @throws TemplateParsingException テンプレートの解析中にエラーが発生した
     * @throws FileNotFoundException テンプレートファイルが見つからなかった
     * @throws IOException HTMLデータ読み込み中のIO例外
     */
    public static HTMLTemplate load(File file, String encoding)
        throws FileNotFoundException, TemplateParsingException, IOException {

        FileLocator fl = new BasicFileLocator(file);
        return load(fl, Locale.getDefault(), encoding);
    }

    /*public static NekoHTMLTemplate load(File file) throws FileNotFoundException, TemplateParsingException {
        FileLocator fl = new BasicFileLocator(file);
        return load(fl, Locale.getDefault());
    }*/

    /**
     *
     * @param file
     * @param locale
     * @param encoding 文字コード。nullを指定するとHTMLから判断する
     * @return
     * @throws FileNotFoundException テンプレートファイルが見つからなかった
     * @throws TemplateParsingException テンプレートの解析中にエラーが発生した
     * @throws IOException HTMLデータ読み込み中のIO例外
     */
    public static HTMLTemplate load(FileLocator file, Locale[] locale, String encoding) throws FileNotFoundException, TemplateParsingException, IOException {

        HTMLTemplate template = null;

        for (int i = 0; i < locale.length; i++) {

            template = load(file, locale[i], encoding);
            if ( template != null )
                return template;

        }

        // ロケールのテンプレートが無いときはデフォルトロケールのテンプレート

        template = load(file, Util.NULL_LOCALE, encoding);
        if ( template == null )
            throw new FileNotFoundException(file.getURIString());

        return template;

    }

    /**
    *
    * @param file
    * @param locale
    * @param encoding 文字コード。nullを指定するとHTMLから判断する
    * @return ファイルが見つからなかったらnull
    * @throws TemplateParsingException テンプレートの解析中にエラーが発生した
    * @throws IOException HTMLデータ読み込み中のIO例外
    */
   private static HTMLTemplate load(FileLocator file, Locale locale, String encoding) throws TemplateParsingException, IOException {

       // ロケールにマッチする言語名のリストを作る

       List seq = null;
       if ( locale != Util.NULL_LOCALE )
           seq = Util.expandLocale(locale);
       else {
           seq = new ArrayList();
           seq.add(Util.NULL_LOCALE);
       }

       HTMLTemplate ret = null;
       synchronized(templateCache) {

           for(int i=0; i<seq.size(); i++) {

               // 言語名を含んだファイル名を作る

               String filename = StringUtil.makeLocaleFilename(file.getFilename(), (Locale)seq.get(i));
               FileLocator f = file.newLocation(filename);
               String uristr = f.getURIString();

               // テンプレートキャッシュの中に有効なテンプレートがあるか？

               TemplateCacheEntry entry = (TemplateCacheEntry)templateCache.get(uristr);
               if ( entry != null ) {

                   // キャッシュのテンプレートがまだ有効ならこのファイルを返す

                   if ( entry.isValid() ) {
                       ret = entry.getTamplate();
                       break;
                   }

                   // キャッシュのテンプレートが期限切れならキャッシュから消す

                   else {
                        templateCache.put(uristr, null);
                   }

               }

               // ファイルがあったらロード

               if ( f.exists() ) {
                   ret = parser.parseTemplate(f, encoding);
                   entry = new TemplateCacheEntry(f, ret);
                   templateCache.put(uristr, entry);
                   break;
               }

           }

       }

       return ret;

   }

   /*
   *
   * @param file
   * @param locale
   * @return
   * @throws TemplateParsingException テンプレートの解析中にエラーが発生した
   * @throws FileNotFoundException テンプレートファイルが見つからなかった
   */
   /*public static NekoHTMLTemplate load(FileLocator file, Locale locale) throws TemplateParsingException, FileNotFoundException {
       // ロケールにマッチする言語名のリストを作る
       List seq = Util.makeSequence(locale);
       seq.add(Util.NULL_LOCALE);

       NekoHTMLTemplate ret = null;
       synchronized(templateCache) {
           for(int i=0; i<seq.size(); i++) {
               // 言語名を含んだファイル名を作る
               String filename = StringUtil.makeLocaleFilename(file.getFilename(), (Locale)seq.get(i));
               FileLocator f = file.newLocation(filename);
               String uristr = f.getURIString();

               // テンプレートキャッシュの中に有効なテンプレートがあるか？
               ret = (NekoHTMLTemplate)templateCache2.get(uristr);
               if ( ret != null ) {

                   // ファイルがまだ有効ならこのファイルを返す
                   if ( ret.isValid() ) {
                       break;
                   }
                   // ファイルが無効ならキャッシュから消す
                   else {
                          templateCache.put(uristr, null);
                   }
               }

               // ファイルがあったらロード
               if ( f.exists() ) {
                   ret = new NekoHTMLTemplate(f);
                   templateCache.put(uristr, ret);
                   break;
               }

           }
       }
       if ( ret == null )
           throw new FileNotFoundException(file.getURIString());
       return ret;
   }*/

}

class TemplateCacheEntry {

    FileLocator file;
    HTMLTemplate template;
    long lastModified;

    TemplateCacheEntry(FileLocator file, HTMLTemplate template) {
        this.file = file;
        this.template = template;
        try {
            lastModified = file.getLastModified();
        }
        catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * ファイルが存在していて、更新されていなかったらtrue。
     * @return boolean
     */
    public boolean isValid() {
        try {
            return exists() && !checkFile();
        }
        catch (IOException ex) {
            return false;
        }
    }

    public boolean exists() {
        return file != null && file.exists();
    }

    /**
     * ファイルが更新されていたらtrueを返す。
     * @param locator FileLocator
     * @return boolean
     */
    private boolean checkFile() throws IOException {
        long lastModified = file.getLastModified();
        if ( this.lastModified != lastModified ) {
            return true;
        }
        return false;
    }

    public HTMLTemplate getTamplate() {
        return template;
    }
}
