/*
 * Copyright (c) 2014 - 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.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.support.annotation.NonNull;
import android.util.Log;

import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.db.DatabaseSchema;
import org.gnucash.android.model.Recurrence;
import org.gnucash.android.model.ScheduledAction;

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

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

/**
 * Database adapter for fetching/saving/modifying scheduled events
 *
 * @author Ngewi Fet <ngewif@gmail.com>
 */
public class ScheduledActionDbAdapter extends DatabaseAdapter<ScheduledAction> {

    private RecurrenceDbAdapter mRecurrenceDbAdapter;

    public ScheduledActionDbAdapter(SQLiteDatabase db, RecurrenceDbAdapter recurrenceDbAdapter){
        super(db, ScheduledActionEntry.TABLE_NAME,  new String[]{
                ScheduledActionEntry.COLUMN_ACTION_UID        ,
                ScheduledActionEntry.COLUMN_TYPE              ,
                ScheduledActionEntry.COLUMN_START_TIME        ,
                ScheduledActionEntry.COLUMN_END_TIME          ,
                ScheduledActionEntry.COLUMN_LAST_RUN 		  ,
                ScheduledActionEntry.COLUMN_ENABLED           ,
                ScheduledActionEntry.COLUMN_CREATED_AT        ,
                ScheduledActionEntry.COLUMN_TAG               ,
                ScheduledActionEntry.COLUMN_TOTAL_FREQUENCY   ,
                ScheduledActionEntry.COLUMN_RECURRENCE_UID    ,
                ScheduledActionEntry.COLUMN_AUTO_CREATE       ,
                ScheduledActionEntry.COLUMN_AUTO_NOTIFY       ,
                ScheduledActionEntry.COLUMN_ADVANCE_CREATION  ,
                ScheduledActionEntry.COLUMN_ADVANCE_NOTIFY    ,
                ScheduledActionEntry.COLUMN_TEMPLATE_ACCT_UID ,
                ScheduledActionEntry.COLUMN_EXECUTION_COUNT
        });
        mRecurrenceDbAdapter = recurrenceDbAdapter;
        LOG_TAG = "ScheduledActionDbAdapter";
    }

    /**
     * Returns application-wide instance of database adapter
     * @return ScheduledEventDbAdapter instance
     */
    public static ScheduledActionDbAdapter getInstance(){
        return GnuCashApplication.getScheduledEventDbAdapter();
    }

    @Override
    public void addRecord(@NonNull ScheduledAction scheduledAction, UpdateMethod updateMethod) {
        mRecurrenceDbAdapter.addRecord(scheduledAction.getRecurrence(), updateMethod);
        super.addRecord(scheduledAction, updateMethod);
    }

    @Override
    public long bulkAddRecords(@NonNull List<ScheduledAction> scheduledActions, UpdateMethod updateMethod) {
        List<Recurrence> recurrenceList = new ArrayList<>(scheduledActions.size());
        for (ScheduledAction scheduledAction : scheduledActions) {
            recurrenceList.add(scheduledAction.getRecurrence());
        }

        //first add the recurrences, they have no dependencies (foreign key constraints)
        long nRecurrences = mRecurrenceDbAdapter.bulkAddRecords(recurrenceList, updateMethod);
        Log.d(LOG_TAG, String.format("Added %d recurrences for scheduled actions", nRecurrences));

        return super.bulkAddRecords(scheduledActions, updateMethod);
    }

    /**
     * Updates only the recurrence attributes of the scheduled action.
     * The recurrence attributes are the period, start time, end time and/or total frequency.
     * All other properties of a scheduled event are only used for internal database tracking and are
     * not central to the recurrence schedule.
     * <p><b>The GUID of the scheduled action should already exist in the database</b></p>
     * @param scheduledAction Scheduled action
     * @return Database record ID of the edited scheduled action
     */
    public long updateRecurrenceAttributes(ScheduledAction scheduledAction){
        //since we are updating, first fetch the existing recurrence UID and set it to the object
        //so that it will be updated and not a new one created
        RecurrenceDbAdapter recurrenceDbAdapter = new RecurrenceDbAdapter(mDb);
        String recurrenceUID = recurrenceDbAdapter.getAttribute(scheduledAction.getUID(), ScheduledActionEntry.COLUMN_RECURRENCE_UID);

        Recurrence recurrence = scheduledAction.getRecurrence();
        recurrence.setUID(recurrenceUID);
        recurrenceDbAdapter.addRecord(recurrence, UpdateMethod.update);

        ContentValues contentValues = new ContentValues();
        extractBaseModelAttributes(contentValues, scheduledAction);
        contentValues.put(ScheduledActionEntry.COLUMN_START_TIME, scheduledAction.getStartTime());
        contentValues.put(ScheduledActionEntry.COLUMN_END_TIME,  scheduledAction.getEndTime());
        contentValues.put(ScheduledActionEntry.COLUMN_TAG,       scheduledAction.getTag());
        contentValues.put(ScheduledActionEntry.COLUMN_TOTAL_FREQUENCY, scheduledAction.getTotalPlannedExecutionCount());

        Log.d(LOG_TAG, "Updating scheduled event recurrence attributes");
        String where = ScheduledActionEntry.COLUMN_UID + "=?";
        String[] whereArgs = new String[]{scheduledAction.getUID()};
        return mDb.update(ScheduledActionEntry.TABLE_NAME, contentValues, where, whereArgs);
    }

