content: /*
   *  UVCCamera
   *  library and sample to access to UVC web camera on non-rooted Android device
   *
   * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
   *
   *  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.
   *
   *  All files in the folder are under this Apache License, Version 2.0.
   *  Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
   *  may have a different license, see the respective files.
   */
  package com.serenegiant.usb;
  
  import android.os.Parcel;
  import android.os.Parcelable;
  
  import java.util.Locale;
  
  public class Size implements Parcelable {
  	//
  	public static final Creator<Size> CREATOR = new Creator<Size>() {
  		@Override
  		public Size createFromParcel(final Parcel source) {
  			return new Size(source);
  		}
  
  		@Override
  		public Size[] newArray(final int size) {
  			return new Size[size];
  		}
  	};
  	/**
  	 * native側のuvc_raw_format_tの値, こっちは主にlibuvc用
  	 * 9999 is still image
  	 */
  	public int type;
  	/**
  	 * native側のraw_frame_tの値, androusb用,
  	 * libuvcは対応していない
  	 */
  	public int frame_type;
  	public int index;
  	public int width;
  	public int height;
  	public int frameIntervalType;
  	public int frameIntervalIndex;
  	public int[] intervals;
  	// ここ以下はframeIntervalTypeとintervalsから#updateFrameRateで計算する
  	public float[] fps;
  	private String frameRates;
  
  	/**
  	 * コンストラクタ
  	 *
  	 * @param _type       native側のraw_format_tの値, ただし9999は静止画
  	 * @param _frame_type native側のraw_frame_tの値
  	 * @param _index
  	 * @param _width
  	 * @param _height
  	 */
  	public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height) {
  		type = _type;
  		frame_type = _frame_type;
  		index = _index;
  		width = _width;
  		height = _height;
  		frameIntervalType = -1;
  		frameIntervalIndex = 0;
  		intervals = null;
  		updateFrameRate();
  	}
  
  	/**
  	 * コンストラクタ
  	 *
  	 * @param _type          native側のraw_format_tの値, ただし9999は静止画
  	 * @param _frame_type    native側のraw_frame_tの値
  	 * @param _index
  	 * @param _width
  	 * @param _height
  	 * @param _min_intervals
  	 * @param _max_intervals
  	 */
  	public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int _min_intervals, final int _max_intervals, final int _step) {
  		type = _type;
  		frame_type = _frame_type;
  		index = _index;
  		width = _width;
  		height = _height;
  		frameIntervalType = 0;
  		frameIntervalIndex = 0;
  		intervals = new int[3];
  		intervals[0] = _min_intervals;
  		intervals[1] = _max_intervals;
  		intervals[2] = _step;
  		updateFrameRate();
  	}
  
  	/**
  	 * コンストラクタ
  	 *
  	 * @param _type       native側のraw_format_tの値, ただし9999は静止画
  	 * @param _frame_type native側のraw_frame_tの値
  	 * @param _index
  	 * @param _width
  	 * @param _height
  	 * @param _intervals
  	 */
  	public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int[] _intervals) {
  		type = _type;
  		frame_type = _frame_type;
  		index = _index;
  		width = _width;
  		height = _height;
  		final int n = _intervals != null ? _intervals.length : -1;
  		if (n > 0) {
  			frameIntervalType = n;
  			intervals = new int[n];
  			System.arraycopy(_intervals, 0, intervals, 0, n);
  		} else {
  			frameIntervalType = -1;
  			intervals = null;
  		}
  		frameIntervalIndex = 0;
  		updateFrameRate();
  	}
  
  	/**
  	 * コピーコンストラクタ
  	 *
  	 * @param other
  	 */
  	public Size(final Size other) {
  		type = other.type;
  		frame_type = other.frame_type;
  		index = other.index;
  		width = other.width;
  		height = other.height;
  		frameIntervalType = other.frameIntervalType;
  		frameIntervalIndex = other.frameIntervalIndex;
  		final int n = other.intervals != null ? other.intervals.length : -1;
  		if (n > 0) {
  			intervals = new int[n];
  			System.arraycopy(other.intervals, 0, intervals, 0, n);
  		} else {
  			intervals = null;
  		}
  		updateFrameRate();
  	}
  
  	private Size(final Parcel source) {
  		// 読み取り順はwriteToParcelでの書き込み順と同じでないとダメ
  		type = source.readInt();
  		frame_type = source.readInt();
  		index = source.readInt();
  		width = source.readInt();
  		height = source.readInt();
  		frameIntervalType = source.readInt();
  		frameIntervalIndex = source.readInt();
  		if (frameIntervalType >= 0) {
  			if (frameIntervalType > 0) {
  				intervals = new int[frameIntervalType];
  			} else {
  				intervals = new int[3];
  			}
  			source.readIntArray(intervals);
  		} else {
  			intervals = null;
  		}
  		updateFrameRate();
  	}
  
  	public Size set(final Size other) {
  		if (other != null) {
  			type = other.type;
  			frame_type = other.frame_type;
  			index = other.index;
  			width = other.width;
  			height = other.height;
  			frameIntervalType = other.frameIntervalType;
  			frameIntervalIndex = other.frameIntervalIndex;
  			final int n = other.intervals != null ? other.intervals.length : -1;
  			if (n > 0) {
  				intervals = new int[n];
  				System.arraycopy(other.intervals, 0, intervals, 0, n);
  			} else {
  				intervals = null;
  			}
  			updateFrameRate();
  		}
  		return this;
  	}
  
  	public float getCurrentFrameRate() throws IllegalStateException {
  		final int n = fps != null ? fps.length : 0;
  		if ((frameIntervalIndex >= 0) && (frameIntervalIndex < n)) {
  			return fps[frameIntervalIndex];
  		}
  		throw new IllegalStateException("unknown frame rate or not ready");
  	}
  
  	public void setCurrentFrameRate(final float frameRate) {
  		// 一番近いのを選ぶ
  		int index = -1;
  		final int n = fps != null ? fps.length : 0;
  		for (int i = 0; i < n; i++) {
  			if (fps[i] <= frameRate) {
  				index = i;
  				break;
  			}
  		}
  		frameIntervalIndex = index;
  	}
  
  	@Override
  	public int describeContents() {
  		return 0;
  	}
  
  	@Override
  	public void writeToParcel(final Parcel dest, final int flags) {
  		dest.writeInt(type);
  		dest.writeInt(frame_type);
  		dest.writeInt(index);
  		dest.writeInt(width);
  		dest.writeInt(height);
  		dest.writeInt(frameIntervalType);
  		dest.writeInt(frameIntervalIndex);
  		if (intervals != null) {
  			dest.writeIntArray(intervals);
  		}
  	}
  
  	public void updateFrameRate() {
  		final int n = frameIntervalType;
  		if (n > 0) {
  			fps = new float[n];
  			for (int i = 0; i < n; i++) {
  				final float _fps = fps[i] = 10000000.0f / intervals[i];
  			}
  		} else if (n == 0) {
  			try {
  				final int min = Math.min(intervals[0], intervals[1]);
  				final int max = Math.max(intervals[0], intervals[1]);
  				final int step = intervals[2];
  				if (step > 0) {
  					int m = 0;
  					for (int i = min; i <= max; i += step) {
  						m++;
  					}
  					fps = new float[m];
  					m = 0;
  					for (int i = min; i <= max; i += step) {
  						final float _fps = fps[m++] = 10000000.0f / i;
  					}
  				} else {
  					final float max_fps = 10000000.0f / min;
  					int m = 0;
  					for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) {
  						m++;
  					}
  					fps = new float[m];
  					m = 0;
  					for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) {
  						this.fps[m++] = fps;
  					}
  				}
  			} catch (final Exception e) {
  				// ignore, なんでかminとmaxが0になってるんちゃうかな
  				fps = null;
  			}
  		}
  		final int m = fps != null ? fps.length : 0;
  		final StringBuilder sb = new StringBuilder();
  		sb.append("[");
  		for (int i = 0; i < m; i++) {
  			sb.append(String.format(Locale.US, "%4.1f", fps[i]));
  			if (i < m - 1) {
  				sb.append(",");
  			}
  		}
  		sb.append("]");
  		frameRates = sb.toString();
  		if (frameIntervalIndex > m) {
  			frameIntervalIndex = 0;
  		}
  	}
  
  	@Override
  	public String toString() {
  		float frame_rate = 0.0f;
  		try {
  			frame_rate = getCurrentFrameRate();
  		} catch (final Exception e) {
  		}
  		return String.format(Locale.US, "Size(%dx%d@%4.1f,type:%d,frame:%d,index:%d,%s)", width, height, frame_rate, type, frame_type, index, frameRates);
  	}
  }
  