#include <oxstd.h>
#include <oxfloat.h>
#include "dhtfile.h"

DhtFile::DhtFile()
{
    m_file = m_r = m_c = m_iCptr = 0;
    m_cBlockSize = 1024;
}
DhtFile::~DhtFile()
{
    DhtFile::Close();
}
DhtFile::Close()
{
    fclose(m_file);
    m_file = m_r = m_c = m_iCptr = 0;
}
DhtFile::SetBlockSize(const b)
{
    m_cBlockSize = b;
}

DhtFile::OpenRead(const filename)
{
    decl s, rowsize, headersize, objsize;

                            // use little-endian mode
    m_file = fopen(filename ~ ".dht", "rbe");
    if (!isfile(m_file))
    {
        print("Failed to open ", filename ~ ".dht", "\n");
        return 0;
    }
    m_sFilename = filename;

    fread(m_file, &s, 'c', 2);

    if (s == "\xDA\xEE")
    {
        m_fIs32bit = FALSE;
        fread(m_file, &s, 'c', 2);        // data type 2 byte integer
        objsize = s[0];
        fread(m_file, &s, 'c', 4);        // skip 4 bytes
        fread(m_file, &m_c, 'i');         // columns: 4 byte integer
        fread(m_file, &rowsize, 'i');     // row size: 4 byte integer
        fread(m_file, &headersize, 'i');  // header size: 2 byte integer
    }
    else if (s == "\xDA\xDA")
    {
        m_fIs32bit = TRUE;
        fread(m_file, &s, 'c', 4);        // skip 4 bytes
        fread(m_file, &s, 'c', 2);        // columns: 2 byte integer
        m_c = s[1] * 256 + s[0];
        fread(m_file, &s, 'c', 2);        // row size: 2 byte integer
        rowsize = s[1] * 256 + s[0];
        fread(m_file, &s, 'c', 2);        // header size: 2 byte integer
        headersize = s[1] * 256 + s[0];
        fread(m_file, &s, 'c', 2);        // data type 2 byte integer
        objsize = s[0];
    }
    else
    {   print("This is not a .DHT/.DAT file!\n");
        DhtFile::Close();
        return 0;
    }

    if (objsize != 8)
    {   print("can only handle 8 byte doubles\n");
        DhtFile::Close();
        return 0;
    }
    fseek(m_file, 'c', 128);              // go to start of file names

    decl a;
    m_asNames = new array[0];
    for (; fread(m_file, &s, 'c', 8) > 0; )
    {
        a = new array[1];
        a[0] = s;
        m_asNames ~= a;
    }

    fclose(m_file);

    m_file = fopen(filename ~ ".dat", "rbe"); // open corresponding .DAT file
    if (!isfile(m_file))
    {
        print("Failed to open ", filename ~ ".dat", "\n");
        DhtFile::Close();
        return 0;
    }
    m_r = idiv(fsize(m_file), 8 * m_c);  // derive no of rows from file size

    return 1;
}
DhtFile::Info()
{
    decl i;

    if (!m_c)
    {   print("No file opened\n");
        return;
    }

    print("File \"", m_sFilename, "\" has ", m_r, " rows and ", m_c, " columns.\n");
    print("variable names:\n");
    for (i = 0; i < sizeof(m_asNames); ++i)
        print(m_asNames[i], "\n");
}
DhtFile::Readr(const r)
{
    decl x;

    if (fread(m_file, &x, 'f', r > 0 ? r : m_r, m_c) >= 0)
        return x;

    return 0;
}
DhtFile::Seekr(const r)
{
    if (r >= 0)
        fseek(m_file, 'f', r * m_c);

    return fseek(m_file) / (8 * m_c);
}
DhtFile::Readc(c)
{
    decl i, v, x, fpos, xblock, r;

    if (isstring(c))
        return DhtFile::Readv(c);

    if (c <= 0)
        c = m_c - m_iCptr;

    x = new matrix[m_r][c];
    fpos = fseek(m_file);

    if (m_cBlockSize > 1)
    {
        r = max(idiv(m_cBlockSize, m_c), 4);
		if (r > m_r)  r = m_r;
        fseek(m_file, 'f', 0);

        for (i = 0; i < m_r - r; i += r)
        {
            if (fread(m_file, &xblock, 'f', r, m_c) <= 0)
                break;
            x[i:][] = xblock[][m_iCptr:m_iCptr+c-1];
        }

        if (i < m_r && i >= m_r - r)
        {
            if (fread(m_file, &xblock, 'f', m_r - i, m_c) <= 0)
                break;
            x[i:][] = xblock[][m_iCptr:m_iCptr+c-1];
            i = m_r;
        }
    }
    else
    {
        for (i = 0; i < m_r; ++i)
        {
            fseek(m_file, 'f', i * m_c + m_iCptr);
            if (fread(m_file, &v, 'f', 1, c) <= 0)
                break;
            x[i][] = v;
        }
    }

    if (i < m_r)
        print("Readc error\n");

    fseek(m_file, 'c', fpos);     // rewind to previous position
    m_iCptr += c;

    return x;
}
DhtFile::Seekc(const c)
{
    if (c >= 0)
        m_iCptr = c;

    return m_iCptr;
}
DhtFile::Readv(const s)
{

    decl i = strfind(m_asNames, s);

    if (i < 0)
    {
        print("Variable ", s, " not found\n");
        return 0;
    }
    m_iCptr = i;

    return Readc(1);
}