    @Override
    protected @NonNull SQLiteStatement setBindings(@NonNull SQLiteStatement stmt, @NonNull final ScheduledAction schedxAction) {
        stmt.clearBindings();
        stmt.bindString(1, schedxAction.getActionUID());
        stmt.bindString(2, schedxAction.getActionType().name());
        stmt.bindLong(3,   schedxAction.getStartTime());
        stmt.bindLong(4, schedxAction.getEndTime());
        stmt.bindLong(5, schedxAction.getLastRunTime());
        stmt.bindLong(6, schedxAction.isEnabled() ? 1 : 0);
        stmt.bindString(7, schedxAction.getCreatedTimestamp().toString());
        if (schedxAction.getTag() == null)
            stmt.bindNull(8);
        else
            stmt.bindString(8, schedxAction.getTag());
        stmt.bindString(9, Integer.toString(schedxAction.getTotalPlannedExecutionCount()));
        stmt.bindString(10, schedxAction.getRecurrence().getUID());
        stmt.bindLong(11,   schedxAction.shouldAutoCreate() ? 1 : 0);
        stmt.bindLong(12,   schedxAction.shouldAutoNotify() ? 1 : 0);
        stmt.bindLong(13,   schedxAction.getAdvanceCreateDays());
        stmt.bindLong(14,   schedxAction.getAdvanceNotifyDays());
        stmt.bindString(15, schedxAction.getTemplateAccountUID());

        stmt.bindString(16, Integer.toString(schedxAction.getExecutionCount()));
        stmt.bindString(17, schedxAction.getUID());
        return stmt;
    }
    /**
     * Builds a {@link org.gnucash.android.model.ScheduledAction} instance from a row to cursor in the database.
     * The cursor should be already pointing to the right entry in the data set. It will not be modified in any way
     * @param cursor Cursor pointing to data set
     * @return ScheduledEvent object instance
     */
    @Override
    public ScheduledAction buildModelInstance(@NonNull final Cursor cursor){
        String actionUid = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_ACTION_UID));
        long startTime  = cursor.getLong(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_START_TIME));
        long endTime    = cursor.getLong(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_END_TIME));
        long lastRun    = cursor.getLong(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_LAST_RUN));
        String typeString = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_TYPE));
        String tag      = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_TAG));
        boolean enabled = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_ENABLED)) > 0;
        int numOccurrences = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_TOTAL_FREQUENCY));
        int execCount = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_EXECUTION_COUNT));
        int autoCreate = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_AUTO_CREATE));
        int autoNotify = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_AUTO_NOTIFY));
        int advanceCreate = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_ADVANCE_CREATION));
        int advanceNotify = cursor.getInt(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_ADVANCE_NOTIFY));
        String recurrenceUID = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_RECURRENCE_UID));
        String templateActUID = cursor.getString(cursor.getColumnIndexOrThrow(ScheduledActionEntry.COLUMN_TEMPLATE_ACCT_UID));

        ScheduledAction event = new ScheduledAction(ScheduledAction.ActionType.valueOf(typeString));
        populateBaseModelAttributes(cursor, event);
        event.setStartTime(startTime);
        event.setEndTime(endTime);
        event.setActionUID(actionUid);
        event.setLastRun(lastRun);
        event.setTag(tag);
        event.setEnabled(enabled);
        event.setTotalPlannedExecutionCount(numOccurrences);
        event.setExecutionCount(execCount);
        event.setAutoCreate(autoCreate == 1);
        event.setAutoNotify(autoNotify == 1);
        event.setAdvanceCreateDays(advanceCreate);
        event.setAdvanceNotifyDays(advanceNotify);
        //TODO: optimize by doing overriding fetchRecord(String) and join the two tables
        event.setRecurrence(mRecurrenceDbAdapter.getRecord(recurrenceUID));
        event.setTemplateAccountUID(templateActUID);

        return event;
    }

    /**
     * Returns all {@link org.gnucash.android.model.ScheduledAction}s from the database with the specified action UID.
     * Note that the parameter is not of the the scheduled action record, but from the action table
     * @param actionUID GUID of the event itself
     * @return List of ScheduledEvents
     */
    public List<ScheduledAction> getScheduledActionsWithUID(@NonNull String actionUID){
        Cursor cursor = mDb.query(ScheduledActionEntry.TABLE_NAME, null,
                ScheduledActionEntry.COLUMN_ACTION_UID + "= ?",
                new String[]{actionUID}, null, null, null);

        List<ScheduledAction> scheduledActions = new ArrayList<>();
        try {
            while (cursor.moveToNext()) {
                scheduledActions.add(buildModelInstance(cursor));
            }
        } finally {
            cursor.close();
        }
        return scheduledActions;
    }

    /**
     * Returns all enabled scheduled actions in the database
     * @return List of enabled scheduled actions
     */
    public List<ScheduledAction> getAllEnabledScheduledActions(){
        Cursor cursor = mDb.query(mTableName,
                null, ScheduledActionEntry.COLUMN_ENABLED + "=1", null, null, null, null);
        List<ScheduledAction> scheduledActions = new ArrayList<>();
        while (cursor.moveToNext()){
            scheduledActions.add(buildModelInstance(cursor));
        }
        return scheduledActions;
    }

    /**
     * Returns the number of instances of the action which have been created from this scheduled action
     * @param scheduledActionUID GUID of scheduled action
     * @return Number of transactions created from scheduled action
     */
    public long getActionInstanceCount(String scheduledActionUID) {
        String sql = "SELECT COUNT(*) FROM " + DatabaseSchema.TransactionEntry.TABLE_NAME
                + " WHERE " + DatabaseSchema.TransactionEntry.COLUMN_SCHEDX_ACTION_UID + "=?";
        SQLiteStatement statement = mDb.compileStatement(sql);
        statement.bindString(1, scheduledActionUID);
        return statement.simpleQueryForLong();
    }
}
