/*
 * Copyright (C) 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.codeim.youliao.ui.base;

import java.util.ArrayList;
import java.util.List;

import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.codeim.youliao.R;
import com.codeim.youliao.TwitterApplication;
import com.codeim.youliao.app.Preferences;
import com.codeim.youliao.data.Tweet;
import com.codeim.youliao.db.StatusTable;
import com.codeim.youliao.fanfou.Status;
import com.codeim.youliao.http.HttpException;
import com.codeim.youliao.task.GenericTask;
import com.codeim.youliao.task.TaskAdapter;
import com.codeim.youliao.task.TaskListener;
import com.codeim.youliao.task.TaskManager;
import com.codeim.youliao.task.TaskParams;
import com.codeim.youliao.task.TaskResult;
import com.codeim.youliao.ui.module.BrowseCursorAdapter;
import com.codeim.youliao.ui.module.FlingGestureListener;
import com.codeim.youliao.ui.module.SimpleFeedback;
import com.codeim.youliao.ui.module.TweetAdapter;
import com.codeim.youliao.util.DateTimeHelper;
import com.codeim.youliao.util.DebugTimer;
import com.hlidskialf.android.hardware.ShakeListener;
import com.markupartist.android.widget.PullToRefreshListView;
import com.markupartist.android.widget.PullToRefreshListView.OnRefreshListener;

/**
 * TwitterCursorBaseLine用于带有静态数据来源（对应数据库的，与twitter表同构的特定表）的展现
 */
public abstract class BrowseCursorBaseActivity extends BrowseListBaseActivity {
    static final String TAG = "BrowseCursorBaseActivity";
	
	protected String mNameType = "";

    // Views.
    protected PullToRefreshListView mTweetList;
    protected BrowseCursorAdapter mTweetAdapter;

    protected View mListHeader;
    protected View mListFooter;

    protected TextView loadMoreBtn;
    protected ProgressBar loadMoreGIF;
    protected TextView loadMoreBtnTop;
    protected ProgressBar loadMoreGIFTop;

    protected static int lastPosition = 0;

    protected ShakeListener mShaker = null;
    
    // Tasks.
    protected TaskManager taskManager = new TaskManager();
    private GenericTask mRetrieveTask;
    private GenericTask mGetMoreTask;

    private int mRetrieveCount = 0;

    private TaskListener mRetrieveTaskListener = new TaskAdapter() {

        @Override
        public String getName() {
            return "RetrieveTask";
        }

        @Override
        public void onPostExecute(GenericTask task, TaskResult result) {
            // 刷新按钮停止旋转
            loadMoreGIF.setVisibility(View.GONE);
            mTweetList.onRefreshComplete();

            if (result == TaskResult.AUTH_ERROR) {
                
            } else if (result == TaskResult.OK) {
                // TODO: XML处理, GC压力
                SharedPreferences.Editor editor = getPreferences().edit();
                editor.putLong(Preferences.LAST_TWEET_REFRESH_KEY, DateTimeHelper.getNowTime());
                editor.commit();
                // TODO: 1. StatusType(DONE) ;
                if (mRetrieveCount >= StatusTable.MAX_ROW_NUM) {
                    // 只有在取回的数据大于MAX时才做GC, 因为小于时可以保证数据的连续性
                    getDb().gc(getUserId(), getDatabaseType()); // GC
                }
                draw();
                if (task == mRetrieveTask) {
                    goTop();
                }
            } else if (result == TaskResult.IO_ERROR) {
                // FIXME: bad smell
                if (task == mRetrieveTask) {
                    mFeedback.failed(((RetrieveTask) task).getErrorMsg());
                } else if (task == mGetMoreTask) {
                    mFeedback.failed(((GetMoreTask) task).getErrorMsg());
                }
            } else {
                // do nothing
            }
            
            // DEBUG
            if (TwitterApplication.DEBUG) {
                DebugTimer.stop();
                Log.v("DEBUG", DebugTimer.getProfileAsString());
            }
        }

        @Override
        public void onPreExecute(GenericTask task) {
            mRetrieveCount = 0;
            mTweetList.prepareForRefresh();

            if (TwitterApplication.DEBUG) {
                DebugTimer.start();
            }
        }

        @Override
        public void onProgressUpdate(GenericTask task, Object param) {
            Log.d(TAG, "onProgressUpdate");
            draw();
        }
    };

