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

import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import ch.spherIC.recurvebowsight.database.dao.ArcherySetupDao;
import ch.spherIC.recurvebowsight.database.dao.ArrowDao;
import ch.spherIC.recurvebowsight.database.dao.RiserDao;
import ch.spherIC.recurvebowsight.database.dao.SightDao;
import ch.spherIC.recurvebowsight.database.scheme.data.TblRBSArcherySetup;
import ch.spherIC.recurvebowsight.database.scheme.masterdata.TblRBSArrow;
import ch.spherIC.recurvebowsight.database.scheme.masterdata.TblRBSRiser;
import ch.spherIC.recurvebowsight.database.scheme.masterdata.TblRBSSight;
import ch.spherIC.recurvebowsight.model.IArcherySetup;
import ch.spherIC.recurvebowsight.model.IArrow;
import ch.spherIC.recurvebowsight.model.IRiser;
import ch.spherIC.recurvebowsight.model.ISight;
import ch.spherIC.recurvebowsight.model.impl.ArcherySetup;
import ch.spherIC.recurvebowsight.model.impl.Arrow;
import ch.spherIC.recurvebowsight.model.impl.Riser;
import ch.spherIC.recurvebowsight.model.impl.Sight;


/**
 * See http://www.sqlite.org/lang.html.
 *
 * @author F. Smilari
 */
public final class RBSDatabaseHelper extends SQLiteOpenHelper {

    public static final String DB_NAME = "RecurveBowSight.db";
    public static final int DB_VERSION = 3;

    @SuppressLint("StaticFieldLeak")
    private static RBSDatabaseHelper instance;
    @SuppressLint("StaticFieldLeak")
    private static Context context;
    private static String actualDBName = DB_NAME;
    private boolean databaseCreated = false;


    /**
     * Constructor.
     *
     * @param context {@link Context}
     */
    private RBSDatabaseHelper(final Context context) {
        super(context, actualDBName, null, DB_VERSION);
    }


    public static RBSDatabaseHelper getInstance() {
        if (instance == null) {
            instance = new RBSDatabaseHelper(context);
        }

        return instance;
    }

    /**
     * @param context the context to set
     */
    public static void setContext(final Context context) {
        RBSDatabaseHelper.context = context;
    }

    /**
     * @param dbName the database name to set
     */
    public static void setDB(final String dbName) {
        RBSDatabaseHelper.actualDBName = dbName;
    }


    public void initializeDB() {
        this.databaseCreated = false;
        getWritableDatabase();

        if (this.databaseCreated) {
            insertMasterData();
        }
    }

    @Override
    public void onOpen(final SQLiteDatabase db) {
        super.onOpen(db);
        if (!db.isReadOnly()) {
            db.execSQL("PRAGMA foreign_keys=ON;");
        }
    }

    @Override
    public void onCreate(final SQLiteDatabase db) {

        // Create Tables
        db.execSQL(TblRBSRiser.getCreateStatement());
        db.execSQL(TblRBSSight.getCreateStatement());
        db.execSQL(TblRBSArrow.getCreateStatement());
        db.execSQL(TblRBSArcherySetup.getCreateStatement());

        this.databaseCreated = true;
    }

    /**
     * Insert master data.
     */
    public void insertMasterData() {

        IRiser riserFS;
        ISight sightFS;
        IArrow arrowFS;
        IArcherySetup archerySetup = getArcherySetupMasterData();

        for (IRiser riser : getRiserMasterData()) {
            RiserDao.getInstance().insert(riser);
        }

        for (ISight sight : getSightMasterData()) {
            SightDao.getInstance().insert(sight);
        }

        for (IArrow arrow : getArrowMasterData()) {
            ArrowDao.getInstance().insert(arrow);
        }
        riserFS = RiserDao.getInstance().loadById(1);
        sightFS = SightDao.getInstance().loadById(1);
        arrowFS = ArrowDao.getInstance().loadById(1);
        archerySetup.setRiser(riserFS);
        archerySetup.setSight(sightFS);
        archerySetup.setArrow(arrowFS);
        ArcherySetupDao.getInstance().insert(archerySetup);
    }


