#include <SQLiteCommand.h>
#include <stdlib.h>

#include "dataprovider.h"
#include "sphericalgeometry.h"

DataProvider* DataProvider::instance = NULL;

DataProvider& DataProvider::getInstance()
{
    if (!instance)
    {
        instance = new DataProvider("\\Storage Card\\speedcam_pl.db");
    }
    return *instance;
}

DataProvider::DataProvider(char* dbname)
    : log(0)
{
    db = new Database();
    db->setDBPath(dbname);
    db->open();
}

void DataProvider::close()
{
    delete db;
    delete instance;
    instance = NULL; 
}

SpeedTrap* DataProvider::getNearestSpeedTrap(float longitude, float latitude, float maxd, float& nd)
{
    int keys[] = {0, 0, 0, 0};

    int ilon = (int)(longitude * 1e7);
    int ilat = (int)(latitude * 1e7);
    int keyx = (ilon / 10000000) & 0xFFFF;
    int keyy = (ilat / 10000000) & 0xFFFF;
    int nkx = (ilon - keyx*10000000 < 5000000) ? keyx - 1 : keyx + 1;
    int nky = (ilat - keyy*10000000 < 5000000) ? keyy - 1 : keyy + 1;

    keys[0] = keyx | (keyy << 16);
    keys[1] = keyx | (nky << 16);
    keys[2] = nkx | (keyy << 16);
    keys[3] = nkx | (nky << 16);

    int qx = (int)latitude;
    int qy = (int)longitude;
    int quadId = (qx << 16) | (qy & 0xFFFF);

    SpeedTrap* nearest = NULL;
    float dmin = maxd;
    for (int k = 0; k < 4; k++)
    {   
        int key = keys[k];
        if (log)
            fprintf(log, "Key: %08X\n", key);
        int cidx = -1;
        for (int i = 0; i < cache.size(); ++i)
        {
            if (cache[i].first == key)
            {
                cidx = i;
                break;
            }
        }
        if (cidx < 0)
            cidx = bufferData(key);
        
        vector<SpeedTrap>* data = cache[cidx].second;
        if (log)
            fprintf(log, "Data");
        for (int i = 0; i < data->size(); ++i)
        {
            SpeedTrap* st = &(*data)[i];
            float d = SphericalGeometry::getDistance(longitude, latitude, 
                st->getLongitude(), st->getLatitude());
            if (d <= dmin)
            {
                nearest = st;
                dmin = d;
                nd = d;
            }
        }
    }
    
    return nearest;
}

int DataProvider::bufferData(int quadId)
{
    int cnt = 0;
    
    QuadData* qd = new QuadData(quadId, new vector<SpeedTrap>());
    cache.push_back(*qd);
    int idx = cache.size() - 1;
    
    char query[256];
    sprintf(query, "select * from speedcam where quad_id = %d", quadId);
    SQLiteCommand cmd(query, db->getConnection());
    SQLiteDataReader reader = cmd.ExecuteReader();
    
    while (reader.Read()) 
    {
        SpeedTrap* st = new SpeedTrap();
        
        st->id = atoi(reader["_id"]);
        st->longitude = atoi(reader["longitude"]) / 1e7;
        st->latitude = atoi(reader["latitude"]) / 1e7;
        st->speed = atoi(reader["speed"]);
        st->type = (SpeedTrapType)atoi(reader["type"]);
        st->direction = atoi(reader["direction"]);
        st->dirType = (SpeedTrapDirType)atoi(reader["dir_type"]);
                
        qd->second->push_back(*st);
        cnt++;
    }
    if (log)
        fprintf(log, "%d records loaded\n", cnt);
    reader.Close();
    
    return idx;
}

