package com.airbnb.android.react.navigation;

import android.support.annotation.NonNull;
import android.text.TextUtils;

/**
 * Helper to create higher fidelity transition names. <p> This can be used by shared element
 * transitions to understand what a particular element actually corresponds to. For example, a
 * listing card may use listing|1337|photo|0 which would have the exact same transition name in a
 * listing marquee and image viewer. However, if the user scrolls to photo 5 within the Listing
 * Marquee and then goes back to a page with the listing card, it could be understood that
 * listing|1337|photo|5 can be transitioned to listing|1337|photo|0 with a crossfade.
 */
public class TransitionName {
  private static final char DELIMETER = '|';
  private final String type;
  private final long id;
  private final String subtype;
  private final long subId;

  private TransitionName(String type, long id, String subtype, long subId) {
    this.type = type;
    this.id = id;
    this.subtype = subtype;
    this.subId = subId;
  }

  public static TransitionName create(@NonNull String type) {
    return create(type, 0);
  }

  public static TransitionName create(@NonNull String type, long id) {
    return create(type, id, "");
  }

  public static TransitionName create(@NonNull String type, long id, @NonNull String subtype) {
    return create(type, id, subtype, 0);
  }

  /**
   * Use empty string instead of null of subtype is not applicabale.
   */
  public static TransitionName create(
      @NonNull String type, long id, @NonNull String subtype, long subId) {
    return new TransitionName(type, id, subtype, subId);
  }

  public static String toString(@NonNull String type) {
    return toString(type, 0);
  }

  public static String toString(@NonNull String type, long id) {
    return toString(type, id, "");
  }


  public static String toString(@NonNull String type, long id, @NonNull String subtype) {
    return toString(type, id, subtype, 0);
  }

  /**
   * Use empty string instead of null of subtype is not applicabale.
   */
  public static String toString(
      @NonNull String type, long id, @NonNull String subtype, long subId) {
    if (type.indexOf(DELIMETER) != -1) {
      throw new IllegalArgumentException("Invalid type " + type + ". Delimeter is " + DELIMETER);
    } else if (subtype.indexOf(DELIMETER) != -1) {
      throw new IllegalArgumentException(
          "Invalid subtype " + subtype + ". Delimeter is " + DELIMETER);
    }

    return type + DELIMETER + id + DELIMETER + subtype + DELIMETER + subId;
  }

  public static TransitionName parse(String transitionNameString) {
    if (TextUtils.isEmpty(transitionNameString)) {
      return new TransitionName("", 0, "", 0);
    }

    String[] parsed = transitionNameString.split("[" + DELIMETER + "]");
    switch (parsed.length) {
      case 1:
        return new TransitionName(parsed[0], 0, "", 0);
      case 2:
        return new TransitionName(parsed[0], Long.parseLong(parsed[1]), "", 0);
      case 3:
        return new TransitionName(parsed[0], Long.parseLong(parsed[1]), parsed[2], 0);
      case 4:
        return new TransitionName(parsed[0], Long.parseLong(parsed[1]), parsed[2], Long
            .parseLong(parsed[3]));
      default:
        throw new IllegalArgumentException(
            "Invalid transition name " + transitionNameString + ". Split into " + parsed.length +
                ". Should be less than 4.");
    }
  }

  /**
   * Returns whether at least the type, id, and subtype match. This could be useful when there are 2
   * elements that represent the same thing (like a listing photo) but they are just different
   * indices. With this knowledge, we can safely maintain the shared element mapping and crossfade
   * them instead.
   */
  public boolean partialEquals(TransitionName other) {
    return type.equals(other.type) && id == other.id && subtype.equals(other.subtype);
  }

  public long subId() {
    return subId;
  }

  public long id() {
    return id;
  }

  public String type() {
    return type;
  }
}
