// Created on: 2020-01-25
// Created by: Natalia ERMOLAEVA
// Copyright (c) 2020 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement. 

#include <inspector/TreeModel_ItemProperties.hxx>
#include <inspector/TreeModel_ItemStream.hxx>
#include <inspector/Convert_Tools.hxx>
#include <inspector/Convert_TransientShape.hxx>

#include <BRepBuilderAPI_MakeVertex.hxx>
#include <gp_XYZ.hxx>
#include <Quantity_ColorRGBA.hxx>
#include <Standard_Dump.hxx>

#include <Standard_WarningsDisable.hxx>
#include <QApplication>
#include <QFont>
#include <Standard_WarningsRestore.hxx>

IMPLEMENT_STANDARD_RTTIEXT(TreeModel_ItemProperties, Standard_Transient)

// =======================================================================
// function : Init
// purpose :
// =======================================================================
void TreeModel_ItemProperties::Init ()
{
  myRowValues.Clear();

  InitByStream (Item()->Stream());
}

// =======================================================================
// function : InitByStream
// purpose :
// =======================================================================
void TreeModel_ItemProperties::InitByStream (const Standard_SStream& aStream)
{
  NCollection_IndexedDataMap<TCollection_AsciiString, Standard_DumpValue> aValues;
  TCollection_AsciiString aStreamText = Standard_Dump::Text (aStream);
  Standard_Dump::SplitJson (aStreamText, aValues);

  TreeModel_ItemStreamPtr aStreamParent = itemDynamicCast<TreeModel_ItemStream>(Item());
  TCollection_AsciiString aKey;
  Standard_DumpValue aKeyValue;
  if (!aStreamParent)
  {
    if (!Item() || Item()->Object().IsNull())
    {
      aKey = "Dump";
    }
    else
    {
      const Handle(Standard_Transient)& anItemObject = Item()->Object();
      aKey = anItemObject.IsNull() ? "Dump" : anItemObject->DynamicType()->Name();
    }
    aKeyValue = Standard_DumpValue (aStreamText, 1);

    myKey = aKey;
    myStreamValue = aKeyValue;
  }
  else
  {
    TCollection_AsciiString aValue;
    if (Item()->Parent())
    {
      const Handle(TreeModel_ItemProperties)& aParentProperties = Item()->Parent()->Properties();
      if (aParentProperties)
        aParentProperties->ChildStream (Item()->Row(), aKey, aKeyValue);
    }
    myKey = aKey;
    myStreamValue = aKeyValue;

    aValues.Clear();
    Standard_Dump::SplitJson (myStreamValue.myValue, aValues);
  }

  for (Standard_Integer anIndex = 1; anIndex <= aValues.Size(); anIndex++)
  {
    Standard_DumpValue aValue = aValues.FindFromIndex (anIndex);
    if (Standard_Dump::HasChildKey (aValue.myValue))
      myChildren.Add (aValues.FindKey (anIndex), aValue);
    else
    {
      TreeModel_RowValue aRowValue (aValue.myStartPosition, aValues.FindKey (anIndex).ToCString(), aValue.myValue.ToCString());
      myRowValues.Add (myRowValues.Size() + 1, aRowValue);
    }
  }
  if (myRowValues.Size() == 1)
  {
    Quantity_Color aColor;
    if (Convert_Tools::ConvertStreamToColor (aStream, aColor))
    {
      Standard_Real aRed, aGreen, aBlue;
      aColor.Values (aRed, aGreen, aBlue, Quantity_TOC_sRGB);

      int aDelta = 255;
      myRowValues.ChangeFromIndex (1).CustomValues.insert ((int)Qt::BackgroundRole, QColor((int)(aRed * aDelta),
        (int)(aGreen * aDelta), (int)(aBlue * aDelta)));
    }
  }
  // in case if the stream alert has empty key avalue, use as the key the first row value
  if ((myKey.IsEmpty() || myKey.IsEqual ("Dump")) && myRowValues.Size() > 0)
  {
    myKey = myRowValues.FindFromIndex (1).Value.toString().toStdString().c_str();
  }
}

// =======================================================================
// function :  Reset
// purpose :
// =======================================================================
void TreeModel_ItemProperties::Reset()
{
  myKey = "";
  myStreamValue = Standard_DumpValue();

  myChildren.Clear();
  myRowValues.Clear();
}

// =======================================================================
// function : RowCount
// purpose :
// =======================================================================
int TreeModel_ItemProperties::RowCount() const
{
  return RowValues().Size();
}

// =======================================================================
// function : Data
// purpose :
// =======================================================================
QVariant TreeModel_ItemProperties::Data (const int theRow, const int theColumn, int theRole) const
{
  if (theColumn == 1 && theRole == Qt::BackgroundRole)
  {
    const QMap<int, QVariant>& aCachedValues = RowValues().FindFromIndex (theRow + 1).CustomValues;
    if (aCachedValues.contains ((int)theRole))
      return aCachedValues[(int)theRole];
  }

  if (theRole == Qt::FontRole) // method name is in italic
  {
    if (Data(theRow, 0, Qt::DisplayRole).toString().contains("className"))
    {
      QFont aFont = qApp->font();
      aFont.setItalic (true);
      return aFont;
    }
  }
  if (theRole == Qt::ForegroundRole)
  {
    if (Data(theRow, 0, Qt::DisplayRole).toString().contains("className"))
      return QColor (Qt::darkGray).darker(150);
  }

  if (theRole == Qt::DisplayRole || theRole == Qt::ToolTipRole)
  {
    if (theColumn == 0) return RowValues().FindFromIndex (theRow + 1).Key;
    else if (theColumn == 1) return RowValues().FindFromIndex (theRow + 1).Value;
  }

  return QVariant();
}

