/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla XForms support.
 *
 * The Initial Developer of the Original Code is
 * Novell, Inc.
 * Portions created by the Initial Developer are Copyright (C) 2004
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *  Allan Beaufour <abeaufour@novell.com>
 *  David Landwehr <dlandwehr@novell.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nsXFormsMDG.h"

#include "nsVoidArray.h"
#include "nsIDOMDocument.h"
#include "nsIDOMText.h"
#include "nsXFormsXPathNode.h"
#include "nsXFormsUtils.h"

MOZ_DECL_CTOR_COUNTER(nsXFormsMDG)

nsXFormsMDG::nsXFormsMDG()
  : mEngine(this)
{
  MOZ_COUNT_CTOR(nsXFormsMDG);
}

nsXFormsMDG::~nsXFormsMDG()
{
  MOZ_COUNT_DTOR(nsXFormsMDG);
}

nsresult
nsXFormsMDG::Init()
{
  return mEngine.Init();
}

nsresult
nsXFormsMDG::CreateNewChild(nsIDOMNode* aContextNode, const nsAString& aNodeValue,
                            nsIDOMNode* aBeforeNode)
{
  nsresult rv;

  nsCOMPtr<nsIDOMDocument> document;
  rv = aContextNode->GetOwnerDocument(getter_AddRefs(document));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIDOMText> textNode;
  rv = document->CreateTextNode(aNodeValue, getter_AddRefs(textNode));
  NS_ENSURE_SUCCESS(rv, rv);
  
  nsCOMPtr<nsIDOMNode> newNode;
  if (aBeforeNode) {
    rv = aContextNode->InsertBefore(textNode, aBeforeNode, getter_AddRefs(newNode));
  } else {
    rv = aContextNode->AppendChild(textNode, getter_AddRefs(newNode));
  }

  return rv;
}

nsresult
nsXFormsMDG::AddMIP(ModelItemPropName aType, nsIDOMXPathExpression* aExpression,
                    nsXFormsMDGSet* aDeps, PRBool aDynFunc, nsIDOMNode* aContextNode,
                    PRInt32 aContextPos, PRInt32 aContextSize)
{
  NS_ENSURE_ARG(aExpression);
  NS_ENSURE_ARG(aContextNode);

  return mEngine.Insert(aContextNode, aExpression, aDeps, aDynFunc, aContextPos, aContextSize, aType);
}

nsresult
nsXFormsMDG::MarkNodeAsChanged(nsIDOMNode* aContextNode)
{
  return mEngine.MarkNode(aContextNode);
}

nsresult
nsXFormsMDG::Recalculate(nsXFormsMDGSet& aChangedNodes)
{
  return mEngine.Calculate(aChangedNodes);
}

nsresult
nsXFormsMDG::Rebuild()
{
  return mEngine.Rebuild();
}

void
nsXFormsMDG::ClearDispatchFlags()
{
  mEngine.ClearDispatchFlags();
}

void
nsXFormsMDG::Clear() {
  mEngine.Clear();
}

nsresult
nsXFormsMDG::SetNodeValue(nsIDOMNode* aContextNode, const nsString& aNodeValue,
                          PRBool aMarkNode)
{
  if (IsReadonly(aContextNode)) {
    // TODO: Better feedback for readonly nodes?
    return NS_OK;
  }

  nsXFormsUtils::SetNodeValue(aContextNode, aNodeValue);
  
  // NB: Never reached for Readonly nodes.  
  if (aMarkNode) {
      MarkNodeAsChanged(aContextNode);
  }
  
  return NS_OK;
}


/**********************************************/
/*             Bit test functions             */
/**********************************************/
PRBool
nsXFormsMDG::IsValid(nsIDOMNode* aContextNode)
{
  
  PRBool valid = mEngine.Test(aContextNode, MDG_FLAG_CONSTRAINT);
  if (valid) {
    // TODO: needs Schema support
    // valid = mSchemaHandler->ValidateNode(aContextNode);
  }
  return valid;
}

PRBool
nsXFormsMDG::IsConstraint(nsIDOMNode* aContextNode)
{
  return mEngine.Test(aContextNode, MDG_FLAG_CONSTRAINT);
}

PRBool
nsXFormsMDG::ShouldDispatchValid(nsIDOMNode* aContextNode)
{
  return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_VALID_CHANGED);
}

PRBool
nsXFormsMDG::IsReadonly(nsIDOMNode* aContextNode)
{
  PRBool valid;
  if (   mEngine.Test(aContextNode, MDG_FLAG_READONLY)
      || mEngine.Test(aContextNode, MDG_FLAG_INHERITED_READONLY)) {
    valid = PR_TRUE;
  } else {
    valid = PR_FALSE;
  }
  return valid;
}

PRBool
nsXFormsMDG::ShouldDispatchReadonly(nsIDOMNode* aContextNode)
{
  return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_READONLY_CHANGED);
}


PRBool
nsXFormsMDG::IsRelevant(nsIDOMNode* aContextNode)
{
  PRBool valid;
  if (   mEngine.Test(aContextNode, MDG_FLAG_RELEVANT)
      && mEngine.Test(aContextNode, MDG_FLAG_INHERITED_RELEVANT)) {
    valid = PR_TRUE;
  } else {
    valid = PR_FALSE;
  }
  return valid;
}

PRBool
nsXFormsMDG::ShouldDispatchRelevant(nsIDOMNode* aContextNode)
{
  return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_RELEVANT_CHANGED);
}

PRBool
nsXFormsMDG::IsRequired(nsIDOMNode* aContextNode)
{
  return mEngine.Test(aContextNode, MDG_FLAG_REQUIRED);
}

PRBool
nsXFormsMDG::ShouldDispatchRequired(nsIDOMNode* aContextNode)
{
  return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_REQUIRED_CHANGED);
}

PRBool
nsXFormsMDG::ShouldDispatchValueChanged(nsIDOMNode* aContextNode)
{
  return mEngine.Test(aContextNode, MDG_FLAG_DISPATCH_VALUE_CHANGED);
}
