/*
* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Handler for appling styles
*
*/



// INCLUDE FILES
#include "CSSHandler.h"
#include "nw_lmgr_simplepropertylist.h"
#include "nw_hed_documentroot.h"
#include "nw_hed_CompositeContentHandler.h"
#include "nw_lmgr_statictablebox.h"
#include "nw_fbox_inputbox.h"
#include "HEDDocumentListener.h"
#include "nw_lmgr_rootbox.h"
#include "nw_basicforms_inputelementhandler.h"
#include "nwx_logger.h"
#include "BrsrStatusCodes.h"

// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES  

// CONSTANTS

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// FORWARD DECLARATIONS

// ============================= LOCAL FUNCTIONS ===============================


// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CCCSSHandler::CCCSSHandler
// C++ default constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
//
CCSSHandler::CCSSHandler(NW_CSS_Processor_t* aProcessor) : 
              CActive( CActive::EPriorityLow + 1 )
  {
  iProcessor = aProcessor;
  }

// -----------------------------------------------------------------------------
// CCSSHandler::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CCSSHandler::ConstructL()
  {
  CActiveScheduler::Add(this);
  }

// -----------------------------------------------------------------------------
// CCSSHandler::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CCSSHandler* CCSSHandler::NewL(NW_CSS_Processor_t* aProcessor)
  {
  CCSSHandler* self = new( ELeave ) CCSSHandler(aProcessor);
  
  CleanupStack::PushL( self );
  self->ConstructL();
  CleanupStack::Pop();

  return self;
  }

    
// Destructor
CCSSHandler::~CCSSHandler()
  {
  Cancel();
  }


// -----------------------------------------------------------------------------
// CCSSHandler::RunL
// Applies styles to each box of the box tree
// -----------------------------------------------------------------------------
//
void CCSSHandler::RunL()
  {
	// Each time RunL() gets called we need to
	// 1. Apply styles to the next box
	// 2. Find out if the styles have been applied 
  //    to the entire box tree    
	//
	//	-	If it has completed, tell the owner to collapse
  //    the box tree and update display
	//
	//	-	If it hasn't completed, simulate a request
	//		completion, so that this RunL() will get
	//		called again by the active scheduler.
	//
  TBool completed = EFalse;
	TRAPD(ret, completed = ApplyBoxStylesL());
  if (ret == KErrNone)
    {
	  if (!completed)
	    {
		  // Before simulating a request completion we need to
		  // set this active object as active.
		  SetActive();
      // The following two lines simulate a request completion
	    // as if it were generated by a server. This completion will
	    // be processed by the active scheduler. If other events have
	    // completed belonging to higher-priority active objects, those
	    // will get processed first. Otherwise, our RunL() function
	    // will get called, allowing us to apply styles to next box
	    // User::RequestComplete() takes two parameters: the address 
	    // of the status variable for the active object whose event
	    // has completed (in this case, *this* active object), and
	    // an error code to write to this address - KErrNone in this case.
		  TRequestStatus* status = &iStatus;
		  User::RequestComplete(status, KErrNone);
	    }

	  else
	    {
      // call the function to update display
		  TInt ret1;
		  TRAP(ret1, ApplyStylesCompleteL());
      }
    }
}

// -----------------------------------------------------------------------------
// CCCSSHandler::DoCancel
// -----------------------------------------------------------------------------
//
void CCSSHandler::DoCancel()
{
}

