package com.limegroup.gnutella.spam;

import java.util.Arrays;

/**
 * Like a KeywordToken but also holds part of the meta-data name which this
 * token belongs to, so the filter can specifically rate for example videos with
 * "Type: Adult" as spam
 */
public class XMLKeywordToken extends AbstractToken {
	private static final long serialVersionUID = 3617573808026760503L;

	public static final int TYPE = TYPE_XML_KEYWORD;

	/**
	 * must be positive
	 * 
	 * This is a heuristic value to prevent an token from becoming bad after
	 * only a small number of bad evaluations.
	 */
	private static final byte INITAL_GOOD = 20;

	/**
	 * must be positive
	 * 
	 * This value determines how dynamic the filter is. A low MAX value will
	 * allow this Token to get a bad rating after occourring only a few times in
	 * spam, a high value will make it very improbable that the filter will
	 * change its mind about a certain token without user-intervention
	 */
	private static final int MAX = 100;

	private final byte[] _keyword;

	private final byte[] _xmlField;

	private byte _good;

	private byte _bad;
    
    private final int _hashCode;

	XMLKeywordToken(String xmlField, byte[] keyword) {
		_good = 90; // give every keyword initial credit
		_bad = 0;
		_keyword = keyword;
		_xmlField = xmlField.getBytes();
        
        int hash = xmlField.hashCode();
        for(int i = 0;i < keyword.length; i++)
            hash = (37 * hash) + keyword[i];
        
        _hashCode = hash;
	}

	/**
	 * implements interface <tt>Token</tt>
	 */
	public float getRating() {
		return (float) Math.pow(1.f * _bad / (_good + _bad + 1), 2);
	}

	/**
	 * implements interface <tt>Token</tt>
	 */
	public void rate(int rating) {
		_age = 0;
		switch (rating) {
		case RATING_GOOD:
			_good++;
			break;
		case RATING_SPAM:
			_bad++;
			break;
		case RATING_USER_MARKED_GOOD:
			_bad = 0;
			break;
		case RATING_USER_MARKED_SPAM:
			_bad = (byte) Math.min(10 + _bad, MAX);
			break;
		case RATING_CLEARED:
			_bad = 0;
			_good = 90;
			break;
		default:
			throw new IllegalArgumentException("unknown type of rating");
		}

		if (_good >= MAX || _bad >= MAX) {
			_good = (byte) (_good * 9 / 10);
			_bad = (byte) (_bad * 9 / 10);
		}
	}

	/**
	 * implements interface <tt>Token</tt>
	 */
	public int getType() {
		return TYPE;
	}

    public final int hashCode() {
        return _hashCode;
    }
    
    public final boolean equals(Object o) {
        if (o == null)
            return false;
        if (!(o instanceof XMLKeywordToken))
            return false;
        
        if (_hashCode != o.hashCode()) {
            return false;
        }
        
        return Arrays.equals(_xmlField, ((XMLKeywordToken)o)._xmlField) 
                && Arrays.equals(_keyword, ((XMLKeywordToken)o)._keyword);
    }
    
	/**
	 * overrides method from <tt>Object</tt>
	 */
	public String toString() {
		return new String(_xmlField) + "::" + new String(_keyword) + " : "
				+ _good + " : " + _bad;
	}
}
