/*
 * Copyright (C) 2022 - Florindo Smilari (spherIC)
 */
package ch.spherIC.recurvebowsight.database.dao;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

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

import ch.spherIC.recurvebowsight.database.RBSDatabaseHelper;
import ch.spherIC.recurvebowsight.model.IEntity;


/**
 * DAO-Vaterklasse.
 *
 * @author F. Smilari
 */
public abstract class DAO<E extends IEntity> {

    private Optional<SQLiteDatabase> db = Optional.empty();

    /**
     * Konstruktor.
     */
    public DAO() {
        super();
    }

    /**
     * Konstruktor.
     */
    public DAO(SQLiteDatabase db) {
        super();
        setDb(db);
    }


    /**
     * @return the db
     */
    protected SQLiteDatabase getDb() {
        return this.db.isPresent() ? db.get() : RBSDatabaseHelper.getInstance().getWritableDatabase();
    }

    protected void setDb(SQLiteDatabase db) {
        this.db = Optional.of(db);
    }

    protected abstract E cursorToEntity(final Cursor cursor);

    protected abstract ContentValues getContentValues(E entity);

    protected abstract String getTableName();

    protected abstract String getIdColumn();


    /**
     * Lädt die Entität über eine Id.
     *
     * @param id
     * @param db
     * @return E
     */
    public E loadById(final long id, SQLiteDatabase db) {
        E entity;
        Cursor cursor = db.query(getTableName(), null, getIdColumn() + " = " + id, null, null, null, null);
        if (cursor.getCount() > 0) {
            cursor.moveToFirst();
            entity = cursorToEntity(cursor);
            cursor.close();
            return entity;
        }
        return null;
    }

    /**
     * Lädt die Entität über eine Id.
     *
     * @param id
     * @return E
     */
    public E loadById(final long id) {
        E entity;
        Cursor cursor = getDb().query(getTableName(), null, getIdColumn() + " = " + id, null, null, null, null);
        if (cursor.getCount() > 0) {
            cursor.moveToFirst();
            entity = cursorToEntity(cursor);
            cursor.close();
            return entity;
        }
        return null;
    }

    /**
     * Lädt alle Entitäten.
     *
     * @return
     */
    public List<E> loadAll() {
        List<E> entities = new ArrayList<E>();
        Cursor cursor = getDb().query(getTableName(), null, null, null, null, null, getOrderBy());
        cursor.moveToFirst();

        while (!cursor.isAfterLast()) {
            E entity = cursorToEntity(cursor);
            entities.add(entity);
            cursor.moveToNext();
        }

        cursor.close();
        return entities;
    }

    /**
     * Lädt alle Entitäten.
     *
     * @return
     */
    public List<E> loadAll(SQLiteDatabase db) {
        List<E> entities = new ArrayList<E>();
        Cursor cursor = db.query(getTableName(), null, null, null, null, null, getOrderBy());
        cursor.moveToFirst();

        while (!cursor.isAfterLast()) {
            E entity = cursorToEntity(cursor);
            entities.add(entity);
            cursor.moveToNext();
        }

        cursor.close();
        return entities;
    }

    /**
     * Fügt eine Entität in die DB ein.
     *
     * @param entity
     * @return
     */
    public E insert(final E entity, SQLiteDatabase db) {
        ContentValues values = getContentValues(entity);
        long insertId = db.insert(getTableName(), null, values);
        return insertId == -1 ? null : loadById(insertId, db);
    }

    /**
     * Fügt eine Entität in die DB ein.
     *
     * @param entity
     * @return
     */
    public E insert(final E entity) {
        ContentValues values = getContentValues(entity);
        long insertId = getDb().insert(getTableName(), null, values);
        return insertId == -1 ? null : loadById(insertId);
    }

    /**
     * Updated eine Entität in der DB ein.
     *
     * @param entity
     * @param db
     * @return
     */
    public E update(final E entity, SQLiteDatabase db) {
        ContentValues values = getContentValues(entity);
        db.update(getTableName(), values, getIdColumn() + " = " + entity.getId(), null);
        return loadById(entity.getId(), db);
    }

    /**
     * Updated eine Entität in der DB ein.
     *
     * @param entity
     * @return
     */
    public E update(final E entity) {
        ContentValues values = getContentValues(entity);
        getDb().update(getTableName(), values, getIdColumn() + " = " + entity.getId(), null);
        return loadById(entity.getId());
    }

    /**
     * Löscht eine Entität aus der DB.
     *
     * @param entity
     * @param db
     */
    public void delete(final E entity, SQLiteDatabase db) {
        db.delete(getTableName(), getIdColumn() + " = " + entity.getId(), null);
    }

    /**
     * Löscht eine Entität aus der DB.
     *
     * @param entity
     */
    public void delete(final E entity) {
        getDb().delete(getTableName(), getIdColumn() + " = " + entity.getId(), null);
    }

    protected String getOrderBy() {
        return null;
    }
}
