package de.smasi.tickmate;

import android.app.ListActivity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import de.smasi.tickmate.database.DataSource;
import de.smasi.tickmate.models.Group;
import de.smasi.tickmate.models.Track;
import de.smasi.tickmate.widgets.MultiTickButton;
import de.smasi.tickmate.widgets.TickButton;

public class TickAdapter extends BaseAdapter {

	private final Context context;
    private Calendar activeDay;  // When set, the display will be fixed to this day.
    // Null value is intentionally used to indicate display should follow the actual current day.
    private Calendar today, yday;
    int count, count_ahead;
    private Map<Calendar, View> mRowCache = new HashMap<>();

    private List<Track> mTracksCurrentlyDisplayed; // Determined by group selector

    private boolean isTodayAtTop = false;  // Reverses the date ordering - most recent dates at the top
    private static final String TAG = "TickAdapter";
    private static final int DEFAULT_COUNT_PAST = 21; 
    private static final int DEFAULT_COUNT_AHEAD = 0;

    private int mCurrentGroupId;

	public TickAdapter(Context context, Calendar activeDay, Bundle restoreStateBundle) {
		// super(context, R.layout.rowlayout, days);
		this.context = context;
		this.count = DEFAULT_COUNT_PAST;
		this.count_ahead = DEFAULT_COUNT_AHEAD;

        setActiveDay(activeDay);
        isTodayAtTop = PreferenceManager.getDefaultSharedPreferences(context).
                getBoolean("reverse-date-order-key", false);
    }

    public void unsetActiveDay() {
        setActiveDay(null);
    }

    private void updateToday() {
        today = Calendar.getInstance();
        today.set(Calendar.HOUR_OF_DAY, 0);
        today.set(Calendar.MINUTE, 0);
        today.set(Calendar.SECOND, 0);
        today.set(Calendar.MILLISECOND, 0);


        yday = (Calendar) today.clone();
        yday.add(Calendar.DATE, -1);
    }

	public void setActiveDay(Calendar activeDay) {
        updateToday();

        // null is used to indicate 'not set'
		if (activeDay != null) {
			java.text.DateFormat dateFormat = android.text.format.DateFormat
					.getDateFormat(context);
			Log.d(TAG, "Active day set to " + dateFormat.format(activeDay.getTime()));
			activeDay.set(Calendar.HOUR_OF_DAY, 0);
			activeDay.set(Calendar.MINUTE, 0);
			activeDay.set(Calendar.SECOND, 0);
			activeDay.set(Calendar.MILLISECOND, 0);
		}
		
		this.activeDay = activeDay;
        Log.d(TAG, "Active day set to " + activeDay);

        notifyDataSetChanged();
	}


    public Calendar getActiveDay() {
		if (this.activeDay == null) {
            updateToday();
            return (Calendar) this.today.clone();  
        } else {
            return (Calendar) this.activeDay.clone();
        }
    }

    public void addCount(int num) {
        this.count += num;
        notifyDataSetChanged();
    }

    public int getCount() {
        // Return either 0 or this.count, depending on whether the empty view should be displayed.
        // Empty view is displayed if (a) no active tracks exist (b) a group is selected, and there
        // are no tracks for that group

        DataSource ds = DataSource.getInstance();

        if (ds.getActiveTracks().size() == 0) {
            return 0;
        }

        if ((mCurrentGroupId != Group.ALL_GROUP.getId())
                && (ds.getTracksForGroup(mCurrentGroupId).size() == 0)) {
            return 0;
        }

        return this.count;
    }

	public Object getItem(int position) {
        if (isTodayAtTop) {
            return position;
        } else {
            return getCount() - position;
        }
    }

    public long getItemId(int position) {
        return position;
    }

    public void setCurrentGroup(int groupId) {
        mCurrentGroupId = groupId;
        notifyDataSetChanged();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //        Log.d(TAG, "getView(" + position + ", " + convertView + ", " + parent);

        Integer days = (Integer) getItem(position);
        Calendar rowDay = getActiveDay();
        rowDay.add(Calendar.DATE, -days);

        View v;
        if (mRowCache.containsKey(rowDay)) {
            v = mRowCache.get(rowDay);
        } else {
            v = buildRow(rowDay);
            mRowCache.put(rowDay, v);
        }

        return v;
    }

    /**
     * Used to create and insert the week separator
     *
     * @param tickGrid the ViewGroup into which the week separator will be inserted
     */
    private void addStartWeekSeparator(ViewGroup tickGrid) {
        TextView splitter2 = new TextView(this.context);
        splitter2.setText("");
        splitter2.setHeight(5);
        tickGrid.addView(splitter2);
        TextView splitter = new TextView(this.context);
        splitter.setText("");
        splitter.setHeight(11);
        splitter.setBackgroundResource(R.drawable.center_line);
        splitter.setPadding(0, 20, 0, 0);
        tickGrid.addView(splitter);
    }