    // Refresh data at startup if last refresh was this long ago or greater.
    private static final long REFRESH_THRESHOLD = 5 * 60 * 1000;
    // Refresh followers if last refresh was this long ago or greater.
    // private static final long FOLLOWERS_REFRESH_THRESHOLD = 12 * 60 * 60 * 1000;

    abstract protected void markAllRead();
    abstract protected Cursor fetchMessages();
    public abstract int getDatabaseType();
    public abstract String getUserId();
    public abstract String fetchMaxId();
    public abstract String fetchMinId();
    public abstract int addMessages(ArrayList<Tweet> tweets, boolean isUnread);
    public abstract List<Status> getMessageSinceId(String maxId) throws HttpException;
    public abstract List<Status> getMoreMessageFromId(String minId) throws HttpException;

    public static final int CONTEXT_REPLY_ID = Menu.FIRST + 1;
    // public static final int CONTEXT_AT_ID = Menu.FIRST + 2;
    public static final int CONTEXT_RETWEET_ID = Menu.FIRST + 3;
    public static final int CONTEXT_DM_ID = Menu.FIRST + 4;
    public static final int CONTEXT_MORE_ID = Menu.FIRST + 5;
    public static final int CONTEXT_ADD_FAV_ID = Menu.FIRST + 6;
    public static final int CONTEXT_DEL_FAV_ID = Menu.FIRST + 7;

    @Override
    protected void setupState() {
        Cursor cursor;

        cursor = fetchMessages(); // getDb().fetchMentions();
        setTitle(getActivityTitle());
        startManagingCursor(cursor);

        mTweetList = (PullToRefreshListView) findViewById(R.id.tweet_list);

        // TODO: 需处理没有数据时的情况
        Log.d("LDS", cursor.getCount() + " cursor count");
        setupListHeader(true);

        mTweetAdapter = new BrowseCursorAdapter(this, cursor);
        mTweetList.setAdapter(mTweetAdapter);
        // registerOnClickListener(mTweetList);
    }

    /**
     * 绑定listView底部 - 载入更多 NOTE: 必须在listView#setAdapter之前调用
     */
    protected void setupListHeader(boolean addFooter) {
        // Add Header to ListView
        //mListHeader = View.inflate(this, R.layout.listview_header, null);
        //mTweetList.addHeaderView(mListHeader, null, true);
    	mTweetList.setOnRefreshListener(new OnRefreshListener() {
    		@Override
    		public void onRefresh(){
    			doRetrieve();
    		}
    	});

        // Add Footer to ListView
        mListFooter = View.inflate(this, R.layout.listview_footer, null);
        mTweetList.addFooterView(mListFooter, null, true);
        
        // Find View
        loadMoreBtn = (TextView) findViewById(R.id.ask_for_more);
        loadMoreGIF = (ProgressBar) findViewById(R.id.rectangleProgressBar);
        loadMoreBtnTop = (TextView) findViewById(R.id.ask_for_more_header);
        loadMoreGIFTop = (ProgressBar) findViewById(R.id.rectangleProgressBar_header);
    }

    @Override
    protected void specialItemClicked(int position) {
        // 注意 mTweetAdapter.getCount 和 mTweetList.getCount的区别
        // 前者仅包含数据的数量（不包括foot和head），后者包含foot和head
        // 因此在同时存在foot和head的情况下，list.count = adapter.count + 2
        if (position == 0) {
            // 第一个Item(header)
            loadMoreGIFTop.setVisibility(View.VISIBLE);
            doRetrieve();
        } else if (position == mTweetList.getCount() - 1) {
            // 最后一个Item(footer)
            loadMoreGIF.setVisibility(View.VISIBLE);
            doGetMore();
        }
    }

    @Override
    protected int getLayoutId() {
        return R.layout.main;
    }

    @Override
    protected ListView getTweetList() {
        return mTweetList;
    }

    @Override
    protected TweetAdapter getTweetAdapter() {
        return mTweetAdapter;
    }

    @Override
    protected boolean useBasicMenu() {
        return true;
    }

    @Override
    protected Tweet getContextItemTweet(int position) {
        position = position - 1;
        // 因为List加了Header和footer，所以要跳过第一个以及忽略最后一个
        if (position >= 0 && position < mTweetAdapter.getCount()) {
            Cursor cursor = (Cursor) mTweetAdapter.getItem(position);
            if (cursor == null) {
                return null;
            } else {
                return StatusTable.parseCursor(cursor);
            }
        } else {
            return null;
        }
    }

