/**
   * Method that load the gles texture and apply to the requestor frame (which includes fix the
   * aspect ratio and/or effects and borders)
   *
   * @param requestor The requestor target
   * @param ti The original texture information (the one with the bitmap one)
   */
  private void applyToRequestor(TextureRequestor requestor, GLESTextureInfo ti) {
    // Transform requestor dimensions to screen dimensions
    RectF dimens = requestor.getRequestorDimensions();
    Rect pixels =
        new Rect(
            0,
            0,
            (int) (mScreenDimensions.width() * dimens.width() / 2),
            (int) (mScreenDimensions.height() * dimens.height() / 2));

    final Disposition disposition = requestor.getDisposition();
    synchronized (mEffectsSync) {
      if (disposition.hasFlag(Disposition.EFFECT_FLAG)) {
        ti.effect = mEffects.getNextEffect();
      }
      if (disposition.hasFlag(Disposition.BORDER_FLAG)) {
        ti.border = mBorders.getNextBorder();
      }
    }

    // Check if we have to apply any correction to the image
    GLESTextureInfo dst;
    if (ti.bitmap != null && Preferences.General.isFixAspectRatio(mContext)) {

      // Create a texture of power of two here to avoid scaling the bitmap twice
      int w = pixels.width();
      int h = pixels.height();
      if (!BitmapUtils.isPowerOfTwo(w, h)
          && PreferencesProvider.Preferences.General.isPowerOfTwo(mContext)) {
        w = h = BitmapUtils.calculateUpperPowerOfTwo(Math.min(w, h));
      }

      // Create a thumbnail of the image
      Bitmap thumb = BitmapUtils.createScaledBitmap(ti.bitmap, w, h, BitmapUtils.ScalingLogic.CROP);
      if (!thumb.equals(ti.bitmap)) {
        ti.bitmap.recycle();
      }
      dst = GLESUtil.loadTexture(mContext, thumb, ti.effect, ti.border, pixels);
    } else {
      // Load the texture without any correction
      dst = GLESUtil.loadTexture(mContext, ti.bitmap, ti.effect, ti.border, pixels);
    }

    // Swap references
    ti.bitmap = dst.bitmap;
    ti.handle = dst.handle;
    ti.effect = null;
    ti.border = null;
    dst.handle = 0;
    dst.bitmap = null;

    // And notify to the requestor
    requestor.setTextureHandle(ti);

    // Clean up memory
    if (ti.bitmap != null) {
      ti.bitmap.recycle();
      ti.bitmap = null;
    }
  }
  /**
   * Method that converts the resize frame to a dispostion reference
   *
   * @return Disposition The disposition reference
   */
  private Disposition resizerToDisposition() {
    int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight());
    int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom());
    int cw = w / mCols;
    int ch = h / mRows;

    // Remove overlapped areas
    Disposition resizer = new Disposition();
    resizer.x = Math.round(mResizeFrame.getX() / cw);
    resizer.y = Math.round(mResizeFrame.getY() / ch);
    resizer.w = Math.round(mResizeFrame.getWidth() / cw);
    resizer.h = Math.round(mResizeFrame.getHeight() / ch);

    // Fix disposition (limits)
    resizer.x = Math.max(resizer.x, 0);
    resizer.y = Math.max(resizer.y, 0);
    resizer.w = Math.min(resizer.w, mCols - resizer.x);
    resizer.h = Math.min(resizer.h, mRows - resizer.y);

    return resizer;
  }
  /**
   * Method that returns all dispositions that matched exactly (in one side) with the argument
   * disposition.
   *
   * @param disposition The disposition to check
   */
  private List<Disposition> findAdjacentsDispositions(Disposition disposition) {
    if (mDispositions.size() <= 1) return null;

    // Check left size
    if (disposition.x != 0) {
      List<Disposition> dispositions = new ArrayList<Disposition>();
      for (Disposition d : mDispositions) {
        if (d.compareTo(disposition) != 0) {
          if ((d.x + d.w) == disposition.x
              && (d.y >= disposition.y)
              && ((d.y + d.h) <= (disposition.y + disposition.h))) {
            dispositions.add(d);
          }
        }
      }
      // Check if the sum of all the dispositions matches the disposition
      int sum = 0;
      for (Disposition d : dispositions) {
        sum += d.h;
      }
      if (sum == disposition.h) {
        return dispositions;
      }
    }
    // Check top size
    if (disposition.y != 0) {
      List<Disposition> dispositions = new ArrayList<Disposition>();
      for (Disposition d : mDispositions) {
        if (d.compareTo(disposition) != 0) {
          if ((d.y + d.h) == disposition.y
              && (d.x >= disposition.x)
              && ((d.x + d.w) <= (disposition.x + disposition.w))) {
            dispositions.add(d);
          }
        }
      }
      // Check if the sum of all the dispositions matches the disposition
      int sum = 0;
      for (Disposition d : dispositions) {
        sum += d.w;
      }
      if (sum == disposition.w) {
        return dispositions;
      }
    }
    // Check right size
    if ((disposition.x + disposition.w) != mCols) {
      List<Disposition> dispositions = new ArrayList<Disposition>();
      for (Disposition d : mDispositions) {
        if (d.compareTo(disposition) != 0) {
          if ((d.x) == (disposition.x + disposition.w)
              && (d.y >= disposition.y)
              && ((d.y + d.h) <= (disposition.y + disposition.h))) {
            dispositions.add(d);
          }
        }
      }
      // Check if the sum of all the dispositions matches the disposition
      int sum = 0;
      for (Disposition d : dispositions) {
        sum += d.h;
      }
      if (sum == disposition.h) {
        return dispositions;
      }
    }
    // Check bottom size
    if ((disposition.y + disposition.h) != mRows) {
      List<Disposition> dispositions = new ArrayList<Disposition>();
      for (Disposition d : mDispositions) {
        if (d.compareTo(disposition) != 0) {
          if ((d.y) == (disposition.y + disposition.h)
              && (d.x >= disposition.x)
              && ((d.x + d.w) <= (disposition.x + disposition.w))) {
            dispositions.add(d);
          }
        }
      }
      // Check if the sum of all the dispositions matches the disposition
      int sum = 0;
      for (Disposition d : dispositions) {
        sum += d.w;
      }
      if (sum == disposition.w) {
        return dispositions;
      }
    }
    return null;
  }
  /** Method that request the deletion of the current selected frame */
  @SuppressWarnings("boxing")
  public void deleteCurrentFrame() {
    if (mTarget == null) return;
    if (mResizeFrame == null) return;

    final Disposition targetDisposition = resizerToDisposition();

    // Get valid dispositions to move
    final List<Disposition> adjacents = findAdjacentsDispositions(targetDisposition);
    if (adjacents == null) {
      // Nothing to do
      Toast.makeText(
              getContext(), R.string.pref_disposition_unable_delete_advise, Toast.LENGTH_SHORT)
          .show();
      return;
    }

    // Hide resizer
    mResizeFrame.setVisibility(View.GONE);

    // Animate adjacents views
    List<Animator> animators = new ArrayList<Animator>();
    animators.add(ObjectAnimator.ofFloat(mTarget, "scaleX", 1.0f, 0.0f));
    animators.add(ObjectAnimator.ofFloat(mTarget, "scaleY", 1.0f, 0.0f));

    Disposition first = null;
    for (Disposition adjacent : adjacents) {
      // Extract the view and remove from dispositions
      View v = findViewFromRect(getLocationFromDisposition(adjacent));
      mDispositions.remove(adjacent);

      // Clone first disposition
      if (first == null) {
        first = new Disposition();
        first.x = adjacent.x;
        first.y = adjacent.y;
        first.w = adjacent.w;
        first.h = adjacent.h;
      }

      // Add animators and fix the adjacent
      if (v != null) {
        if (first.x < targetDisposition.x) {
          // From Left to Right
          int width = mTarget.getWidth() + mInternalPadding;
          animators.add(
              ValueAnimator.ofObject(
                  new Evaluators.WidthEvaluator(v), v.getWidth(), v.getWidth() + width));

          // Update the adjacent
          adjacent.w += targetDisposition.w;
          mDispositions.add(adjacent);

        } else if (first.x > targetDisposition.x) {
          // From Right to Left
          int width = mTarget.getWidth() + mInternalPadding;
          animators.add(
              ValueAnimator.ofObject(
                  new Evaluators.WidthEvaluator(v), v.getWidth(), v.getWidth() + width));
          animators.add(ObjectAnimator.ofFloat(v, "x", v.getX(), mTarget.getX()));

          // Update the adjacent
          adjacent.x = targetDisposition.x;
          adjacent.w += targetDisposition.w;
          mDispositions.add(adjacent);

        } else if (first.y < targetDisposition.y) {
          // From Top to Bottom
          int height = mTarget.getHeight() + mInternalPadding;
          animators.add(
              ValueAnimator.ofObject(
                  new Evaluators.HeightEvaluator(v), v.getHeight(), v.getHeight() + height));

          // Update the adjacent
          adjacent.h += targetDisposition.h;
          mDispositions.add(adjacent);

        } else if (first.y > targetDisposition.y) {
          // From Bottom to Top
          int height = mTarget.getHeight() + mInternalPadding;
          animators.add(
              ValueAnimator.ofObject(
                  new Evaluators.HeightEvaluator(v), v.getHeight(), v.getHeight() + height));
          animators.add(ObjectAnimator.ofFloat(v, "y", v.getY(), mTarget.getY()));

          // Update the adjacent
          adjacent.y = targetDisposition.y;
          adjacent.h += targetDisposition.h;
          mDispositions.add(adjacent);
        }
      }
    }
    if (animators.size() > 0) {
      AnimatorSet animSet = new AnimatorSet();
      animSet.playTogether(animators);
      animSet.setDuration(getResources().getInteger(R.integer.disposition_hide_anim));
      animSet.setInterpolator(new AccelerateInterpolator());
      animSet.addListener(
          new AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
              // Ignore
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
              // Ignore
            }

            @Override
            public void onAnimationEnd(Animator animation) {
              finishDeleteAnimation(targetDisposition);
            }

            @Override
            public void onAnimationCancel(Animator animation) {
              finishDeleteAnimation(targetDisposition);
            }
          });
      animSet.start();
    }
  }