    public View buildRow(Calendar cal) {
        Locale locale = Locale.getDefault();
        Date date = cal.getTime();
        java.text.DateFormat dateFormat = android.text.format.DateFormat
                .getDateFormat(context);

        //Log.v(TAG, "Inflating row " + dateFormat.format(cal.getTime()));

        LinearLayout tickgrid = new LinearLayout(this.context);
        tickgrid.setOrientation(LinearLayout.VERTICAL);
        String s = dateFormat.format(date);

        TextView t_weekday = new TextView(this.context);
        TextView t_date = new TextView(this.context);

        if (cal.compareTo(today) == 0)
            t_date.setText(context.getString(R.string.today));
        else if (cal.compareTo(yday) == 0)
            t_date.setText(context.getString(R.string.yesterday));
        else
            t_date.setText(s);

        // If the date order has not been reversed, then add the splitter above the first day of the week
        //  splitter for first weekday depends on current locale
        if (!isTodayAtTop &&  ( cal.get(Calendar.DAY_OF_WEEK) == cal.getFirstDayOfWeek())) {
            addStartWeekSeparator(tickgrid);
        }

        String day_name = cal.getDisplayName(Calendar.DAY_OF_WEEK,
                Calendar.SHORT, locale);
        t_weekday.setText(day_name.toUpperCase(locale));

        t_weekday.setTextAppearance(this.context,
                android.R.style.TextAppearance_Medium);
        t_date.setWidth(120);
        t_date.setTextAppearance(this.context,
                android.R.style.TextAppearance_Small);
        t_date.setTextSize((float) 11.0);
        t_date.setTextColor(Color.GRAY);
        t_weekday.setWidth(120);
        LinearLayout row = new LinearLayout(this.context);
        row.setOrientation(LinearLayout.HORIZONTAL);
        LinearLayout l = new LinearLayout(this.context);
        l.setOrientation(LinearLayout.VERTICAL);
        l.addView(t_weekday);
        l.addView(t_date);
        t_date.setEllipsize(null);
        t_weekday.setEllipsize(null);

        // Some screen characteristics:
        // float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
        // int densityDpi = context.getResources().getDisplayMetrics().densityDpi;
        // Log.d("tickmate", t_weekday.getTextSize() + "|" +
        // t_date.getTextSize() + "|" + scaledDensity + "|" + densityDpi);
        // Small screen, normal font 27.0|16.5|1.5|240
        // Small screen, huge font 35.0|21.449999|1.9499999|240
        // Huge screen, normal font 24.0|14.643751|1.3312501|213
        // Huge screen, huge font 31.0|19.036875|1.730625|213

        int rowHeight = (int) (t_weekday.getTextSize() + t_date.getTextSize()) + 40;

        l.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                rowHeight, 0.8f));
        l.setGravity(Gravity.CENTER_VERTICAL);

        LinearLayout l2 = new LinearLayout(this.context);
        l2.setOrientation(LinearLayout.HORIZONTAL);
        for (Track track : mTracksCurrentlyDisplayed) {

            if (track.multipleEntriesEnabled()) {
                MultiTickButton counter = new MultiTickButton(this.context,
                        track, (Calendar) cal.clone());
                counter.setLayoutParams(new LayoutParams(
                        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
                        (1.0f) / mTracksCurrentlyDisplayed.size()));
                l2.addView(counter);
            } else {
                TickButton checker = new TickButton(this.context, track, cal);
                checker.setLayoutParams(new LayoutParams(
                        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
                        (1.0f) / mTracksCurrentlyDisplayed.size()));
                l2.addView(checker);
            }
        }
        l2.setWeightSum(1.0f);
        l2.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                rowHeight, 0.2f));

        row.addView(l);
        row.addView(l2);
        row.setGravity(Gravity.CENTER);

        if (cal.compareTo(this.getActiveDay()) == 0) {
            row.setBackgroundResource(android.R.drawable.dark_header);
            row.setPadding(0, 0, 0, 0);
        }

        tickgrid.addView(row);
        tickgrid.setPadding(10, 0, 10, 5);

        // With the date order reversed, add the splitter below the first day of the week
        //  splitter for first weekday depends on current locale
        if (isTodayAtTop &&  ( cal.get(Calendar.DAY_OF_WEEK) == cal.getFirstDayOfWeek())) {
            addStartWeekSeparator(tickgrid);
        }

        return tickgrid;
    }

    // Used for Jump To [Date|Today], and used when the toggling isTodayAtTop
    public void scrollToLatest() {
        int scrollposition = (isTodayAtTop) ? 0 : this.count - 1;
        ((ListActivity) context).getListView().smoothScrollToPositionFromTop(scrollposition, 1, 0);
    }

	@Override
	public void notifyDataSetChanged() {
		java.text.DateFormat dateFormat = android.text.format.DateFormat
				.getDateFormat(context);

        // Today may have changed, for instance when the App was running in the background
        // for a while and then resumed. So, update today!
        updateToday();

        mRowCache.clear();

        boolean previousIsTodayAtTop = isTodayAtTop;  // Used to determine if this value has been toggled since last data set change

        isTodayAtTop = PreferenceManager.getDefaultSharedPreferences(context).
                getBoolean("reverse-date-order-key", false);

        if (isTodayAtTop != previousIsTodayAtTop) {
            scrollToLatest();
        }

        DataSource ds = DataSource.getInstance();

        Calendar startday = this.getActiveDay();
        Calendar endday = (Calendar) startday.clone();
        startday.add(Calendar.DATE, -this.count);

        mTracksCurrentlyDisplayed = getTracksForCurrentGroup();
//            Log.d(TAG, "Tracks associated with current group (" + getCurrentGroupId() +
//                    ") are: (" + TextUtils.join(",", mTracksCurrentlyDisplayed) + ")");

        Log.v(TAG, "Data range has been updated: " + dateFormat.format(startday.getTime()) + " - " + dateFormat.format(today.getTime()));
        ds.retrieveTicks(startday, endday);

//        Log.d(TAG, "Tracks currently displayed: " + TextUtils.join("\n ", mTracksCurrentlyDisplayed));
//        for (Track t : mTracksCurrentlyDisplayed) { Log.d(TAG, t.getName()); }

        super.notifyDataSetChanged();
    }

    // TODO this should really live in a cache (also, it's duplicated in TickHeader)
    private List<Track> getTracksForCurrentGroup() {
        if (mCurrentGroupId == Group.ALL_GROUP.getId()) {
            return DataSource.getInstance().getActiveTracks();
        } else {
            return DataSource.getInstance().getTracksForGroup(mCurrentGroupId);
        }
    }
}