package com.gracecode.android.presentation.adapter;

import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.android.volley.RequestQueue;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.NetworkImageView;
import com.gracecode.android.common.Logger;
import com.gracecode.android.presentation.Huaban;
import com.gracecode.android.presentation.R;
import com.gracecode.android.presentation.dao.Pin;
import com.gracecode.android.presentation.helper.DatabaseHelper;
import com.gracecode.android.presentation.listener.PinsAdapterListener;
import com.gracecode.android.presentation.listener.PstRequestListener;
import com.gracecode.android.presentation.request.PstRequest;
import com.gracecode.android.presentation.ui.DetailActivity;

import java.sql.SQLException;
import java.util.ArrayList;

public class PinsAdapter extends BaseAdapter
        implements AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener {
    private static final int MAX_PINS_NUM = 1024;
    private static final int UPDATE_SET_CHANGED = 0x001;

    private final Context mContext;
    private final DatabaseHelper mDatabaseHelper;
    private final PinsAdapterListener mListener;
    private ArrayList<Pin> mPins = new ArrayList<>();
    private RequestQueue mRequestQueue;
    private boolean isRequesting = false;
    private Huaban mHuabanApp;

    @Override
    public void onRefresh() {
        loadNewestPinsFromNetwork();
    }

    private static final class Holder {
        private final NetworkImageView mImageView;
        private final TextView mTitle;

        private Holder(View v) {
            mImageView = (NetworkImageView) v.findViewById(R.id.url);
            mTitle = (TextView) v.findViewById(R.id.title);
            v.setTag(this);
        }

        public static Holder get(View v) {
            if (v.getTag() instanceof Holder) {
                return (Holder) v.getTag();
            }

            return new Holder(v);
        }
    }

    private PstRequestListener mLoadHeadResponseListener = new PstRequestListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            mListener.onErrorResponse(error);
        }

        @Override
        public void onResponse(String response) {
            mListener.onResponse(response);
        }

        @Override
        public void onSaved(int affectedRows) {
            if (affectedRows > 0) {
                loadNewestPins(false);
                mUIChangedChangedHandler.sendEmptyMessage(UPDATE_SET_CHANGED);
            }

            mListener.onSaved(affectedRows);
            isRequesting = false;
        }
    };

    private PstRequestListener mLoadFootResponseListener = new PstRequestListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            mListener.onErrorResponse(error);
        }

        @Override
        public void onResponse(String response) {
            mListener.onResponse(response);
        }

        @Override
        public void onSaved(int affectedRows) {
            if (affectedRows > 0) {
                loadPinsByMaxId(false);
                notifyDataSetChanged();
            }
            mListener.onSaved(affectedRows);
            isRequesting = false;
        }
    };

    public PinsAdapter(Context context, PinsAdapterListener listener) {
        mRequestQueue = Huaban.getInstance().getRequestQueue();
        mContext = context;
        mDatabaseHelper = Huaban.getInstance().getDatabaseHelper();
        mListener = listener;
        mHuabanApp = Huaban.getInstance();

        loadNewestPins(true);
    }

    // @see https://dev.twitter.com/docs/working-with-timelines
    private int getBeforeMaxId() {
        try {
            int tmp = mPins.get(0).getId();
            for (int i = 0; i < getCount(); i++) {
                int id = mPins.get(i).getId();
                if (tmp > id) tmp = id;
            }
            return tmp;
        } catch (RuntimeException e) {
            return 0;
        }
    }

    private int getAfterSinceId() {
        try {
            int id = 0;
            for (int i = 0; i < getCount(); i++) {
                int tmp = mPins.get(i).getId();
                if (tmp > id) id = tmp;
            }
            return id;
        } catch (RuntimeException e) {
            return 0;
        }
    }

    public void loadNewestPinsFromNetwork() {
        PstRequest request = new PstRequest(0, getAfterSinceId(), mLoadHeadResponseListener);
        isRequesting = true;
        mRequestQueue.add(request);
    }

    public void loadPinsByMaxIdFromNetwork() {
        PstRequest request = new PstRequest(getBeforeMaxId(), 0, mLoadFootResponseListener);
        isRequesting = true;
        mRequestQueue.add(request);
    }

    private final Handler mUIChangedChangedHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_SET_CHANGED:
                    notifyDataSetChanged();
                    break;
            }
        }
    };

    private void loadPinsByMaxId(boolean allowNetworkFetch) {
        if (mPins.size() > MAX_PINS_NUM) {
            Logger.w("Max " + MAX_PINS_NUM + " Pins Limited.");
            return;
        }

        try {
            ArrayList<Pin> pins = mDatabaseHelper.getPinsBeforeMaxId(getBeforeMaxId());
            if (allowNetworkFetch && !isRequesting && pins.size() < Huaban.PAGE_SIZE) {
                Logger.i("Query from network, with max " + getBeforeMaxId());
                loadPinsByMaxIdFromNetwork();
                return;
            }
            mPins.addAll(pins);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            mUIChangedChangedHandler.sendEmptyMessage(UPDATE_SET_CHANGED);
        }
    }

    private void loadNewestPins(boolean allowNetworkFetch) {
        try {
            ArrayList<Pin> pins = mDatabaseHelper.getPinsAfterSinceId(getAfterSinceId());
            if (allowNetworkFetch && !isRequesting && pins.size() < Huaban.PAGE_SIZE) {
                Logger.i("There is no newer Pins exists. Query with since id " + getAfterSinceId());
                loadNewestPinsFromNetwork();
                return;
            }
            mPins.addAll(0, pins);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            mUIChangedChangedHandler.sendEmptyMessage(UPDATE_SET_CHANGED);
        }
    }

    @Override
    public int getCount() {
        return mPins.size();
    }

    @Override
    public Pin getItem(int i) {
        return mPins.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        Pin pin = getItem(i);
        String imageUrl = mHuabanApp.isDownloadRetinaImage() ?
                pin.getRetinaSquareThumbUrl() : pin.getSquareThumbUrl();

        if (view == null) {
            LayoutInflater inflater = LayoutInflater.from(mContext);
            view = inflater.inflate(R.layout.grid, null);
        }

        if (i > getCount() - 2) {
            loadPinsByMaxId(true);
        }

        Holder h = Holder.get(view);
        Logger.v("Load image from " + imageUrl);
        h.mImageView.setImageUrl(imageUrl, mHuabanApp.getImageLoader());

        h.mTitle.setText(pin.getText());
        if (pin.getText().trim().length() > 0) {
            h.mTitle.setVisibility(View.VISIBLE);
        } else {
            h.mTitle.setVisibility(View.INVISIBLE);
        }

        return view;
    }

    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
        Intent intent = new Intent(mContext, DetailActivity.class);
        intent.putExtra(DatabaseHelper.FIELD_ID, getItem(i).getId());
        mContext.startActivity(intent);
    }
}