package cgmblelib.database.source.db;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;

import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import cgmblelib.database.dao.DaoGlucose;
import cgmblelib.database.entity.PocGlucose;
import cgmblelib.database.source.SourceGlucose;
import cgmblelib.utils.AppExecutors;

/**
 * Created by minyuchun on 2019/10/25.
 *
 * @author minyuchun
 */

public class GlucoseDBDataSource extends DBDataSource implements SourceGlucose {
    /**
     * 单例
     */
    private static volatile GlucoseDBDataSource INSTANCE;
    /**
     * dao
     */
    private DaoGlucose mDaoGlucose;
    /**
     * 线程池
     */
    private AppExecutors mAppExecutors;


    private GlucoseDBDataSource(@NonNull AppExecutors appExecutors,
                                @NonNull DaoGlucose daoGlucose) {
        mAppExecutors = appExecutors;
        mDaoGlucose = daoGlucose;
    }

    public static GlucoseDBDataSource getInstance(@NonNull AppExecutors appExecutors, @NonNull DaoGlucose daoGlucose) {
        if (INSTANCE == null) {
            synchronized (GlucoseDBDataSource.class) {
                if (INSTANCE == null) {
                    INSTANCE = new GlucoseDBDataSource(appExecutors, daoGlucose);
                }
            }
        }
        return INSTANCE;
    }

    @Override
    public void delete(long deviceId, int glucoseId) {
        mDaoGlucose.removeAll(deviceId, glucoseId);
    }

