// Created on: 2000-08-15
// Created by: Andrey BETENEV
// Copyright (c) 2000-2014 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 <XCAFPrs.hxx>

#include <TDF_Label.hxx>
#include <TopLoc_IndexedMapOfLocation.hxx>
#include <TopLoc_Location.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Compound.hxx>
#include <XCAFDoc_ColorTool.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_GraphNode.hxx>
#include <XCAFDoc_LayerTool.hxx>
#include <XCAFDoc_VisMaterialTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <XCAFPrs_Style.hxx>

static Standard_Boolean viewnameMode = Standard_False;

//! Fill colors of XCAFPrs_Style structure.
static void fillStyleColors (XCAFPrs_Style& theStyle,
                             const Handle(XCAFDoc_ColorTool)& theTool,
                             const TDF_Label& theLabel)
{
  Quantity_ColorRGBA aColor;
  if (theTool->GetColor (theLabel, XCAFDoc_ColorGen, aColor))
  {
    theStyle.SetColorCurv (aColor.GetRGB());
    theStyle.SetColorSurf (aColor);
  }
  if (theTool->GetColor (theLabel, XCAFDoc_ColorSurf, aColor))
  {
    theStyle.SetColorSurf (aColor);
  }
  if (theTool->GetColor (theLabel, XCAFDoc_ColorCurv, aColor))
  {
    theStyle.SetColorCurv (aColor.GetRGB());
  }
}

static Standard_Boolean getShapesOfSHUO (TopLoc_IndexedMapOfLocation& theaPrevLocMap,
                                         const Handle(XCAFDoc_ShapeTool)& theSTool,
                                         const TDF_Label& theSHUOlab,
                                         TopTools_SequenceOfShape& theSHUOShapeSeq)
{
  Handle(XCAFDoc_GraphNode) SHUO;
  TDF_LabelSequence aLabSeq;
  theSTool->GetSHUONextUsage( theSHUOlab, aLabSeq );
  if (aLabSeq.Length() >= 1)
    for (Standard_Integer i = 1; i <= aLabSeq.Length(); i++) {
      TDF_Label aSubCompL = aLabSeq.Value( i );
      TopLoc_Location compLoc = XCAFDoc_ShapeTool::GetLocation ( aSubCompL.Father() );
      // create new map of laocation (to not merge locations from different shapes)
      TopLoc_IndexedMapOfLocation aNewPrevLocMap;
      for (Standard_Integer m = 1; m <= theaPrevLocMap.Extent(); m++)
        aNewPrevLocMap.Add( theaPrevLocMap.FindKey( m ) );
      aNewPrevLocMap.Add( compLoc );
      // got for the new sublocations and corresponding shape
      getShapesOfSHUO( aNewPrevLocMap, theSTool, aSubCompL, theSHUOShapeSeq );
    }
  else {
    TopoDS_Shape aSHUO_NUSh = theSTool->GetShape ( theSHUOlab.Father() );
    if ( aSHUO_NUSh.IsNull() ) return Standard_False;
    // cause got shape with location already.
    TopLoc_Location nullLoc;
    aSHUO_NUSh.Location ( nullLoc );
    // multiply the locations
    Standard_Integer intMapLenght = theaPrevLocMap.Extent();
    if ( intMapLenght < 1 )
      return Standard_False; // should not be, but to avoid exception...?
    TopLoc_Location SupcompLoc;
    SupcompLoc = theaPrevLocMap.FindKey( intMapLenght );
    if (intMapLenght > 1) {
      Standard_Integer l = intMapLenght - 1;
      while (l >= 1) {
        SupcompLoc = theaPrevLocMap.FindKey( l ).Multiplied( SupcompLoc );
        l--;
      }
    }
    aSHUO_NUSh.Location( SupcompLoc, Standard_False );
    theSHUOShapeSeq.Append( aSHUO_NUSh );
  }
  return (theSHUOShapeSeq.Length() > 0);
}

//=======================================================================
//function : CollectStyleSettings
//purpose  : 
//=======================================================================

