/**
 * jsoup License
 * The jsoup code-base (include source and compiled packages) are distributed under the open source MIT license as described below.
 * The MIT License
 * Copyright © 2009 - 2016 Jonathan Hedley (jonathan@hedley.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package jp.agentec.abook.abv.ui.common.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.Iterator;

import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.cl.billing.Base64;
import jp.agentec.adf.util.FileUtil;

/**
 * Created by jang on 2016/09/14.
 */
public class ABVCachedWebView extends WebView {

    private static final String TAG = "ABVCachedWebView";

    private String cacheFileUrl;
    private Handler handler = new Handler();

    public ABVCachedWebView(Context context) {
        super(context);
    }

    public ABVCachedWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ABVCachedWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    private class _JavascriptInterface {
        public void viewSource(final String htmlSrc, final  String url) {
            boolean isSaveExpiresDate = false;
            if (FileUtil.exists(cacheFileUrl)) {
                try {
                    String readHtml = FileUtil.readTextFile(cacheFileUrl);
                    if (!htmlSrc.equals(readHtml)) {
                        Logger.d(TAG, "different HTML source:200");
                        isSaveExpiresDate = true;
                    }
                } catch (IOException e) {
                    // 無視する
                }
            } else {
                isSaveExpiresDate = true;
            }

            if (isSaveExpiresDate) {
                try {
                    // HTMLのソースをコードを保存する
                    // KDDIではHTMLコードサイズが小さいため、この方式を導入する
                    FileUtil.createFile(cacheFileUrl, htmlSrc);
                } catch (IOException e) {
                    // 無視する
                }
                handleSaveExpiredData(htmlSrc, url);
            }
        }
    }

    @SuppressLint("JavascriptInterface")
    public void initSetting() {
        Logger.d(TAG, "[initSetting]");
        getSettings().setAllowFileAccess(true);
        //jacascriptを許可する
        getSettings().setJavaScriptEnabled(true);
        addJavascriptInterface(new _JavascriptInterface(), "ABVCachedWebView");

        setWebViewClient(new WebViewClient() {
            private boolean isRedirected;
            private boolean isError = false;

            // ページ読み込み開始時の処理
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                Logger.d(TAG, "[onPageStarted]");
                isRedirected = false;
            }

            // ページ読み込み完了時の処理
            @Override
            public void onPageFinished(final WebView view, final String url) {
                // onPageFinishedが２回呼ばれるケースの対応
                if (isRedirected) {
                    isRedirected = false;
                    return;
                }
                isRedirected = true;

                if (cacheFileUrl == null || isError) {
                    // loadUrl("about:blank");の場合
                    isError = false;
                    Logger.d(TAG, "[onPageFinished]:do not show Webview:" + url);
                    return;
                }

                if (Logger.isDebugEnabled()) {
                    Logger.d(TAG, "[onPageFinished]:" + url);
                    Toast.makeText(getContext(), "読み込み完了", Toast.LENGTH_LONG).show();
                }

                // Htmlをロードする際に白い画面が表示されるため、実際の画面がローディングされるまで、間をおいて表示する
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        setVisibility(View.VISIBLE);
                    }
                }, 500);

                new Thread() {
                    @Override
                    public void run() {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                // Mainスレッドから実施
                                view.loadUrl("javascript:window.ABVCachedWebView.viewSource(document.documentElement.outerHTML, '" + url + "');");
                            }
                        });
                    }
                }.start();
            }

            // ページ読み込みエラー時の処理
            @Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                Logger.d(TAG, "[onReceivedError]:" + errorCode);
                if (Logger.isDebugEnabled()) {
                    Toast.makeText(getContext(), "通信エラー", Toast.LENGTH_LONG).show();
                }
                isError = true;
            }
        });
    }


    /**
     * 有効期限切れのチェックを行う
     * @param url String
     * @throws IOException
     */
    public void checkExpiredDate(String url) throws IOException {
        String urlHash = Base64.encode(url.getBytes());
        cacheFileUrl = String.format("%s/%s", ABVEnvironment.getInstance().getWebCacheDirectory(), urlHash);
        Logger.d(TAG, "[checkExpiredDateAndSetCacheDir]:cacheFileUrl:" + cacheFileUrl);

        long expiredDate = getExpiredValue(url);// 秒単位
        if (expiredDate == -1) {
            return;
        }

        if (expiredDate < (System.currentTimeMillis() / 1000)) {
            Logger.d(TAG, "[checkExpiredDateAndSetCacheDir]:expired Date");
            // delete cache data
            if (FileUtil.exists(cacheFileUrl)) {
                boolean result = FileUtil.deleteChilds(ABVEnvironment.getInstance().getWebCacheDirectory());
                Logger.d(TAG, "[checkExpiredDateAndSetCacheDir]:deleteFolder:" + result);
            }
            clearCache(true);
            // remove expire date
            SharedPreferences pref = getContext().getSharedPreferences(TAG, Context.MODE_PRIVATE);
            pref.edit().remove(url).commit();
        }
    }

    public String getCacheFileUrl() {
        return cacheFileUrl;
    }


    private long getExpiredValue(String url) {
        SharedPreferences pref = getContext().getSharedPreferences(TAG, Context.MODE_PRIVATE);
        return pref.getLong(url, -1);
    }



    private void handleSaveExpiredData(final String htmlSrc, final String keyUrl) {
        new Thread() {
            @Override
            public void run() {
                saveExpiredData(htmlSrc, keyUrl);
            }
        }.start();
    }

    /**
     * ソースのDocumentからExpiresを取得して、保存する
     */
    private void saveExpiredData(String htmlSrc, final String keyUrl)  {
        final Document doc = Jsoup.parse(htmlSrc);
        Elements elements = doc.getElementsByTag("meta");
        Iterator<Element> iterator = elements.iterator();
        while (iterator.hasNext()) {
            Elements expires = iterator.next().getElementsByAttributeValue("http-equiv", "Expires");
            if (expires.iterator().hasNext()) {
                for (Attribute attribute : expires.get(0).attributes()) {
                    if (attribute.getKey().equals("content")) {
                        saveExpiredDate(attribute.getValue(), keyUrl);
                        return;
                    }
                }
            }
        }
    }

    /**
     * キャッシュの有効期限切れの日付をSharedPreferencesに保存する
     * @param value String HTMLに指定されたExpireの値(秒)
     * @param keyUrl String 指定サイトのURL
     */
    private void saveExpiredDate(String value, String keyUrl) {
        SharedPreferences pref = getContext().getSharedPreferences(TAG, Context.MODE_PRIVATE);
        Logger.d(TAG, "[saveExpiredDate]:" + keyUrl +":" + value);
        SharedPreferences.Editor editor = pref.edit();
        long currentTimeForSecond = System.currentTimeMillis() / 1000;
        long expiredDate = currentTimeForSecond + Long.parseLong(value);
        editor.putLong(keyUrl, expiredDate).commit();
    }

}
