/*
 * Copyright (C) 2020 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.product.etd;

import java.lang.invoke.MethodHandles;
import java.time.YearMonth;
import java.util.Optional;

import org.joda.beans.ImmutableBean;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaBean;
import org.joda.beans.TypedMetaBean;
import org.joda.beans.gen.BeanDefinition;
import org.joda.beans.gen.PropertyDefinition;
import org.joda.beans.impl.light.LightMetaBean;

import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.product.common.PutCall;

/**
 * The option fields of a split OG-ETD identifier.
 */
@BeanDefinition(style = "light")
public final class SplitEtdOption implements ImmutableBean {

  /**
   * The version of the option, defaulted to zero.
   * <p>
   * Some options can have multiple versions, representing some kind of change over time.
   * Version zero is the baseline, version one and later indicates some kind of change occurred.
   */
  @PropertyDefinition(validate = "ArgChecker.notNegative")
  private final int version;
  /**
   * Whether the option is a put or call.
   */
  @PropertyDefinition(validate = "notNull")
  private final PutCall putCall;
  /**
   * The strike price, in decimal form, may be negative.
   */
  @PropertyDefinition
  private final double strikePrice;
  /**
   * The expiry year-month of the underlying instrument.
   * <p>
   * If an option has an underlying instrument, the expiry of that instrument can be specified here.
   * For example, you can have an option expiring in March on the underlying March future, or on the underlying June future.
   * Not all options have an underlying instrument, thus the property is optional.
   * <p>
   * In many cases, the expiry of the underlying instrument is the same as the expiry of the option.
   * In this case, the expiry is often omitted, even though it probably should not be.
   */
  @PropertyDefinition(get = "optional")
  private final YearMonth underlyingExpiryMonth;

  //-------------------------------------------------------------------------
  /**
   * Obtains an instance.
   * 
   * @param version the version
   * @param putCall the put/call flag
   * @param strikePrice the strike price
   * @return the instance
   */
  public static SplitEtdOption of(int version, PutCall putCall, double strikePrice) {
    return new SplitEtdOption(version, putCall, strikePrice, null);
  }

  /**
   * Obtains an instance.
   * 
   * @param version the version
   * @param putCall the put/call flag
   * @param strikePrice the strike price
   * @param underlyingExpiryMonth the month of the underlying expiry
   * @return the instance
   */
  public static SplitEtdOption of(
      int version,
      PutCall putCall,
      double strikePrice,
      YearMonth underlyingExpiryMonth) {

    return new SplitEtdOption(version, putCall, strikePrice, underlyingExpiryMonth);
  }

  //------------------------- AUTOGENERATED START -------------------------
  /**
   * The meta-bean for {@code SplitEtdOption}.
   */
  private static final TypedMetaBean<SplitEtdOption> META_BEAN =
      LightMetaBean.of(
          SplitEtdOption.class,
          MethodHandles.lookup(),
          new String[] {
              "version",
              "putCall",
              "strikePrice",
              "underlyingExpiryMonth"},
          new Object[0]);

  /**
   * The meta-bean for {@code SplitEtdOption}.
   * @return the meta-bean, not null
   */
  public static TypedMetaBean<SplitEtdOption> meta() {
    return META_BEAN;
  }

  static {
    MetaBean.register(META_BEAN);
  }

  private SplitEtdOption(
      int version,
      PutCall putCall,
      double strikePrice,
      YearMonth underlyingExpiryMonth) {
    ArgChecker.notNegative(version, "version");
    JodaBeanUtils.notNull(putCall, "putCall");
    this.version = version;
    this.putCall = putCall;
    this.strikePrice = strikePrice;
    this.underlyingExpiryMonth = underlyingExpiryMonth;
  }

  @Override
  public TypedMetaBean<SplitEtdOption> metaBean() {
    return META_BEAN;
  }

  //-----------------------------------------------------------------------
  /**
   * Gets the version of the option, defaulted to zero.
   * <p>
   * Some options can have multiple versions, representing some kind of change over time.
   * Version zero is the baseline, version one and later indicates some kind of change occurred.
   * @return the value of the property
   */
  public int getVersion() {
    return version;
  }

  //-----------------------------------------------------------------------
  /**
   * Gets whether the option is a put or call.
   * @return the value of the property, not null
   */
  public PutCall getPutCall() {
    return putCall;
  }

  //-----------------------------------------------------------------------
  /**
   * Gets the strike price, in decimal form, may be negative.
   * @return the value of the property
   */
  public double getStrikePrice() {
    return strikePrice;
  }

  //-----------------------------------------------------------------------
  /**
   * Gets the expiry year-month of the underlying instrument.
   * <p>
   * If an option has an underlying instrument, the expiry of that instrument can be specified here.
   * For example, you can have an option expiring in March on the underlying March future, or on the underlying June future.
   * Not all options have an underlying instrument, thus the property is optional.
   * <p>
   * In many cases, the expiry of the underlying instrument is the same as the expiry of the option.
   * In this case, the expiry is often omitted, even though it probably should not be.
   * @return the optional value of the property, not null
   */
  public Optional<YearMonth> getUnderlyingExpiryMonth() {
    return Optional.ofNullable(underlyingExpiryMonth);
  }

  //-----------------------------------------------------------------------
  @Override
  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }
    if (obj != null && obj.getClass() == this.getClass()) {
      SplitEtdOption other = (SplitEtdOption) obj;
      return (version == other.version) &&
          JodaBeanUtils.equal(putCall, other.putCall) &&
          JodaBeanUtils.equal(strikePrice, other.strikePrice) &&
          JodaBeanUtils.equal(underlyingExpiryMonth, other.underlyingExpiryMonth);
    }
    return false;
  }

  @Override
  public int hashCode() {
    int hash = getClass().hashCode();
    hash = hash * 31 + JodaBeanUtils.hashCode(version);
    hash = hash * 31 + JodaBeanUtils.hashCode(putCall);
    hash = hash * 31 + JodaBeanUtils.hashCode(strikePrice);
    hash = hash * 31 + JodaBeanUtils.hashCode(underlyingExpiryMonth);
    return hash;
  }

  @Override
  public String toString() {
    StringBuilder buf = new StringBuilder(160);
    buf.append("SplitEtdOption{");
    buf.append("version").append('=').append(JodaBeanUtils.toString(version)).append(',').append(' ');
    buf.append("putCall").append('=').append(JodaBeanUtils.toString(putCall)).append(',').append(' ');
    buf.append("strikePrice").append('=').append(JodaBeanUtils.toString(strikePrice)).append(',').append(' ');
    buf.append("underlyingExpiryMonth").append('=').append(JodaBeanUtils.toString(underlyingExpiryMonth));
    buf.append('}');
    return buf.toString();
  }

  //-------------------------- AUTOGENERATED END --------------------------
}