    @Override
    protected void updateTweet(Tweet tweet) {
        // TODO: updateTweet() 在哪里调用的? 目前尚只支持:
        // updateTweet(String tweetId, ContentValues values)
        // setFavorited(String tweetId, String isFavorited)
        // 看是否还需要增加updateTweet(Tweet tweet)方法

        // 对所有相关表的对应消息都进行刷新（如果存在的话）
        // getDb().updateTweet(TwitterDbAdapter.TABLE_FAVORITE, tweet);
        // getDb().updateTweet(TwitterDbAdapter.TABLE_MENTION, tweet);
        // getDb().updateTweet(TwitterDbAdapter.TABLE_TWEET, tweet);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    	Log.d(TAG, "TwitterCursorBaseActivity at _onCreate");
        super.onCreate(savedInstanceState);

        boolean shouldRetrieve = false;
		mNameType = "browse_";
        // FIXME： 该子类页面全部使用了这个统一的计时器，导致进入Mention等分页面后经常不会自动刷新
        long lastRefreshTime = mPreferences.getLong(mNameType + Preferences.LAST_TWEET_REFRESH_KEY, 0);
        long nowTime = DateTimeHelper.getNowTime();

        long diff = nowTime - lastRefreshTime;
        Log.d(TAG, "Last refresh was " + diff + " ms ago.");

        if (diff > REFRESH_THRESHOLD) {
            shouldRetrieve = true;
        } else if (isTrue(savedInstanceState, SIS_RUNNING_KEY)) {
            // Check to see if it was running a send or retrieve task.
            // It makes no sense to resend the send request (don't want
            // dupes)
            // so we instead retrieve (refresh) to see if the message has
            // posted.
            Log.d(TAG, "Was last running a retrieve or send task. Let's refresh.");
            shouldRetrieve = true;
        }

        if (shouldRetrieve) {
            doRetrieve();
        }

        goTop(); // skip the header
    }

	@Override
    protected void onResume() {
        Log.d(TAG, "onResume.");
        if (lastPosition != 0) {
            mTweetList.setSelection(lastPosition);
        }
        if (mShaker != null){
        	mShaker.resume();
        }
        super.onResume();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        if (mRetrieveTask != null && mRetrieveTask.getStatus() == GenericTask.Status.RUNNING) {
            outState.putBoolean(SIS_RUNNING_KEY, true);
        }
    }

    @Override
    protected void onRestoreInstanceState(Bundle bundle) {
        super.onRestoreInstanceState(bundle);
        // mTweetEdit.updateCharsRemain();
    }

    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy.");
        super.onDestroy();

        taskManager.cancelAll();

