/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program 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 General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 *    FieldRef.java
 *    Copyright (C) 2008 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.core.pmml;

import java.util.ArrayList;
import org.w3c.dom.Element;

import weka.core.Attribute;

/**
 * Class encapsulating a FieldRef Expression. Is simply a
 * pass-through to an existing field.
 * 
 * @author Mark Hall (mhall{[at]}pentaho{[dot]}com
 * @version $Revision 1.0 $
 */
public class FieldRef extends Expression {
  
  /**
   * For serialization
   */
  private static final long serialVersionUID = -8009605897876168409L;
  
  /** The name of the field to reference */
  protected String m_fieldName = null;
  
  public FieldRef(Element fieldRef, FieldMetaInfo.Optype opType, ArrayList<Attribute> fieldDefs) 
    throws Exception {
    super(opType, fieldDefs);
    
    m_fieldName = fieldRef.getAttribute("field");
    validateField();
  }
  
  public void setFieldDefs(ArrayList<Attribute> fieldDefs) throws Exception {
    super.setFieldDefs(fieldDefs);
    validateField();    
  }
  
  protected void validateField() throws Exception {
    // do some type checking here
    if (m_fieldDefs != null) {
      Attribute a = getFieldDef(m_fieldName);
      if (a == null) {
        throw new Exception("[FieldRef] Can't find field " + m_fieldName
            + " in the supplied field definitions");
      }
      if ((m_opType == FieldMetaInfo.Optype.CATEGORICAL ||
          m_opType == FieldMetaInfo.Optype.ORDINAL) && a.isNumeric()) {
        throw new IllegalArgumentException("[FieldRef] Optype is categorical/ordinal but matching "
            + "parameter in the field definitions is not!");
      }
      
      if (m_opType == FieldMetaInfo.Optype.CONTINUOUS && a.isNominal()) {
        throw new IllegalArgumentException("[FieldRef] Optype is continuous but matching "
            + "parameter in the field definitions is not!");
      }
    }
  }

  @Override
  public double getResult(double[] incoming) throws Exception {

    double result = Double.NaN;
    boolean found = false;
    
    for (int i = 0; i < m_fieldDefs.size(); i++) {
      Attribute a = m_fieldDefs.get(i);
      if (a.name().equals(m_fieldName)) {
        if (a.isNumeric()) {
          if (m_opType == FieldMetaInfo.Optype.CATEGORICAL ||
              m_opType == FieldMetaInfo.Optype.ORDINAL) {
            throw new IllegalArgumentException("[FieldRef] Optype is categorical/ordinal but matching "
                + "parameter is not!");         
          }          
        } else if (a.isNominal()) {
          if (m_opType == FieldMetaInfo.Optype.CONTINUOUS) {
            throw new IllegalArgumentException("[FieldRef] Optype is continuous but matching "
                + "parameter is not!");
          }
        } else {
          throw new IllegalArgumentException("[FieldRef] Unhandled attribute type");
        }
        result = incoming[i];
        found = true;
        break;
      }
    }
    
    if (!found) {
      throw new Exception("[FieldRef] this field: " + m_fieldName + " is not in the supplied "
          + "list of parameters!");
    }
    return result;
  }

  @Override
  public String getResultCategorical(double[] incoming)
      throws Exception {
    
    if (m_opType == FieldMetaInfo.Optype.CONTINUOUS) {
      throw new IllegalArgumentException("[FieldRef] Can't return result as "
          +"categorical/ordinal because optype is continuous!");
    }
    
    boolean found = false;
    String result = null;
    
    for (int i = 0; i < m_fieldDefs.size(); i++) {
      Attribute a = m_fieldDefs.get(i);
      if (a.name().equals(m_fieldName)) {
        found = true;
        result = a.value((int)incoming[i]);
        break;
      }
    }
    
    if (!found) {
      throw new Exception("[FieldRef] this field: " + m_fieldName + " is not in the supplied "
          + "list of parameters!");
    }
    return result;
  }
  
  /**
   * Return the structure of the result of applying this Expression
   * as an Attribute.
   * 
   * @return the structure of the result of applying this Expression as an
   * Attribute.
   */
  public Attribute getOutputDef() {
    
    Attribute a = getFieldDef(m_fieldName);
    if (a != null) {
      return a;
      /* Attribute result = a.copy(attName);
      return result; */
    }
    
    // If we can't find the reference field in the field definitions then
    // we can't return a definition for the result
    return null;
  }
  
  public String toString(String pad) {
    return pad + "FieldRef: " + m_fieldName;
  }
}