// =======================================================================
// function : EditType
// purpose :
// =======================================================================
ViewControl_EditType TreeModel_ItemProperties::EditType (const int, const int theColumn) const
{
  if (theColumn == 0)
    return ViewControl_EditType_None;

  Quantity_Color aColor;
  if (Convert_Tools::ConvertStreamToColor (Item()->Stream(), aColor))
  {
    return ViewControl_EditType_Color;
  }
  return ViewControl_EditType_Line;
}

// =======================================================================
// function : ReplaceValue
// purpose :
// =======================================================================
Standard_Boolean ReplaceValue (const TCollection_AsciiString& theFromValue,
                               const TCollection_AsciiString& theToValue,
                               Standard_DumpValue& theStreamValue)
{
  TCollection_AsciiString aStreamValue = theStreamValue.myValue;

  int aPosition = aStreamValue.FirstLocationInSet (theFromValue, 1, aStreamValue.Length());
  if (aPosition < 1)
    return Standard_False;

  aPosition += 2; // due to 'FirstLocationInSet' returns position taking into account '"\' as 1 position

  TCollection_AsciiString aPartStart = aStreamValue.SubString(1, aPosition - 1);
  TCollection_AsciiString aPartFinal = aStreamValue.SubString(aPosition + theFromValue.Length(),
                                                              aStreamValue.Length());
  theStreamValue.myValue = aPartStart + theToValue + aPartFinal;

  return Standard_True;
}

// =======================================================================
// function : SetData
// purpose :
// =======================================================================
bool TreeModel_ItemProperties::SetData (const int theRow, const int theColumn, const QVariant& theValue, int theRole)
{
  if (theColumn == 0)
    return false;

  if (theRole != Qt::DisplayRole && theRole != Qt::EditRole)
    return false;

  if (myRowValues.Size() == 1 && theColumn == 1)
  {
    TCollection_AsciiString aStreamValue (theValue.toString().toStdString().c_str());
    NCollection_IndexedDataMap<TCollection_AsciiString, Standard_DumpValue> aKeyToValues;
    if (Standard_Dump::SplitJson (aStreamValue, aKeyToValues))
    {
      Standard_SStream aStream;
      aStream << aStreamValue.ToCString();

      int aStartPos = 1;
      Quantity_ColorRGBA aColor;
      if (aColor.InitFromJson (aStream, aStartPos))
      {
        Standard_Real aRed, aGreen, aBlue;
        aColor.GetRGB().Values (aRed, aGreen, aBlue, Quantity_TOC_sRGB);
        int aDelta = 255;
        myRowValues.ChangeFromIndex (1).CustomValues.insert ((int)Qt::BackgroundRole, QColor((int)(aRed * aDelta),
          (int)(aGreen * aDelta), (int)(aBlue * aDelta)));
      }
      Standard_DumpValue aValue = aKeyToValues.FindFromIndex (1);
      myStreamValue.myValue = aValue.myValue.ToCString();
      myRowValues.ChangeFromIndex (1).Value = aValue.myValue.ToCString();

      Item()->StoreItemProperties (theRow, theColumn, theValue);
      return true;
    }
    TCollection_AsciiString aFromValue = myRowValues.ChangeFromIndex (1).Value.toString().toStdString().c_str();
    if (ReplaceValue(aFromValue, aStreamValue, myStreamValue))
    {
      aStreamValue = myStreamValue.myValue;
      if (Standard_Dump::SplitJson (aStreamValue, aKeyToValues))
      {
        Standard_DumpValue aValue = aKeyToValues.FindFromIndex (1);
        myRowValues.ChangeFromIndex (1).Value = aValue.myValue.ToCString();

        Item()->StoreItemProperties (theRow, theColumn, aStreamValue.ToCString());
        return true;
      }
    }
  }

  myRowValues.ChangeFromIndex (theRow + 1).Value = theValue;
  Item()->StoreItemProperties (theRow, theColumn, theValue);
  return true;
}

// =======================================================================
// function : Presentations
// purpose :
// =======================================================================
void TreeModel_ItemProperties::Presentations (NCollection_List<Handle(Standard_Transient)>& thePresentations)
{
  if (!Item())
  {
    return;
  }
  const Standard_SStream& aStream = Item()->Stream();
  Convert_Tools::ConvertStreamToPresentations (aStream, 1, -1, thePresentations);
}

// =======================================================================
// function : TableFlags
// purpose :
// =======================================================================
Qt::ItemFlags TreeModel_ItemProperties::TableFlags (const int, const int theColumn) const
{
  Qt::ItemFlags aFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;

  if (theColumn == 1)
    aFlags = aFlags | Qt::ItemIsEditable;

  return aFlags;
}

// =======================================================================
// function : ChildStream
// purpose :
// =======================================================================
void TreeModel_ItemProperties::ChildStream (const int theRowId,
                                            TCollection_AsciiString& theKey,
                                            Standard_DumpValue& theValue) const
{
  if (myChildren.Size() <= theRowId)
    return;

  theKey = myChildren.FindKey (theRowId + 1);
  theValue = myChildren.FindFromIndex (theRowId + 1);
}

// =======================================================================
// function : initItem
// purpose :
// =======================================================================
void TreeModel_ItemProperties::initItem() const
{
  if (!Item())
    return;
  if (Item()->IsInitialized())
    return;
  Item()->Init();
}