package okuki.android;

import android.os.Parcel;
import android.os.Parcelable;

import java.io.Serializable;

import okuki.Place;
import okuki.SimplePlace;

class WrappedPlace<T extends Place> implements Parcelable {
    private final Class<T> placeClass;
    private final Class dataClass;
    private final Object data;

    WrappedPlace(T place) {
        placeClass = (Class<T>) place.getClass();
        data = place.getData();
        if (data == null) {
            dataClass = Void.class;
        } else {
            dataClass = data.getClass();
        }
    }

    private WrappedPlace(Parcel parcel) {
        placeClass = (Class<T>) parcel.readSerializable();
        dataClass = (Class) parcel.readSerializable();
        if (Void.class.equals(dataClass)) {
            data = parcel.readValue(String.class.getClassLoader());
        } else if (Parcelable.class.isAssignableFrom(dataClass)) {
            data = parcel.readParcelable(dataClass.getClassLoader());
        } else if (Serializable.class.isAssignableFrom(dataClass)) {
            data = parcel.readSerializable();
        } else {
            throw new RuntimeException(String.format("Error restoring place %s", placeClass));
        }
    }

    T getPlace() {
        try {
            if (SimplePlace.class.isAssignableFrom(placeClass)) {
                return (T) placeClass.newInstance();
            } else {
                return (T) placeClass.getConstructor(new Class[]{dataClass}).newInstance(data);
            }
        } catch (Exception e) {
            throw new RuntimeException(String.format("Error getting place %s", placeClass));
        }
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeSerializable(placeClass);
        parcel.writeSerializable(dataClass);
        if (Void.class.equals(dataClass)) {
            parcel.writeValue(null);
        } else if (Parcelable.class.isAssignableFrom(dataClass)) {
            parcel.writeParcelable((Parcelable) data, 0);
        } else if (Serializable.class.isAssignableFrom(dataClass)) {
            parcel.writeSerializable((Serializable) data);
        } else {
            throw new RuntimeException(String.format("Error restoring place %s", placeClass));
        }
    }

    public static final Creator<WrappedPlace> CREATOR = new Creator<WrappedPlace>() {
        @Override
        public WrappedPlace createFromParcel(Parcel parcel) {
            return new WrappedPlace(parcel);
        }

        @Override
        public WrappedPlace[] newArray(int size) {
            return new WrappedPlace[size];
        }
    };

}