/*
 * Copyright 2009 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.zxing.oned.rss;

/** Adapted from listings in ISO/IEC 24724 Appendix B and Appendix G. */
public final class RSSUtils {

  private RSSUtils() {}

  static int[] getRSSwidths(int val, int n, int elements, int maxWidth, boolean noNarrow) {
    int[] widths = new int[elements];
    int bar;
    int narrowMask = 0;
    for (bar = 0; bar < elements - 1; bar++) {
      narrowMask |= 1 << bar;
      int elmWidth = 1;
      int subVal;
      while (true) {
        subVal = combins(n - elmWidth - 1, elements - bar - 2);
        if (noNarrow && (narrowMask == 0) &&
            (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
          subVal -= combins(n - elmWidth - (elements - bar), elements - bar - 2);
        }
        if (elements - bar - 1 > 1) {
          int lessVal = 0;
          for (int mxwElement = n - elmWidth - (elements - bar - 2);
               mxwElement > maxWidth;
               mxwElement--) {
            lessVal += combins(n - elmWidth - mxwElement - 1, elements - bar - 3);
          }
          subVal -= lessVal * (elements - 1 - bar);
        } else if (n - elmWidth > maxWidth) {
          subVal--;
        }
        val -= subVal;
        if (val < 0) {
          break;
        }
        elmWidth++;
        narrowMask &= ~(1 << bar);
      }
      val += subVal;
      n -= elmWidth;
      widths[bar] = elmWidth;
    }
    widths[bar] = n;
    return widths;
  }

  public static int getRSSvalue(int[] widths, int maxWidth, boolean noNarrow) {
    int elements = widths.length;
    int n = 0;
    for (int width : widths) {
      n += width;
    }
    int val = 0;
    int narrowMask = 0;
    for (int bar = 0; bar < elements - 1; bar++) {
      int elmWidth;
      for (elmWidth = 1, narrowMask |= 1 << bar;
           elmWidth < widths[bar];
           elmWidth++, narrowMask &= ~(1 << bar)) {
        int subVal = combins(n - elmWidth - 1, elements - bar - 2);
        if (noNarrow && (narrowMask == 0) &&
            (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
          subVal -= combins(n - elmWidth - (elements - bar),
                            elements - bar - 2);
        }
        if (elements - bar - 1 > 1) {
          int lessVal = 0;
          for (int mxwElement = n - elmWidth - (elements - bar - 2);
               mxwElement > maxWidth; mxwElement--) {
            lessVal += combins(n - elmWidth - mxwElement - 1,
                               elements - bar - 3);
          }
          subVal -= lessVal * (elements - 1 - bar);
        } else if (n - elmWidth > maxWidth) {
          subVal--;
        }
        val += subVal;
      }
      n -= elmWidth;
    }
    return val;
  }

  private static int combins(int n, int r) {
    int maxDenom;
    int minDenom;
    if (n - r > r) {
      minDenom = r;
      maxDenom = n - r;
    } else {
      minDenom = n - r;
      maxDenom = r;
    }
    int val = 1;
    int j = 1;
    for (int i = n; i > maxDenom; i--) {
      val *= i;
      if (j <= minDenom) {
        val /= j;
        j++;
      }
    }
    while (j <= minDenom) {
      val /= j;
      j++;
    }
    return val;
  }

  static int[] elements(int[] eDist, int N, int K) {
    int[] widths = new int[eDist.length + 2];
    int twoK = K << 1;
    widths[0] = 1;
    int i;
    int minEven = 10;
    int barSum = 1;
    for (i = 1; i < twoK - 2; i += 2) {
      widths[i] = eDist[i - 1] - widths[i - 1];
      widths[i + 1] = eDist[i] - widths[i];
      barSum += widths[i] + widths[i + 1];
      if (widths[i] < minEven) {
        minEven = widths[i];
      }
    }
    widths[twoK - 1] = N - barSum;
    if (widths[twoK - 1] < minEven) {
      minEven = widths[twoK - 1];
    }
    if (minEven > 1) {
      for (i = 0; i < twoK; i += 2) {
        widths[i] += minEven - 1;
        widths[i + 1] -= minEven - 1;
      }
    }
    return widths;
  }


}
