/*
 * Copyright (c) 2015 Ngewi Fet <ngewif@gmail.com>
 *
 * 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 org.gnucash.android.db.adapter;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.model.PeriodType;
import org.gnucash.android.model.Recurrence;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import static org.gnucash.android.db.DatabaseSchema.RecurrenceEntry;

/**
 * Database adapter for {@link Recurrence} entries
 */
public class RecurrenceDbAdapter extends DatabaseAdapter<Recurrence> {
    /**
     * Opens the database adapter with an existing database
     *
     * @param db        SQLiteDatabase object
     */
    public RecurrenceDbAdapter(SQLiteDatabase db) {
        super(db, RecurrenceEntry.TABLE_NAME, new String[]{
                RecurrenceEntry.COLUMN_MULTIPLIER,
                RecurrenceEntry.COLUMN_PERIOD_TYPE,
                RecurrenceEntry.COLUMN_BYDAY,
                RecurrenceEntry.COLUMN_PERIOD_START,
                RecurrenceEntry.COLUMN_PERIOD_END
        });
    }

    public static RecurrenceDbAdapter getInstance(){
        return GnuCashApplication.getRecurrenceDbAdapter();
    }

    @Override
    public Recurrence buildModelInstance(@NonNull Cursor cursor) {
        String type = cursor.getString(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_PERIOD_TYPE));
        long multiplier = cursor.getLong(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_MULTIPLIER));
        String periodStart = cursor.getString(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_PERIOD_START));
        String periodEnd = cursor.getString(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_PERIOD_END));
        String byDays = cursor.getString(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_BYDAY));

        PeriodType periodType = PeriodType.valueOf(type);

        Recurrence recurrence = new Recurrence(periodType);
        recurrence.setMultiplier((int) multiplier);
        recurrence.setPeriodStart(Timestamp.valueOf(periodStart));
        if (periodEnd != null)
            recurrence.setPeriodEnd(Timestamp.valueOf(periodEnd));
        recurrence.setByDays(stringToByDays(byDays));

        populateBaseModelAttributes(cursor, recurrence);

        return recurrence;
    }

    @Override
    protected @NonNull SQLiteStatement setBindings(@NonNull SQLiteStatement stmt, @NonNull final Recurrence recurrence) {
        stmt.clearBindings();
        stmt.bindLong(1, recurrence.getMultiplier());
        stmt.bindString(2, recurrence.getPeriodType().name());
        if (!recurrence.getByDays().isEmpty())
            stmt.bindString(3, byDaysToString(recurrence.getByDays()));
        //recurrence should always have a start date
        stmt.bindString(4, recurrence.getPeriodStart().toString());

        if (recurrence.getPeriodEnd() != null)
            stmt.bindString(5, recurrence.getPeriodEnd().toString());
        stmt.bindString(6, recurrence.getUID());

        return stmt;
    }

    /**
     * Converts a list of days of week as Calendar constants to an String for
     * storing in the database.
     *
     * @param byDays list of days of week constants from Calendar
     * @return String of days of the week or null if {@code byDays} was empty
     */
    private static @NonNull String byDaysToString(@NonNull List<Integer> byDays) {
        StringBuilder builder = new StringBuilder();
        for (int day : byDays) {
            switch (day) {
                case Calendar.MONDAY:
                    builder.append("MO");
                    break;
                case Calendar.TUESDAY:
                    builder.append("TU");
                    break;
                case Calendar.WEDNESDAY:
                    builder.append("WE");
                    break;
                case Calendar.THURSDAY:
                    builder.append("TH");
                    break;
                case Calendar.FRIDAY:
                    builder.append("FR");
                    break;
                case Calendar.SATURDAY:
                    builder.append("SA");
                    break;
                case Calendar.SUNDAY:
                    builder.append("SU");
                    break;
                default:
                    throw new RuntimeException("bad day of week: " + day);
            }
            builder.append(",");
        }
        builder.deleteCharAt(builder.length()-1);
        return builder.toString();
    }

    /**
     * Converts a String with the comma-separated days of the week into a
     * list of Calendar constants.
     *
     * @param byDaysString String with comma-separated days fo the week
     * @return list of days of the week as Calendar constants.
     */
    private static @NonNull List<Integer> stringToByDays(@Nullable String byDaysString) {
        if (byDaysString == null)
            return Collections.emptyList();

        List<Integer> byDaysList = new ArrayList<>();
        for (String day : byDaysString.split(",")) {
            switch (day) {
                case "MO":
                    byDaysList.add(Calendar.MONDAY);
                    break;
                case "TU":
                    byDaysList.add(Calendar.TUESDAY);
                    break;
                case "WE":
                    byDaysList.add(Calendar.WEDNESDAY);
                    break;
                case "TH":
                    byDaysList.add(Calendar.THURSDAY);
                    break;
                case "FR":
                    byDaysList.add(Calendar.FRIDAY);
                    break;
                case "SA":
                    byDaysList.add(Calendar.SATURDAY);
                    break;
                case "SU":
                    byDaysList.add(Calendar.SUNDAY);
                    break;
                default:
                    throw new RuntimeException("bad day of week: " + day);
            }
        }
        return byDaysList;
    }
}
