/* Copyright (C) 2005-2011 Fabio Riccardi */

package com.lightcrafts.image.metadata.values;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import com.lightcrafts.image.metadata.ImageMetaType;
import com.lightcrafts.image.metadata.ImageMetadataDirectory;
import com.lightcrafts.utils.LCArrays;

import static com.lightcrafts.image.metadata.ImageMetaType.META_STRING;

/**
 * A <code>StringMetaValue</code> is-an {@link ImageMetaValue} for holding a
 * string metadata value.
 *
 * @author Paul J. Lucas [paul@lightcrafts.com]
 */
public final class StringMetaValue extends ImageMetaValue {

    ////////// public /////////////////////////////////////////////////////////

    /**
     * Construct a <code>StringMetaValue</code>.
     * This constructor exists only for externalization.
     */
    public StringMetaValue() {
        // do nothing
    }

    /**
     * Construct a <code>StringMetaValue</code>.
     *
     * @param values The array of values.
     */
    public StringMetaValue( String... values ) {
        if ( values != null && values.length > 0 ) {
            m_value = new String[ values.length ];
            System.arraycopy( values, 0, m_value, 0, values.length );
        }
    }

    /**
     * {@inheritDoc}
     */
    public StringMetaValue clone() {
        final StringMetaValue copy = (StringMetaValue)super.clone();
        copy.m_value = m_value.clone();
        return copy;
    }

    /**
     * {@inheritDoc}
     */
    public int compareTo( Object o ) {
        if ( o instanceof StringMetaValue ) {
            final StringMetaValue rightValue = (StringMetaValue)o;
            final String leftString = getStringValue().toLowerCase();
            final String rightString =
                rightValue.getStringValue().toLowerCase();
            return leftString.compareTo( rightString );
        }
        return super.compareTo( o );
    }

    /**
     * Compares this <code>ImageMetaValue</code> to a {@link String}.  A
     * case-insensitive comparison is done.
     *
     * @param s The {@link String} to compare to.
     * @return Returns a negative integer, zero, or a positive integer as this
     * <code>ImageMetaValue</code> is less than, equal to, or greater than the
     * string.
     * @see #compareTo(Object)
     */
    public int compareTo( String s ) {
        final String leftString = getStringValue();
        if ( leftString == null )
            return s == null ? 0 : -1;
        return leftString.toLowerCase().compareTo( s.toLowerCase() );
    }

    /**
     * {@inheritDoc}
     */
    public double getDoubleValue() {
        try {
            return Double.parseDouble( getStringValue() );
        }
        catch ( NumberFormatException e ) {
            return 0;
        }
    }

    /**
     * {@inheritDoc}
     */
    public long getLongValue() {
        try {
            return Long.parseLong( getStringValue() );
        }
        catch ( NumberFormatException e ) {
            return 0;
        }
    }

    /**
     * Gets the type of this metadata value.
     *
     * @return Always returns <code>META_STRING</code>.
     */
    public ImageMetaType getType() {
        return META_STRING;
    }

    /**
     * {@inheritDoc}
     */
    public int getValueCount() {
        return m_value != null ? m_value.length : 0;
    }

    /**
     * {@inheritDoc}
     */
    public void setDoubleValue( double newValue ) {
        setStringValueAt( Double.toString( newValue ), 0 );
    }

    /**
     * {@inheritDoc}
     */
    public void setLongValue( long newValue ) {
        setStringValueAt( Long.toString( newValue ), 0 );
    }

    /**
     * Sets the {@link String} value at the given index.
     *
     * @param newValue The new value.
     * @param index The index to set the value of.
     */
    public synchronized void setStringValueAt( String newValue, int index ) {
        checkIsEditable();
        if ( m_value == null )
            m_value = new String[ index + 1 ];
        else if ( index >= m_value.length )
            m_value = (String[])LCArrays.resize( m_value, index + 1 );
        m_value[ index ] = newValue;
        dirty();
    }

    /**
     * {@inheritDoc}
     */
    public void readExternal( ObjectInput in ) throws IOException {
        final int length = readHeader( in );
        m_value = new String[ length ];
        for ( int i = 0; i < length; ++i )
            m_value[i] = in.readUTF();
    }

    /**
     * @serialData The header (see {@link #writeHeader(ObjectOutput)}) followed
     * by the UTF-8 encoding of the {@link String} values.
     */
    public void writeExternal( ObjectOutput out ) throws IOException {
        writeHeader( out );
        for ( String value : m_value )
            out.writeUTF( value );
    }

    ////////// protected //////////////////////////////////////////////////////

    /**
     * Parse and append a new value.
     *
     * @param newValue The new value.
     */
    protected void appendValueImpl( String newValue ) {
        if ( m_value == null )
            m_value = new String[]{ newValue };
        else {
            m_value = (String[])LCArrays.resize( m_value, m_value.length + 1 );
            m_value[ m_value.length - 1 ] = newValue;
        }
    }

    /**
     * Gets the values as an array of {@link String}.
     *
     * @return Returns said array.
     */
    protected String[] getValuesImpl() {
        if ( m_value == null )
            return null;
        final String[] value = new String[ m_value.length ];
        System.arraycopy( m_value, 0, value, 0, m_value.length );
        return value;
    }

    /**
     * Parse and set the values.
     *
     * @param newValue The array of new values.
     */
    protected void setValuesImpl( String[] newValue ) {
        if ( newValue == null ) {
            m_value = null;
            return;
        }
        if ( m_value == null || m_value.length != newValue.length )
            m_value = new String[ newValue.length ];
        System.arraycopy( newValue, 0, m_value, 0, newValue.length );
    }

    /**
     * Convert this value to its {@link String} representation.  Multiple
     * values are separated by commas.
     *
     * @return Returns said string.
     */
    protected String toStringImpl() {
        if ( m_value == null )
            return null;
        final ImageMetadataDirectory owningDir = getOwningDirectory();
        final int tagID = getOwningTagID();
        final StringBuilder sb = new StringBuilder();
        boolean comma = false;
        for ( String value : m_value ) {
            if ( !comma )
                comma = true;
            else
                sb.append( ',' );
            if ( owningDir != null )
                value = owningDir.getTagValueLabelFor( tagID, value );
            sb.append( value );
        }
        return sb.toString();
    }

    ////////// private ////////////////////////////////////////////////////////

    private String[] m_value;
}
/* vim:set et sw=4 ts=4: */