    @Override
    public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
        switch (oldVersion) {
            case 1:
                upgradeFromV1ToV2(db);
            case 2:
                upgradeFromV2ToV3(db);
        }
    }

    private void upgradeFromV1ToV2(SQLiteDatabase db) {
        String sql = "Alter table " + TblRBSSight.NAME + " add column " + TblRBSSight.COL_POINTER_OFFSET + " Real NOT NULL DEFAULT 0";
        db.execSQL(sql);
    }

    private void upgradeFromV2ToV3(SQLiteDatabase db) {
        RiserDao.getInstance(db);
        SightDao.getInstance(db);
        ArrowDao.getInstance(db);
        ArcherySetupDao.getInstance(db);
        Map<Long, IArrow> arrowForAS = new HashMap<>();

        // Create new Table RBS_ARROW
        db.execSQL(TblRBSArrow.getCreateStatement());

        // Insert dummy and default master data in new Table RBS_ARROW
        IArrow arrowDummy = ArrowDao.getInstance(db).insert(getArrowDummy());
        for (IArrow arrow : getArrowMasterData()) {
            ArrowDao.getInstance(db).insert(arrow);
        }

        // Insert existing arrow data from Archery setups in new Table RBS_ARROW
        Cursor cursor = db.query(TblRBSArcherySetup.NAME, null, null, null, null, null, null);
        cursor.moveToFirst();
        while (!cursor.isAfterLast()) {
            long asId = cursor.getLong(0);
            double diameter = cursor.getDouble(4);
            double cw = cursor.getDouble(5);
            double weight = cursor.getDouble(6);
            double v0 = cursor.getDouble(7);
            IArrow arrow = new Arrow(0, "Mfct_" + UUID.randomUUID().toString(), "Mdl_" + UUID.randomUUID().toString(),
                                     diameter, weight, cw, v0, "1xweiss/2xrot");
            arrow = ArrowDao.getInstance(db).insert(arrow);
            if (arrow != null) {
                arrowForAS.put(asId, arrow);
            }
            cursor.moveToNext();
        }

        // Upgrade ArcherySetup Table
        db.execSQL(TblRBSArcherySetup.getCreateTempTableStatement_Upd2V3());
        db.execSQL(TblRBSArcherySetup.getInsertIntoTempTable_Upd2V3());
        db.execSQL("Drop Table " + TblRBSArcherySetup.NAME);
        db.execSQL(TblRBSArcherySetup.getCreateStatement());
        db.execSQL(TblRBSArcherySetup.getInsertIntoTable_Upd2V3());
        db.execSQL("Drop Table " + TblRBSArcherySetup.BACKUP_NAME);
        ArrowDao.getInstance(db).delete(arrowDummy);

        // Insert arrow id's in archery setups
        for (Map.Entry<Long, IArrow> entry : arrowForAS.entrySet()) {
            IArcherySetup archerySetup = ArcherySetupDao.getInstance(db).loadById(entry.getKey());
            archerySetup.setArrow(entry.getValue());
            ArcherySetupDao.getInstance(db).update(archerySetup);
        }
    }

    private List<IRiser> getRiserMasterData() {
        List<IRiser> risers = new ArrayList<>();
        risers.add(new Riser(0, "Hoyt", "HPX", 27d, 0d, 61d));
        return risers;
    }

    private List<ISight> getSightMasterData() {
        List<ISight> sights = new ArrayList<>();
        sights.add(new Sight(0, "Avalon", "Tec One", 17.5, -12d, "260,250,240,230,220,210,200,190,180,170,160", 5d, 105d, 0d));
        return sights;
    }

    private List<IArrow> getArrowMasterData() {
        List<IArrow> arrows = new ArrayList<>();
        arrows.add(new Arrow(0, "Easton", "Carbon One 600", 5.8, 21.0, 3.1, 57.6, "1xWeiss,2xBlau"));
        return arrows;
    }

    private IArrow getArrowDummy() {
        return new Arrow(-999, "Dummy", "Dummy", 1.0, 1.0, 0.0, 0., "dummycol");
    }

    private IArcherySetup getArcherySetupMasterData() {
        return new ArcherySetup(0, "FITA Indoor FS", null, null, null, 1.30, 71d, 1.57, 135d, 0.001, 0.1, 55d, 260d, 11d, 195d);
    }
}
