package cz.jzitnik.game.entities;

import java.util.NoSuchElementException;
import java.util.function.*;

public class MyOptional<T> {
    private T value;
    private boolean isPresent;

    public MyOptional() {
        this.isPresent = false;
    }

    public MyOptional(T value) {
        this.value = value;
        this.isPresent = true;
    }

    public boolean isPresent() {
        return isPresent;
    }

    public T get() {
        if (!isPresent) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    public T orElse(T other) {
        return isPresent ? value : other;
    }

    public <U> MyOptional<U> map(Function<? super T, ? extends U> mapper) {
        if (!isPresent) {
            return new MyOptional<>();
        }
        return new MyOptional<>(mapper.apply(value));
    }

    public <U> MyOptional<U> flatMap(Function<? super T, ? extends MyOptional<U>> mapper) {
        if (!isPresent) {
            return new MyOptional<>();
        }
        return mapper.apply(value);
    }

    public void ifPresent(Consumer<? super T> action) {
        if (isPresent) {
            action.accept(value);
        }
    }

    public T orElseGet(Supplier<? extends T> other) {
        return isPresent ? value : other.get();
    }

    // Filter the value based on a condition
    public MyOptional<T> filter(Predicate<? super T> predicate) {
        if (!isPresent || !predicate.test(value)) {
            return new MyOptional<>();
        }
        return this;
    }

    @Override
    public String toString() {
        return isPresent ? "SerializableOptional[" + value + "]" : "SerializableOptional.empty";
    }

    public static <T> MyOptional<T> empty() {
        return new MyOptional<>();
    }

    public static <T> MyOptional<T> of(T value) {
        return new MyOptional<>(value);
    }

    public static <T> MyOptional<T> ofNullable(T value) {
        return value == null ? empty() : new MyOptional<>(value);
    }

    public boolean isEmpty() {
        return !isPresent;
    }
}
