/***************************************************************************

    source::worx xtree
    Copyright © 2024-2025 c.holzheuer
    christoph.holzheuer@gmail.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

***************************************************************************/



//#include <cmath>
#include <xqitemtype.h>

#include <QDateTime>
#include <QDebug>

XQItemTypeMap XQItemType::s_ItemTypeMap;
size_t XQItemType::s_ItemTypeCount = 0;

///
/// XQItemType
/// 


XQItemType::XQItemType()
  : XQItem(nullptr) // vermeide rekursion
{
}


XQItemType::XQItemType( const XQItemType& other)
  : XQItem( other )
{
}


//! destruktor, räumt das fixedChoices auf, so vorhanden.

XQItemType::~XQItemType()
{
  // das einzige property, was auch auf dem heap liegt.
  QStandardItemModel* choices = fixedChoices();
  if( choices )
  {
    setfixedChoices( nullptr );
    delete choices;
  }
}


//! ruft QStandardItem::data auf

QVariant XQItemType::data( int role ) const
{
  return QStandardItem::data(role);
}


//! ruft QStandardItem::setData auf

void XQItemType::setData(const QVariant& value, int role )
{
  //qDebug() << " --- itemType set Data:" << role << " : " << value.toString();
  return QStandardItem::setData(value,role);
}

//! tested, ob ein attribute (z.B. unitType) hier vorhanden ist
int XQItemType::roleForAttributeKey( const QString& attrKey )
{
  int role = XQItem::fetchItemDataRole(attrKey);
  // gibbed überhaupt eine rolle für unser attribut?
  if( role != XQItem::NoRole)
  {
    // wenn ja, ist die role hier besetzt?
    QVariant value = data(role);
    if( !value.isValid() || value.isNull() )
      return XQItem::NoRole;
  }
  return role;
}


//! setzt einen attributwert neu. Ggf. wird ein neuer ItemType erzeugt.

XQItemType* XQItemType::replaceAttribute( const QVariant& newValue, int role )
{

  QString msg = newValue.toString();
  if( role == XQItem::IconRole )
  {
    QIcon icn = newValue.value<QIcon>();
    msg = XQAppData::iconName( icn );
  }

  //qDebug() << " --- Before: " << makeItemTypeKey() << " role:" << XQItem::fetchItemDataRoleName(role) << " value:" << msg;

  // hat sich überhaupt was geändert?
  QVariant oldValue = data(role);

  // nein, es hat nicht
  if( oldValue == newValue )
    return this;

  // kopie von mir
  XQItemType* myClone = new XQItemType(*this);
  // Änderungen übernehmen
  myClone->setData( newValue, role );
  // Gibt es den geänderten ItemType schon?
  QString newKey = myClone->makeItemTypeKey();
  // jawoll

  if( s_ItemTypeMap.contains( newKey ) )
  {
    // abräumen ...
    delete myClone;
    // und die alte version zurückgegen
    return s_ItemTypeMap[newKey];
  }

  // speichern
  s_ItemTypeMap.insert( newKey, myClone );

  //qDebug() << " --- After:  " << myClone->makeItemTypeKey();

  /// Obacht! Der alte, geänderte itemType bleibt erhalten
  /// und verrottet ggf. ohne Daseinszweck

  return myClone;

}


//! formatiert einen zahlenwert als string mit einheit.

QString XQItemType::formatToSI( const QString& valueTxt, XQItem::UnitType unitType )
{

  if( valueTxt.isEmpty() )
    return valueTxt;

  if( XQItem::ISODate == unitType )
  {
    // format iso date
    QDateTime dateTime = QDateTime::fromString(valueTxt, Qt::ISODate);
    // fixme! make this configurable!
    QString format = "dd.MM.yyyy HH:mm:ss"; // You can customize this format
    // Format the QDateTime object into a human-readable string
    return dateTime.toString(format);
  }

  QLocale sysLocale = QLocale::system();
  sysLocale.setNumberOptions(sysLocale.numberOptions() | QLocale::OmitGroupSeparator);

  double dVal =  sysLocale.toDouble(valueTxt);
  QString strVal, strPrefix;

  int exp = (int)::log10f(dVal);
  exp = (exp/3)*3;

  double nVal = dVal;
  if( !s_PrefixExponentMap.key(exp).isEmpty()  )
    nVal /= ::pow( 10,exp);
  strVal =  sysLocale.toString(nVal, 'f', 2);
  strPrefix =  s_PrefixExponentMap.key(exp);
  //qDebug() << " convert: " << dVal << " : " << valueTxt << ": " << strVal  << ":" << exp  << " : " << strPrefix << ": " << nVal;
  QString unitStr = XQItem::fetchUnitTypeToString( unitType);
  return QString("%1 %2%3").arg( strVal, strPrefix, unitStr );

}


//! entfernt die einheit aus einem formatierten string

QString XQItemType::unFormatFromSI(const QString& formText )
{

  const QString input = formText.simplified();
  // #1: strip numeric part
  if( input.isEmpty() )
    return input;

  int idx = 0;
  for( auto c : input )
  {
    if( c.isNumber() || c.toLower() == 'e' || c == '.' ||  c == ',' ||c == '-' || c == '+' )
      idx++;
    else
      break;
  }

  if(!idx)
    return QString("0");

  QString numPart = formText.left(idx);
  QString unitPart;
  //if(idx + 1 < formText.size() )
  unitPart = formText.right(idx - 1).simplified();

  QLocale sysLocale = QLocale::system();
  double dVal =  sysLocale.toDouble(numPart);
  if( unitPart.size() > 0 )
  {
    QString prefix = QString(unitPart[0]);
    if( s_PrefixExponentMap.contains(prefix) )
      dVal *= std::pow( 10.0, s_PrefixExponentMap[prefix] );
  }

  sysLocale.setNumberOptions(sysLocale.numberOptions() | QLocale::OmitGroupSeparator);
  QString result = sysLocale.toString(dVal, 'f', 2);

  //qDebug() << " convert: " << numPart << " : " << unitPart << " : " << dVal << " : " << result;

  return result;

}

///
/// --- statics --------------------------------------------------------------------------
///

//! gibt den dummy item type zurück (benutzt für null-items).

XQItemType* XQItemType::staticItemType()
{
  static XQItemType s_DummyItemType;
  return &s_DummyItemType;
}


//! erzeugt aus den eingenschaften des itemTypes einen eindeutigen schlüssel.

QString XQItemType::makeItemTypeKey()
{
  QString key("%1:%2:%3:%4:%5:%6:%7");

  key = key.arg( renderStyleToString() );
  key = key.arg( editorTypeToString() );
  key = key.arg( unitTypeToString() );
  key = key.arg( contentFormat() );
  key = key.arg( itemFlagsToString() );
  // icons haben leider keine namen, es sei denn, sie kommen aus einen theme
  //key = key.arg( icon().name() );
  //key = key.arg( icon().cacheKey() );
  key = key.arg( XQAppData::iconName( icon() ) );
  key = key.arg( fixedChoicesToString() );

  return key;
}