        // 刷新按钮停止旋转
        if (loadMoreGIF != null){
        	loadMoreGIF.setVisibility(View.GONE);
        }
        if (mTweetList != null){
        	mTweetList.onRefreshComplete();
        }
    }

    @Override
    protected void onPause() {
        Log.d(TAG, "onPause.");
        if (mShaker != null){
        	mShaker.pause();
        }
        super.onPause();
        lastPosition = mTweetList.getFirstVisiblePosition();
		mTweetAdapter.stopPlay();
    }

    @Override
    protected void onRestart() {
        Log.d(TAG, "onRestart.");
        super.onRestart();
    }

    @Override
    protected void onStart() {
        Log.d(TAG, "onStart.");
        super.onStart();
    }

    @Override
    protected void onStop() {
        Log.d(TAG, "onStop.");
        super.onStop();
    }

    // UI helpers.

    @Override
    protected String getActivityTitle() {
        return null;
    }

    @Override
    protected void adapterRefresh() {
        mTweetAdapter.notifyDataSetChanged();
        mTweetAdapter.refresh();
    }

    // Retrieve interface
    public void updateProgress(String progress) {
        mProgressText.setText(progress);
    }

    public void draw() {
        mTweetAdapter.refresh();
    }

    public void goTop() {
        Log.d(TAG, "goTop.");
        mTweetList.setSelection(1);
    }

    public void doRetrieve() {
        Log.d(TAG, "Attempting retrieve.");

        if (mRetrieveTask != null && mRetrieveTask.getStatus() == GenericTask.Status.RUNNING) {
            return;
        } else {
            mRetrieveTask = new RetrieveTask();
            mRetrieveTask.setFeedback(mFeedback);
            mRetrieveTask.setListener(mRetrieveTaskListener);
            mRetrieveTask.execute();

            // Add Task to manager
            taskManager.addTask(mRetrieveTask);
        }
    }

    private class RetrieveTask extends GenericTask {
        private String _errorMsg;

        public String getErrorMsg() {
            return _errorMsg;
        }

        @Override
        protected TaskResult _doInBackground(TaskParams... params) {
            List<com.codeim.youliao.fanfou.Status> statusList;

            try {
                String maxId = fetchMaxId(); // getDb().fetchMaxMentionId();
                statusList = getMessageSinceId(maxId);
            } catch (HttpException e) {
                Log.e(TAG, e.getMessage(), e);
                _errorMsg = e.getMessage();
                return TaskResult.IO_ERROR;
            }

            ArrayList<Tweet> tweets = new ArrayList<Tweet>();
            for (com.codeim.youliao.fanfou.Status status : statusList) {
                if (isCancelled()) {
                    return TaskResult.CANCELLED;
                }
                tweets.add(Tweet.create(status));
                if (isCancelled()) {
                    return TaskResult.CANCELLED;
                }
            }

            publishProgress(SimpleFeedback.calProgressBySize(40, 20, tweets));
            mRetrieveCount = addMessages(tweets, false);

            return TaskResult.OK;
        }
    }

    // GET MORE TASK
    private class GetMoreTask extends GenericTask {
        private String _errorMsg;

        public String getErrorMsg() {
            return _errorMsg;
        }

        @Override
        protected TaskResult _doInBackground(TaskParams... params) {
            List<com.codeim.youliao.fanfou.Status> statusList;

            String minId = fetchMinId(); // getDb().fetchMaxMentionId();

            if (minId == null) {
                return TaskResult.FAILED;
            }

            try {
                statusList = getMoreMessageFromId(minId);
            } catch (HttpException e) {
                Log.e(TAG, e.getMessage(), e);
                _errorMsg = e.getMessage();
                return TaskResult.IO_ERROR;
            }

            if (statusList == null) {
                return TaskResult.FAILED;
            }

            ArrayList<Tweet> tweets = new ArrayList<Tweet>();
            publishProgress(SimpleFeedback.calProgressBySize(40, 20, tweets));

            for (com.codeim.youliao.fanfou.Status status : statusList) {
                if (isCancelled()) {
                    return TaskResult.CANCELLED;
                }

                tweets.add(Tweet.create(status));

                if (isCancelled()) {
                    return TaskResult.CANCELLED;
                }
            }

            addMessages(tweets, false);

            return TaskResult.OK;
        }
    }

    public void doGetMore() {
        Log.d(TAG, "Attempting getMore.");

        if (mGetMoreTask != null && mGetMoreTask.getStatus() == GenericTask.Status.RUNNING) {
            return;
        } else {
            mGetMoreTask = new GetMoreTask();
            mGetMoreTask.setFeedback(mFeedback);
            mGetMoreTask.setListener(mRetrieveTaskListener);
            mGetMoreTask.execute();

            // Add Task to manager
            taskManager.addTask(mGetMoreTask);
        }
    }
    
    
    //////////////////// Gesture test /////////////////////////////////////
    private static boolean useGestrue;
    {
        useGestrue = TwitterApplication.mPref.getBoolean(Preferences.USE_GESTRUE, false);
        if (useGestrue) {
            Log.v(TAG, "Using Gestrue!");
        } else {
            Log.v(TAG, "Not Using Gestrue!");
        }
    }
    
    //////////////////// Gesture test /////////////////////////////////////
    private static boolean useShake;
    {
        useShake = TwitterApplication.mPref.getBoolean(Preferences.USE_SHAKE, false);
        if (useShake) {
            Log.v(TAG, "Using Shake to refresh!");
        } else {
            Log.v(TAG, "Not Using Shake!");
        }
    }
    
    
    protected FlingGestureListener myGestureListener = null;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (useGestrue && myGestureListener != null) {
            return myGestureListener.getDetector().onTouchEvent(event);
        }
        return super.onTouchEvent(event);
    }   
}