void XCAFPrs::CollectStyleSettings (const TDF_Label& theLabel,
                                    const TopLoc_Location& theLoc,
                                    XCAFPrs_IndexedDataMapOfShapeStyle& theSettings,
                                    const Quantity_ColorRGBA& theLayerColor)
{
  // for references, first collect colors of referred shape
  {
    TDF_Label aLabelRef;
    if (XCAFDoc_ShapeTool::GetReferredShape (theLabel, aLabelRef))
    {
      Quantity_ColorRGBA aLayerColor = theLayerColor;
      Handle(XCAFDoc_LayerTool) aLayerTool = XCAFDoc_DocumentTool::LayerTool (theLabel);
      Handle(TColStd_HSequenceOfExtendedString) aLayerNames = new TColStd_HSequenceOfExtendedString();
      aLayerTool->GetLayers (theLabel, aLayerNames);
      if (aLayerNames->Length() == 1)
      {
        TDF_Label aLayer = aLayerTool->FindLayer (aLayerNames->First());
        Handle(XCAFDoc_ColorTool) aColorTool = XCAFDoc_DocumentTool::ColorTool(theLabel);
        Quantity_ColorRGBA aColor;
        if (aColorTool->GetColor (aLayer, XCAFDoc_ColorGen, aColor))
          aLayerColor = aColor;
      }
      TopLoc_Location aLocSub = theLoc.Multiplied (XCAFDoc_ShapeTool::GetLocation (theLabel));
      CollectStyleSettings (aLabelRef, aLocSub, theSettings, aLayerColor);
    }
  }

  // for assemblies, first collect colors defined in components
  {
    TDF_LabelSequence aComponentLabSeq;
    if (XCAFDoc_ShapeTool::GetComponents (theLabel, aComponentLabSeq)
    && !aComponentLabSeq.IsEmpty())
    {
      for (TDF_LabelSequence::Iterator aComponentIter (aComponentLabSeq); aComponentIter.More(); aComponentIter.Next())
      {
        const TDF_Label& aComponentLab = aComponentIter.Value();
        CollectStyleSettings (aComponentLab, theLoc, theSettings, theLayerColor);
      }
    }
  }

  // collect settings on subshapes
  Handle(XCAFDoc_ColorTool) aColorTool = XCAFDoc_DocumentTool::ColorTool(theLabel);
  Handle(XCAFDoc_VisMaterialTool) aMatTool = XCAFDoc_DocumentTool::VisMaterialTool (theLabel);

  TDF_LabelSequence aLabSeq;
  XCAFDoc_ShapeTool::GetSubShapes (theLabel, aLabSeq);
  // and add the shape itself
  aLabSeq.Append (theLabel);
  for (TDF_LabelSequence::Iterator aLabIter (aLabSeq); aLabIter.More(); aLabIter.Next())
  {
    const TDF_Label& aLabel = aLabIter.Value();
    XCAFPrs_Style aStyle;
    aStyle.SetVisibility (aColorTool->IsVisible (aLabel));
    aStyle.SetMaterial (aMatTool->GetShapeMaterial (aLabel));

    Handle(TColStd_HSequenceOfExtendedString) aLayerNames;
    Handle(XCAFDoc_LayerTool) aLayerTool = XCAFDoc_DocumentTool::LayerTool (aLabel);
    if (aStyle.IsVisible())
    {
      aLayerNames = new TColStd_HSequenceOfExtendedString();
      aLayerTool->GetLayers (aLabel, aLayerNames);
      Standard_Integer aNbHidden = 0;
      for (TColStd_HSequenceOfExtendedString::Iterator aLayerIter (*aLayerNames); aLayerIter.More(); aLayerIter.Next())
      {
        const TCollection_ExtendedString& aLayerName = aLayerIter.Value();
        if (!aLayerTool->IsVisible (aLayerTool->FindLayer (aLayerName)))
        {
          ++aNbHidden;
        }
      }
      aStyle.SetVisibility (aNbHidden == 0
                         || aNbHidden != aLayerNames->Length());
    }

    if (aColorTool->IsColorByLayer (aLabel))
    {
      Quantity_ColorRGBA aLayerColor = theLayerColor;
      if (aLayerNames.IsNull())
      {
        aLayerNames = new TColStd_HSequenceOfExtendedString();
        aLayerTool->GetLayers (aLabel, aLayerNames);
      }
      if (aLayerNames->Length() == 1)
      {
        TDF_Label aLayer = aLayerTool->FindLayer (aLayerNames->First());
        Quantity_ColorRGBA aColor;
        if (aColorTool->GetColor (aLayer, XCAFDoc_ColorGen, aColor))
        {
          aLayerColor = aColor;
        }
      }

      aStyle.SetColorCurv (aLayerColor.GetRGB());
      aStyle.SetColorSurf (aLayerColor);
    }
    else
    {
      fillStyleColors (aStyle, aColorTool, aLabel);
    }

    // PTV try to set color from SHUO structure
    const Handle(XCAFDoc_ShapeTool)& aShapeTool = aColorTool->ShapeTool();
    if (aShapeTool->IsComponent (aLabel))
    {
      TDF_AttributeSequence aShuoAttribSeq;
      aShapeTool->GetAllComponentSHUO (aLabel, aShuoAttribSeq);
      for (TDF_AttributeSequence::Iterator aShuoAttribIter (aShuoAttribSeq); aShuoAttribIter.More(); aShuoAttribIter.Next())
      {
        Handle(XCAFDoc_GraphNode) aShuoNode = Handle(XCAFDoc_GraphNode)::DownCast (aShuoAttribIter.Value());
        if (aShuoNode.IsNull())
        {
          continue;
        }

        const TDF_Label aShuolab = aShuoNode->Label();
        {
          TDF_LabelSequence aShuoLabSeq;
          aShapeTool->GetSHUONextUsage (aShuolab, aShuoLabSeq);
          if (aShuoLabSeq.IsEmpty())
          {
            continue;
          }
        }

        XCAFPrs_Style aShuoStyle;
        aShuoStyle.SetMaterial  (aMatTool->GetShapeMaterial (aShuolab));
        aShuoStyle.SetVisibility(aColorTool->IsVisible (aShuolab));
        fillStyleColors (aShuoStyle, aColorTool, aShuolab);
        if (aShuoStyle.IsEmpty())
        {
          continue;
        }

        // set style for all component from Next Usage Occurrence.
      #ifdef OCCT_DEBUG
        std::cout << "Set the style for SHUO next_usage-occurrence" << std::endl;
      #endif
        /* 
        // may be work, but static it returns excess shapes. It is more faster to use OLD version.
        // PTV 14.02.2003 NEW version using API of ShapeTool
        TopTools_SequenceOfShape aShuoShapeSeq;
        aShapeTool->GetAllStyledComponents (aShuoNode, aShuoShapeSeq);
        for (TopTools_SequenceOfShape::Iterator aShuoShapeIter (aShuoShapeSeq); aShuoShapeIter.More(); aShuoShapeIter.Next())
        {
          const TopoDS_Shape& aShuoShape = aShuoShapeIter.Value();
          if (!aShuoShape.IsNull())
            theSettings.Bind (aShuoShape, aShuoStyle);
        }*/
        // OLD version that was written before ShapeTool API, and it FASTER for presentation
        // get TOP location of SHUO component
        TopLoc_Location compLoc = XCAFDoc_ShapeTool::GetLocation (aLabel);
        TopLoc_IndexedMapOfLocation aPrevLocMap;
        // get previous set location
        if (!theLoc.IsIdentity())
        {
          aPrevLocMap.Add (theLoc);
        }
        aPrevLocMap.Add (compLoc);

        // get shapes of SHUO Next_Usage components
        TopTools_SequenceOfShape aShuoShapeSeq;
        getShapesOfSHUO (aPrevLocMap, aShapeTool, aShuolab, aShuoShapeSeq);
        for (TopTools_SequenceOfShape::Iterator aShuoShapeIter (aShuoShapeSeq); aShuoShapeIter.More(); aShuoShapeIter.Next())
        {
          const TopoDS_Shape& aShuoShape = aShuoShapeIter.Value();
          XCAFPrs_Style* aMapStyle = theSettings.ChangeSeek (aShuoShape);
          if (aMapStyle == NULL)
            theSettings.Add (aShuoShape, aShuoStyle);
          else
            *aMapStyle = aShuoStyle;
        }
        continue;
      }
    }

    if (aStyle.IsEmpty())
    {
      continue;
    }

    TopoDS_Shape aSubshape = XCAFDoc_ShapeTool::GetShape (aLabel);
    if (aSubshape.ShapeType() == TopAbs_COMPOUND)
    {
      if (aSubshape.NbChildren() == 0)
      {
        continue;
      }
    }
    aSubshape.Move (theLoc, Standard_False);
    XCAFPrs_Style* aMapStyle = theSettings.ChangeSeek (aSubshape);
    if (aMapStyle == NULL)
      theSettings.Add (aSubshape, aStyle);
    else
      *aMapStyle = aStyle;
  }
}

//=======================================================================
//function : SetViewNameMode
//purpose  : 
//=======================================================================

void XCAFPrs::SetViewNameMode(const Standard_Boolean aNameMode )
{
  viewnameMode = aNameMode;
}

//=======================================================================
//function : GetViewNameMode
//purpose  : 
//=======================================================================

Standard_Boolean XCAFPrs::GetViewNameMode()
{
  return viewnameMode;
}
