// xlsxcell.cpp

#include "xlsxcell.h"

#include "xlsxcell_p.h"
#include "xlsxformat.h"
#include "xlsxformat_p.h"
#include "xlsxutility_p.h"
#include "xlsxworkbook.h"
#include "xlsxworksheet.h"

#include <cmath>

#include <QDate>
#include <QDateTime>
#include <QDebug>
#include <QTime>
#include <QtGlobal>

QT_BEGIN_NAMESPACE_XLSX

CellPrivate::CellPrivate(Cell *p)
    : q_ptr(p)
{
}

CellPrivate::CellPrivate(const CellPrivate *const cp)
    : parent(cp->parent)
    , cellType(cp->cellType)
    , value(cp->value)
    , formula(cp->formula)
    , format(cp->format)
    , richString(cp->richString)
    , styleNumber(cp->styleNumber)
{
}

/*!
  \class Cell
  \inmodule QtXlsx
  \brief The Cell class provides a API that is used to handle the worksheet cell.

*/

/*!
  \enum Cell::CellType
  \value BooleanType      Boolean type
  \value NumberType       Number type, can be blank or used with formula
  \value ErrorType        Error type
  \value SharedStringType Shared string type
  \value StringType       String type, can be used with formula
  \value InlineStringType Inline string type
  */

/*!
 * \internal
 * Created by Worksheet only.
 */
// qint32 styleIndex = (-1)
Cell::Cell(const QVariant &data,
           CellType type,
           const Format &format,
           Worksheet *parent,
           qint32 styleIndex)
    : d_ptr(new CellPrivate(this))
{
    d_ptr->value       = data;
    d_ptr->cellType    = type;
    d_ptr->format      = format;
    d_ptr->parent      = parent;
    d_ptr->styleNumber = styleIndex;
}

/*!
 * \internal
 */
Cell::Cell(const Cell *const cell)
    : d_ptr(new CellPrivate(cell->d_ptr))
{
    d_ptr->q_ptr = this;
}

/*!
 * Destroys the Cell and cleans up.
 */
Cell::~Cell()
{
    if (nullptr != d_ptr)
        delete d_ptr;
}

/*!
 * Return the dataType of this Cell
 */
Cell::CellType Cell::cellType() const
{
    Q_D(const Cell);

    return d->cellType;
}

/*!
 * Return the data content of this Cell
 */
QVariant Cell::value() const
{
    Q_D(const Cell);

    return d->value;
}

/*!
 * Return the data content of this Cell for reading
 */
QVariant Cell::readValue() const
{
    Q_D(const Cell);

    QVariant ret; // return value
    ret = d->value;

    Format fmt = this->format();

    if (isDateTime()) {
        QVariant vDT = dateTime();
        if (vDT.isNull()) {
            return QVariant();
        }

// https://github.com/QtExcel/QXlsx/issues/171
// https://www.qt.io/blog/whats-new-in-qmetatype-qvariant
#if QT_VERSION >= 0x060000 // Qt 6.0 or over
        if (vDT.metaType().id() == QMetaType::QDateTime) {
            ret = vDT;
        } else if (vDT.metaType().id() == QMetaType::QDate) {
            ret = vDT;
        } else if (vDT.metaType().id() == QMetaType::QTime) {
            ret = vDT;
        } else {
            return QVariant();
        }
#else
        if (vDT.type() == QVariant::DateTime) {
            ret = vDT;
        } else if (vDT.type() == QVariant::Date) {
            ret = vDT;
        } else if (vDT.type() == QVariant::Time) {
            ret = vDT;
        } else {
            return QVariant();
        }
#endif

        // QDateTime dt = dateTime();
        // ret = dt;

        // QString strFormat = fmt.numberFormat();
        // if (!strFormat.isEmpty())
        // {
        // 	// TODO: use number format
        // }

        // qint32 styleNo = d->styleNumber;

        // if (styleNo == 10)
        // {
        // }

        // if (styleNo == 11)
        // {
        // QTime timeValue = dt.time(); // only time. (HH:mm:ss)
        // ret = timeValue;
        // return ret;
        // }

        // if (styleNo == 12)
        // {
        // }

        // if (styleNo == 13) // (HH:mm:ss)
        // {
        // double dValue = d->value.toDouble();
        // int day = int(dValue); // unit is day.
        // double deciamlPointValue1 = dValue - double(day);

        // double dHour = deciamlPointValue1 * (double(1.0) / double(24.0));
        // int hour = int(dHour);

        // double deciamlPointValue2 = deciamlPointValue1 - (double(hour) * (double(1.0) /
        // double(24.0))); double dMin = deciamlPointValue2 * (double(1.0) / double(60.0)); int min
        // = int(dMin);

        // double deciamlPointValue3 = deciamlPointValue2 - (double(min) * (double(1.0) /
        // double(60.0))); double dSec = deciamlPointValue3 * (double(1.0) / double(60.0)); int sec
        // = int(dSec);

        // int totalHour = hour + (day * 24);

        // QString strTime;
        // strTime = QString("%1:%2:%3").arg(totalHour).arg(min).arg(sec);
        // ret = strTime;

        // return ret;
        // }

        // return ret;
        // */
    }

    if (hasFormula()) {
        QString formulaString = this->formula().formulaText();
        ret                   = formulaString;
        return ret; // return formula string
    }

    return ret;
}

