/*
  * JBoss, Home of Professional Open Source
  * Copyright 2005, JBoss Inc., and individual contributors as indicated
  * by the @authors tag. See the copyright.txt in the distribution for a
  * full listing of individual contributors.
  *
  * This is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as
  * published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this software; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
package org.jboss.xb.binding.sunday.marshalling;

import java.util.Arrays;
import java.util.List;
import javax.xml.namespace.QName;
import org.jboss.xb.binding.Constants;
import org.jboss.xb.binding.JBossXBRuntimeException;
import org.jboss.xb.binding.SimpleTypeBindings;
import org.jboss.xb.binding.Util;
import org.jboss.xb.binding.introspection.FieldInfo;
import org.jboss.xb.binding.metadata.PropertyMetaData;
import org.jboss.xb.binding.sunday.unmarshalling.AttributeBinding;
import org.jboss.xb.binding.sunday.unmarshalling.ElementBinding;
import org.jboss.xb.binding.sunday.unmarshalling.SchemaBinding;
import org.jboss.xb.binding.sunday.unmarshalling.TypeBinding;


/**
 * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
 * @version <tt>$Revision: 2913 $</tt>
 */
public class DefaultAttributeMarshaller
   extends AbstractAttributeMarshaller
{
   public static final DefaultAttributeMarshaller INSTANCE = new DefaultAttributeMarshaller();
   
   public Object getValue(MarshallingContext ctx)
   {
      Object owner = ctx.peek();
      SchemaBinding schema = ctx.getSchemaBinding();
      AttributeBinding binding = ctx.getAttributeBinding();
      QName qName = binding.getQName();

      String fieldName = null;
      PropertyMetaData propertyMetaData = binding.getPropertyMetaData();
      if(propertyMetaData != null)
      {
         fieldName = propertyMetaData.getName();
      }

      if(fieldName == null)
      {
         fieldName =
            Util.xmlNameToFieldName(qName.getLocalPart(), schema.isIgnoreLowLine());
      }

      FieldInfo fieldInfo = FieldInfo.getFieldInfo(
         owner.getClass(), fieldName, binding.getRequired() && !schema.isIgnoreUnresolvedFieldOrClass()
      );
      Object value = null;
      if(fieldInfo != null)
      {
         value = fieldInfo.getValue(owner);
      }

      return value;
   }

   public String marshalValue(MarshallingContext ctx, Object value)
   {
      AttributeBinding binding = ctx.getAttributeBinding();

      if(value == null)
      {
         if(binding.getRequired())
         {
            ElementBinding element = (ElementBinding) ctx.getParticleBinding().getTerm();
            throw new JBossXBRuntimeException("Missing value for the required attribute " + binding.getQName() + " of element " + element.getQName());
         }   
         return null;
      }

      String marshalled;

      TypeBinding attrType = binding.getType();

      if(attrType.getItemType() != null)
      {
         TypeBinding itemType = attrType.getItemType();
         if(Constants.NS_XML_SCHEMA.equals(itemType.getQName().getNamespaceURI()))
         {
            List<QName> list;
            if(value instanceof List)
            {
               list = (List<QName>)value;
            }
            else if(value.getClass().isArray())
            {
               list = Arrays.asList((QName[])value);
            }
            else
            {
               throw new JBossXBRuntimeException("Expected value for list type is an array or " +
                  List.class.getName() +
                  " but got: " +
                  value
               );
            }

            if(Constants.QNAME_QNAME.getLocalPart().equals(itemType.getQName().getLocalPart()))
            {
               String attrLocal = binding.getQName().getLocalPart();
               for(int listInd = 0; listInd < list.size(); ++listInd)
               {
                  QName item = (QName)list.get(listInd);
                  String itemNs = item.getNamespaceURI();
                  if(itemNs != null && itemNs.length() > 0)
                  {
                     String itemPrefix = ctx.getPrefix(itemNs);
                     if(itemPrefix == null)
                     {
                        itemPrefix = item.getPrefix();
                        if(itemPrefix == null || itemPrefix.length() == 0)
                        {
                           itemPrefix = attrLocal + listInd;
                        }
                        ctx.declareNamespace(itemPrefix, itemNs);
                     }

                     if(!itemPrefix.equals(item.getPrefix()))
                     {
                        item = new QName(item.getNamespaceURI(), item.getLocalPart(), itemPrefix);
                        list.set(listInd, item);
                     }
                  }
               }
            }

            marshalled = SimpleTypeBindings.marshalList(itemType.getQName().getLocalPart(), list, null);
         }
         else
         {
            throw new JBossXBRuntimeException("Marshalling of list types with item types not from " +
               Constants.NS_XML_SCHEMA + " is not supported."
            );
         }
      }
      else if(attrType.getLexicalPattern() != null &&
         attrType.getBaseType() != null &&
         Constants.QNAME_BOOLEAN.equals(attrType.getBaseType().getQName()))
      {
         String item = attrType.getLexicalPattern().get(0);
         if(item.indexOf('0') != -1 && item.indexOf('1') != -1)
         {
            marshalled = ((Boolean)value).booleanValue() ? "1" : "0";
         }
         else
         {
            marshalled = ((Boolean)value).booleanValue() ? "true" : "false";
         }
      }
      else if(Constants.QNAME_QNAME.equals(attrType.getQName()))
      {
         boolean removePrefix = false;
         String prefix = null;
         String ns = ((QName)value).getNamespaceURI();
         if(ns != null && ns.length() > 0)
         {
            prefix = ctx.getPrefix(ns);
            if(prefix == null)
            {
               prefix = ((QName)value).getPrefix();
               if(prefix == null || prefix.length() == 0)
               {
                  prefix = "ns_" + ((QName)value).getLocalPart();
               }
               ctx.declareNamespace(prefix, ns);
            }
            ctx.getNamespaceContext().addPrefixMapping(prefix, ns);
            removePrefix = true;
         }

         marshalled = SimpleTypeBindings.marshalQName((QName)value, ctx.getNamespaceContext());

         if(removePrefix)
         {
            ctx.getNamespaceContext().removePrefixMapping(prefix);
         }
      }
      else
      {
         marshalled = value.toString();
      }

      return marshalled;
   }

}