    @Override
    public long insertGlucoseIoThread(PocGlucose glucose) {
        try {
            CheckIoThread();
            return mDaoGlucose.insert(glucose);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    @Override
    public long[] insertGlucosesIoThread(@NonNull PocGlucose... glucose) {
        try {
            CheckIoThread();
            return mDaoGlucose.insertAll(glucose);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int updateGlucosesIoThread(@NonNull PocGlucose... glucose) {
        try {
            CheckIoThread();
            return mDaoGlucose.update(glucose);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public LiveData<List<PocGlucose>> getLiveDataGlucoseListByDeviceId(long deviceId) {
        return mDaoGlucose.getAllLiveDataGlucosesByDeviceId(deviceId);
    }

    @Override
    public PocGlucose getLatestGlucoseByDeviceIdIoThread(long deviceId) {
        try {
            CheckIoThread();
            return mDaoGlucose.getLatestGlucose(deviceId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<PocGlucose> getGlucosesByIdIoThread(long deviceId) {
        try {
            CheckIoThread();
            return mDaoGlucose.getAllGlucosesByDeviceId(deviceId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<PocGlucose> getGlucosesByIdIoThreadDesc(long deviceId) {
        try {
            CheckIoThread();
            return mDaoGlucose.getAllGlucosesByDeviceIdDesc(deviceId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<PocGlucose> getGlucoseByTimeMillis(long startTime, long endTime) {
        try {
            CheckIoThread();
            return mDaoGlucose.getGlucoseByTimeMillis(startTime, endTime);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<PocGlucose> getEffectiveGlucosesByIdIoThread(long deviceId) {
        try {
            CheckIoThread();
            return mDaoGlucose.getAllEffectiveGlucosesByDeviceId(deviceId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    @Override
    public PocGlucose getGlucoseByIdIoThread(long deviceId, int glucoseId) {
        try {
            CheckIoThread();
            return mDaoGlucose.getGlucoseById(deviceId, glucoseId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public PocGlucose getGlucoseByTimeIoThread(long deviceId, long timeMillis) {
        try {
            CheckIoThread();
            return mDaoGlucose.getGlucoseByTimeIoThread(deviceId, timeMillis);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int getMaxGlucoseIdIoThread(long deviceId) {
        try {
            CheckIoThread();
            if (getCountGlucoseIoThread(deviceId) > 0) {
                return mDaoGlucose.getMaxGlucoseId(deviceId);
            } else {
                return -1;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }


    @Override
    public int getCountGlucoseIoThread(long deviceId) {
        try {
            CheckIoThread();
            return mDaoGlucose.getCountGlucose(deviceId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    @Override
    public float[] getIwsIoThread(long deviceId, float Iw) {
        CheckIoThread();
        float[] databaseIws = mDaoGlucose.getIws(deviceId);
        float[] Iws = Arrays.copyOfRange(databaseIws, 0, databaseIws.length + 1);
        Iws[databaseIws.length] = Iw;
        return Iws;
    }

    @Override
    public float[] getIbsIoThread(long deviceId, float Ib) {
        CheckIoThread();
        float[] databaseIbs = mDaoGlucose.getIbs(deviceId);
        float[] Ibs = Arrays.copyOfRange(databaseIbs, 0, databaseIbs.length + 1);
        Ibs[databaseIbs.length] = Ib;
        return Ibs;
    }

    @Override
    public float[] getTsIoThread(long deviceId, float T) {
        CheckIoThread();
        float[] databaseTs = mDaoGlucose.getTs(deviceId);
        float[] Ts = Arrays.copyOfRange(databaseTs, 0, databaseTs.length + 1);
        Ts[databaseTs.length] = T;
        return Ts;
    }

    @Override
    public List<PocGlucose> getGlucoseByDeviceIdAndTimeMillis(long deviceId, long timeMillisStart) {
        try {
            CheckIoThread();
            return mDaoGlucose.getGlucoseByDeviceIdAndTimeMillis(deviceId, timeMillisStart);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int[] getYearsIoThread(long deviceId) {
        CheckIoThread();
        return mDaoGlucose.getYears(deviceId);
    }

    @Override
    public int[] getMonthsInYearIoThread(long deviceId, int year) {
        CheckIoThread();
        return mDaoGlucose.getMonths(deviceId, year);
    }

    @Override
    public int[] getDays(long deviceId, int year, int month) {
        CheckIoThread();
        return mDaoGlucose.getDays(deviceId, year, month);
    }

    @Override
    public List<PocGlucose> getEveryDays(long deviceId, int year, int month, int day) {
        CheckIoThread();
        return mDaoGlucose.getEveryDays(deviceId, year, month, day);
    }

    @Override
    public List<PocGlucose> getStartDayList(long deviceId, int year, int month, int day, int hour) {
        CheckIoThread();
        return mDaoGlucose.getStartDayGlucose(deviceId, year, month, day, hour);
    }

    @Override
    public List<PocGlucose> getEndDayList(long deviceId, int year, int month, int day, int hour) {
        CheckIoThread();
        return mDaoGlucose.getEndDayGlucose(deviceId, year, month, day, hour);
    }

    @Override
    public List<PocGlucose> getAllGlucoseByBetweenTime(long deviceId, Date startTime, Date endTime) {
        CheckIoThread();
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.setTime(startTime);
        int startYear = calendar.get(Calendar.YEAR);
        int startMonth = calendar.get(Calendar.MONTH);
        int startDay = calendar.get(Calendar.DAY_OF_MONTH);
        int startHour = calendar.get(Calendar.HOUR_OF_DAY);

        calendar.setTime(endTime);

        int endYear = calendar.get(Calendar.YEAR);
        int endMonth = calendar.get(Calendar.MONTH);
        int endDay = calendar.get(Calendar.DAY_OF_MONTH);
        int endHour = calendar.get(Calendar.HOUR_OF_DAY);

        int startIdBetweenTime = mDaoGlucose.getStartIdBetweenTime(deviceId, startYear, startMonth + 1, startDay, startHour);
        int endIdBetweenTime = mDaoGlucose.getEndIdBetweenTime(deviceId, endYear, endMonth + 1, endDay, endHour);

        return mDaoGlucose.getGlucoseBetweenTime(deviceId, startIdBetweenTime, endIdBetweenTime);
    }

    @Override
    public List<PocGlucose> getEveryPageData(long deviceId, int page, int pageSize) {
        int offset = (page - 1) * pageSize;
        return mDaoGlucose.getPageData(deviceId, pageSize, offset);
    }

    @Override
    public List<PocGlucose> getLatestPageData(long deviceId, int pageSize) {
        return mDaoGlucose.getLatestPageData(deviceId, pageSize);
    }

    @Override
    public List<PocGlucose> getAllGlucoseDayData(String name) {
        return mDaoGlucose.getGlucoseDayByName(name);
    }

    @Override
    public int getPageCountsIoThread(long deviceId, int pageSize) {
        CheckIoThread();
        int count = mDaoGlucose.getCountPreheatEndByDeviceIdIoThread(deviceId);
        return count % pageSize == 0 ? (count / pageSize) : (count / pageSize + 1);
    }

    @Override
    public void deleteAllGlucoseData() {
        try {
            Runnable deleteRunnable = () -> mDaoGlucose.clearGlucoseTable();
            mAppExecutors.diskIO().execute(deleteRunnable);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