/*!
 * Return the style used by this Cell. If no style used, 0 will be returned.
 */
Format Cell::format() const
{
    Q_D(const Cell);

    return d->format;
}

/*!
 * Returns true if the cell has one formula.
 */
bool Cell::hasFormula() const
{
    Q_D(const Cell);

    return d->formula.isValid();
}

/*!
 * Return the formula contents if the dataType is Formula
 */
CellFormula Cell::formula() const
{
    Q_D(const Cell);

    return d->formula;
}

/*!
 * Returns whether the value is probably a dateTime or not
 */
bool Cell::isDateTime() const
{
    Q_D(const Cell);

    Cell::CellType cellType = d->cellType;
    double dValue           = d->value.toDouble(); // number
    //	QString strValue = d->value.toString().toUtf8();
    bool isValidFormat    = d->format.isValid();
    bool isDateTimeFormat = d->format.isDateTimeFormat(); // datetime format

    // dev67
    if (cellType == NumberType || cellType == DateType || cellType == CustomType) {
        if (dValue >= 0 && isValidFormat && isDateTimeFormat) {
            return true;
        }
    }

    return false;
}

/*!
 * Return the data time value.
 */
/*
QDateTime Cell::dateTime() const
{
        Q_D(const Cell);

        if (!isDateTime())
                return QDateTime();

        return datetimeFromNumber(d->value.toDouble(), d->parent->workbook()->isDate1904());
}
*/
QVariant Cell::dateTime() const
{
    Q_D(const Cell);

    if (!isDateTime()) {
        return QVariant();
    }

    // dev57

    QVariant ret;
    double dValue   = d->value.toDouble();
    bool isDate1904 = d->parent->workbook()->isDate1904();
    ret             = datetimeFromNumber(dValue, isDate1904);
    return ret;
}

/*!
 * Returns whether the cell is probably a rich string or not
 */
bool Cell::isRichString() const
{
    Q_D(const Cell);

    if (d->cellType != SharedStringType && d->cellType != InlineStringType &&
        d->cellType != StringType) {
        return false;
    }

    return d->richString.isRichString();
}

qint32 Cell::styleNumber() const
{
    Q_D(const Cell);

    qint32 ret = d->styleNumber;
    return ret;
}

bool Cell::isDateType(CellType cellType, const Format &format)
{
    if (cellType == NumberType || cellType == DateType || cellType == CustomType) {
        return format.isValid() && format.isDateTimeFormat();
    }
    return false;
}

QT_END_NAMESPACE_XLSX