// -----------------------------------------------------------------------------
// CCCSSHandler::ApplyStylesCompleteL
// collapse the box tree, swaps property list and then formats and redraws 
// the updated tree
// -----------------------------------------------------------------------------
//
void CCSSHandler::ApplyStylesCompleteL()
{ 
  NW_LOG0(NW_LOG_LEVEL4, "[CCSSHandler:ApplyStylesCompleteL Begin]");
  NW_HED_DocumentRoot_t* docRoot = 
    (NW_HED_DocumentRoot_t*) NW_HED_DocumentNode_GetRootNode (iProcessor->owner);

  NW_LMgr_RootBox_t* rootBox = NW_HED_DocumentRoot_GetRootBox(docRoot);

  // collapse the box tree
  TBrowserStatusCode status = rootBox->boxTreeListener->Collapse (NULL, NW_TRUE);
  if (status == KBrsrOutOfMemory)
  {
    User::Leave(KErrNoMemory);
  }
  else if (status != KBrsrSuccess)
  {
    User::Leave(KErrAbort);
  }
  NW_LOG0(NW_LOG_LEVEL4, "[CCSSHandler:Collapse tree finished]");

  // swap the property list 
  SwapPropListL();
  NW_LOG0(NW_LOG_LEVEL4, "[CCSSHandler:Swap prop list finished]");
  
  // format the box tree
  status = rootBox->boxTreeListener->FormatBoxTree (NW_TRUE);
  if (status == KBrsrOutOfMemory)
  {
    User::Leave(KErrNoMemory);
  }
  else if (status != KBrsrSuccess)
  {
    User::Leave(KErrAbort);
  }
  NW_LOG0(NW_LOG_LEVEL4, "[CCSSHandler:format tree finished]");

  // draw
  status = rootBox->boxTreeListener->RedrawDisplay (NW_FALSE);
  if (status == KBrsrOutOfMemory)
  {
    User::Leave(KErrNoMemory);
  }
  else if (status != KBrsrSuccess)
  {
    User::Leave(KErrAbort);
  }
  NW_LOG0(NW_LOG_LEVEL4, "[CCSSHandler:ApplyStylesCompleteL finished]");
}

// -----------------------------------------------------------------------------
// CCCSSHandler::ApplyStylesCompleteL
// swaps the property list of the boxes
// -----------------------------------------------------------------------------
//
void
CCSSHandler::SwapPropListL()
  {
  NW_LMgr_Box_t* box = NULL;
  NW_Bool skipChildren = NW_FALSE;
  NW_HED_ContentHandler_t* contentHandler = (NW_HED_ContentHandler_t*)(iProcessor->owner);
  NW_LMgr_PropertyValue_t displayVal;
  
  // Initialize the box visitor
  NW_LMgr_BoxVisitor_Initialize(&iBoxVisitor, contentHandler->boxTree);
  
  box = NW_LMgr_BoxVisitor_NextBox(&iBoxVisitor,&skipChildren);
  // swap the propery lists
  // Look for the next box with Simple property list set 
  while (box != NULL) 
    {
    
    displayVal.token = NW_CSS_PropValue_display_inline; 
    // we want to look at simple prop list since we don't want to look at split boxes
    if (box->propList && NW_Object_IsClass(box->propList, &NW_LMgr_SimplePropertyList_Class))
      {
      NW_LMgr_SimplePropertyList_t* simplePropList = (NW_LMgr_SimplePropertyList_t*)box->propList;
      if (simplePropList->backupPropList)
        {
        box->propList = simplePropList->backupPropList;
        simplePropList->backupPropList = NULL;
        NW_Object_Delete(simplePropList);
        
        // we need to do this so that context can again calculate the border info 
        if (NW_Object_IsClass(box, &NW_LMgr_StaticTableBox_Class))
          {
          NW_LMgr_StaticTableBox_t* tableBox = (NW_LMgr_StaticTableBox_t*)box;
          NW_Object_Delete(tableBox->ctx);
          tableBox->ctx = NULL;
          }
        
        if (NW_Object_IsInstanceOf(box, &NW_FBox_InputBox_Class))
          {
          TBrowserStatusCode status = NW_XHTML_inputElementHandler_HandleValidation(NW_FBox_InputBoxOf(box), 
            NW_FBox_FormBoxOf(box)->formLiaison);
          if (status == KBrsrOutOfMemory)
            {
            User::Leave(KErrNoMemory);
            }
          }
        
        NW_LMgr_Box_GetPropertyValue(box, NW_CSS_Prop_display, NW_CSS_ValueType_Token, &displayVal);
        
        if (displayVal.token != NW_CSS_PropValue_none) 
          {
          // Check display Val and replace box is necessary
          TBrowserStatusCode status = NW_CSS_Processor_HandleDisplayVal(&box, &displayVal);
          if (status == KBrsrOutOfMemory)
            {
            User::Leave(KErrNoMemory);
            }
          else if (status != KBrsrSuccess)
            {
            User::Leave(KErrAbort);
            }
          iBoxVisitor.currentBox = box;
          }
        }
      }// end if
    
    // handle display none
    if (displayVal.token == NW_CSS_PropValue_none) 
      {
      skipChildren = NW_TRUE;
      }
    // Algorithm was changed here. We do not remove boxes with NW_CSS_PropValue_none any more at this point.

    box = NW_LMgr_BoxVisitor_NextBox(&iBoxVisitor,&skipChildren);
  
    
    }// end while
  }

