package de.renew.formalism.function;

import java.io.IOException;
import java.io.Serial;
import java.lang.reflect.Field;

import de.renew.expression.NoArgFunction;
import de.renew.unify.Impossible;
import de.renew.util.ReflectionSerializer;
import de.renew.util.Value;

/**
 * Represents a function that retrieves the value of a static field.
 */
public class StaticFieldFunction implements NoArgFunction {

    /**
     * This field is not really transient, but as the reflection
     * classes are not serializable, we have to store it by
     * ourselves.
     **/
    transient private Field _field;

    /**
     * Creates an instance of this class from a field.
     *
     * @param field a field
     */
    public StaticFieldFunction(Field field) {
        this._field = field;
    }

    @Override
    public Object function() throws Impossible {
        try {
            return Value.possiblyWrap(_field.get(null), _field.getType().isPrimitive());
        } catch (Exception e) {
            if (e instanceof NullPointerException) {
                throw new Impossible(
                    "Static field access impossible: non-static field (" + _field + ")", e);
            } else {
                throw new Impossible(
                    "Exception occured during static field access (" + _field + "): " + e, e);
            }
        }
    }

    /**
     * Serialization method, behaves like default writeObject
     * method. Stores the not-really-transient field field.
     *
     * @param out the output stream to write to
     * @throws IOException if writing the object fails
     **/
    @Serial
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        ReflectionSerializer.writeField(out, _field);
    }

    /**
     * Deserialization method, behaves like default readObject
     * method. Restores the not-really-transient field field.
     *
     * @param in the input stream to read from
     * @throws IOException if reading the object fails
     * @throws ClassNotFoundException if a required class is not found during reading
     **/
    @Serial
    private void readObject(java.io.ObjectInputStream in)
        throws IOException, ClassNotFoundException
    {
        in.defaultReadObject();
        _field = ReflectionSerializer.readField(in);
    }

    @Override
    public String toString() {
        return "StaticFieldFunc(" + _field + ")";
    }
}