// -----------------------------------------------------------------------------
// CCCSSHandler::ApplyBoxStylesL
// Applies styles to each box and stores it as backup property list
// -----------------------------------------------------------------------------
//
TBool CCSSHandler::ApplyBoxStylesL()
{
  NW_DOM_ElementNode_t* elementNode = NULL;
  NW_LMgr_Box_t* box = NULL;
  NW_LMgr_SimplePropertyList_t* oldPropList = NULL;
  TBrowserStatusCode status = KBrsrSuccess;

  // Look for the next box with DOM node set 
  box = GetNextBox(&elementNode);

  // we have element node and proplist
  if (box != NULL)
  {
    oldPropList = (NW_LMgr_SimplePropertyList_t*)(box->propList);
    // set propList as NULL to get new prop list
    box->propList = NULL;

    if (oldPropList->backupPropList)
    {
      NW_Object_Delete(oldPropList->backupPropList);
      oldPropList->backupPropList = NULL;
    }

    // get new styles
    status = iProcessor->applyCSSCallback((void *)(iProcessor->owner), elementNode, &box);
    if (status == KBrsrOutOfMemory)
    {
      User::Leave(KErrNoMemory);
    }

    // box was deleted either because of error
    if (box == NULL)
    {
      NW_Object_Delete(oldPropList);
      User::Leave(KErrAbort);
    }
    
    // save the box proplist as backup prop list
    oldPropList->backupPropList = box->propList;
    box->propList = (NW_LMgr_PropertyList_t*)oldPropList;
  }
  return (box == NULL);
}

// -----------------------------------------------------------------------------
// CCCSSHandler::GetNextBox
// Get next box with  DOM node - these are the boxes we need to apply styles again
// -----------------------------------------------------------------------------
//
NW_LMgr_Box_t*
CCSSHandler::GetNextBox(NW_DOM_ElementNode_t** node)
{
  NW_LMgr_Box_t* box = NULL;
  NW_DOM_ElementNode_t* elementNode = NULL;

  // Look for the next box with DOM node set 
  while ((box = NW_LMgr_BoxVisitor_NextBox(&iBoxVisitor,0)) != NULL) 
  {
    NW_LMgr_PropertyValue_t value; 
    NW_LMgr_PropertyList_t* propList = box->propList;

    // we want to look at simple prop list since we don't want to loook at split boxes
    if (propList && NW_Object_IsClass(propList, &NW_LMgr_SimplePropertyList_Class))
    {
      value.object = NULL;
      (void)NW_LMgr_Box_GetPropertyValue(box, NW_CSS_Prop_elementId, NW_CSS_ValueType_Object, &value);
      if (value.object == NULL)
        continue;

      elementNode = (NW_DOM_ElementNode_t*)value.object;  
      if (elementNode)
      {
        break;
      }
    }
  }
  *node = elementNode;
  return box;
}

// -----------------------------------------------------------------------------
// CCCSSHandler::ApplyStylesL
// This is called from the processor when a new stylesheet needs to be applied
// -----------------------------------------------------------------------------
//
void CCSSHandler::ApplyStylesL()
{
  NW_LOG0(NW_LOG_LEVEL4, "[CCSSHandler:ApplyStylesL Begin]");
  // cancel the active object
  // we need to stop applying styles as a new CSS has arrived which will resolve to
  // new CSS properties
  Cancel();

	// This function is called by client code (processor) to start the long
	// running css apply styles.

  // we want to apply styles to the box tree associated with the owner
  NW_HED_ContentHandler_t* contentHandler = (NW_HED_ContentHandler_t*)(iProcessor->owner);

  // Initialize the box visitor
  NW_LMgr_BoxVisitor_Initialize(&iBoxVisitor, contentHandler->boxTree);

  // Call SetActive() so that this active object gets notified of completed requests.
  SetActive();

  // The following two lines simulate a request completion
	// as if it were generated by a server. This completion will
	// be processed by the active scheduler. If other events have
	// completed belonging to higher-priority active objects, those
	// will get processed first. Otherwise, our RunL() function
	// will get called, allowing us to apply styles to next box
	// User::RequestComplete() takes two parameters: the address 
	// of the status variable for the active object whose event
	// has completed (in this case, *this* active object), and
	// an error code to write to this address - KErrNone in this case.
  TRequestStatus* status = &iStatus;
	User::RequestComplete(status, KErrNone);
}

//  End of File  
